feat(questions): make brand questions the primary monitoring & template axis
- add /questions/combination-fill endpoint with AI-driven, IP-region-aware matrix fill - extract ip2region resolver from ops/app into shared/ipregion for cross-service use - thread question_id filter through dashboard composite, citation summary, and collect-now - switch template wizard from keyword inputs to brand-question selection (primary + supplemental) - pass brand_question and supplemental_questions through assist/title/outline prompts - add AiWaitingModal + 45s client timeout and request_timeout error mapping for long AI flows Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ type MonitoringHandler struct {
|
||||
|
||||
type monitoringCollectNowRequest struct {
|
||||
KeywordID *int64 `json:"keyword_id"`
|
||||
QuestionID *int64 `json:"question_id"`
|
||||
PlatformIDs []string `json:"platform_ids"`
|
||||
Preempt *bool `json:"preempt"`
|
||||
WaitForFirstDispatch *bool `json:"wait_for_first_dispatch"`
|
||||
@@ -43,6 +44,12 @@ func (h *MonitoringHandler) DashboardComposite(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
questionID, err := parseOptionalInt64Pointer(c.Query("question_id"))
|
||||
if err != nil {
|
||||
response.Error(c, response.ErrBadRequest(40031, "invalid_question_id", "question_id must be a number"))
|
||||
return
|
||||
}
|
||||
|
||||
days, err := parseOptionalInt(c.Query("days"))
|
||||
if err != nil {
|
||||
response.Error(c, response.ErrBadRequest(40031, "invalid_days", "days must be a number"))
|
||||
@@ -51,7 +58,7 @@ func (h *MonitoringHandler) DashboardComposite(c *gin.Context) {
|
||||
|
||||
aiPlatformID := parseOptionalStringPointer(c.Query("ai_platform_id"))
|
||||
|
||||
data, svcErr := h.svc.DashboardComposite(c.Request.Context(), brandID, keywordID, days, c.Query("business_date"), aiPlatformID)
|
||||
data, svcErr := h.svc.DashboardComposite(c.Request.Context(), brandID, keywordID, questionID, days, c.Query("business_date"), aiPlatformID)
|
||||
if svcErr != nil {
|
||||
response.Error(c, svcErr)
|
||||
return
|
||||
@@ -78,9 +85,15 @@ func (h *MonitoringHandler) CitationSummary(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
questionID, err := parseOptionalInt64Pointer(c.Query("question_id"))
|
||||
if err != nil {
|
||||
response.Error(c, response.ErrBadRequest(40031, "invalid_question_id", "question_id must be a number"))
|
||||
return
|
||||
}
|
||||
|
||||
aiPlatformID := parseOptionalStringPointer(c.Query("ai_platform_id"))
|
||||
|
||||
data, svcErr := h.svc.CitationSummary(c.Request.Context(), days, brandID, keywordID, c.Query("business_date"), aiPlatformID)
|
||||
data, svcErr := h.svc.CitationSummary(c.Request.Context(), days, brandID, keywordID, questionID, c.Query("business_date"), aiPlatformID)
|
||||
if svcErr != nil {
|
||||
response.Error(c, svcErr)
|
||||
return
|
||||
@@ -142,7 +155,7 @@ func (h *MonitoringHandler) CollectNow(c *gin.Context) {
|
||||
targetClientID = &parsed
|
||||
}
|
||||
|
||||
data, svcErr := h.svc.CollectNow(c.Request.Context(), brandID, req.KeywordID, app.MonitoringCollectNowOptions{
|
||||
data, svcErr := h.svc.CollectNow(c.Request.Context(), brandID, req.KeywordID, req.QuestionID, app.MonitoringCollectNowOptions{
|
||||
PlatformIDs: req.PlatformIDs,
|
||||
Preempt: req.Preempt == nil || *req.Preempt,
|
||||
WaitForFirstDispatch: req.WaitForFirstDispatch != nil && *req.WaitForFirstDispatch,
|
||||
|
||||
Reference in New Issue
Block a user