feat(questions): make brand questions the primary monitoring & template axis
Deployment Config CI / Deployment Config (push) Successful in 29s
Frontend CI / Frontend (push) Successful in 4m4s
Backend CI / Backend (push) Failing after 7m12s

- 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:
2026-05-13 15:59:39 +08:00
parent 37b0b32327
commit 1eae6fb6d4
36 changed files with 2119 additions and 412 deletions
@@ -47,7 +47,7 @@ func TestPlatformTemplateSeedsInjectPromptConfig(t *testing.T) {
if !strings.Contains(analyzePrompt, "推荐类文章做品牌与竞品分析") {
t.Fatalf("analyze_prompt_template = %q, want yaml-backed analyze prompt", analyzePrompt)
}
if !strings.Contains(analyzePrompt, "全屋定制哪家好") || !strings.Contains(analyzePrompt, "搜索查询词") {
if !strings.Contains(analyzePrompt, "全屋定制哪家好") || !strings.Contains(analyzePrompt, "搜索问题候选") {
t.Fatalf("analyze_prompt_template = %q, want question-like long-tail query guidance", analyzePrompt)
}
if !strings.Contains(titlePrompt, "推荐类文章生成 5 个候选标题") {
@@ -66,10 +66,10 @@ func TestAnalyzeFallbackPromptPrefersQuestionLikeQueries(t *testing.T) {
prompt := AnalyzeFallbackPrompt(`{"brand_name":"测试品牌","input_params":{"keyword":"全屋定制"}}`)
for _, want := range []string{
"真实用户会直接搜索的问题型长尾词",
"哪家好",
"最多返回 6 个竞品和 10 个关键词",
} {
"真实用户会直接搜索的问题型长尾词",
"哪家好",
"最多返回 6 个竞品和 10 个搜索问题候选",
} {
if !strings.Contains(prompt, want) {
t.Fatalf("AnalyzeFallbackPrompt() missing %q in prompt:\n%s", want, prompt)
}
@@ -44,7 +44,7 @@ var platformTemplateSeeds = []PlatformTemplateSeed{
},
"wizard": {
"steps": {
"basic": { "title": "基本信息", "description": "品牌、关键词与竞品上下文" },
"basic": { "title": "基本信息", "description": "品牌、品牌问题与竞品上下文" },
"structure": { "title": "文章结构", "description": "标题方案、结构组合与重点补充" },
"generate": { "title": "生成文章", "description": "确认参数并进入生成队列" }
},
@@ -53,11 +53,11 @@ var platformTemplateSeeds = []PlatformTemplateSeed{
"cards": {
"brand": {
"title": "品牌信息",
"hint": "品牌可手动输入,也可从品牌库带入。AI 会结合品牌、官网和模板字段补齐关键词与竞品。"
"hint": "品牌可手动输入,也可从品牌库带入。AI 会结合品牌、官网和品牌问题补齐摘要与竞品。"
},
"keywords": {
"title": "关键词",
"hint": "关键词可以手动改写,AI 分析结果会自动补充到这里,后续标题生成会以当前版本为准。"
"title": "品牌问题",
"hint": "品牌主问题必选且只能选一个;补充覆盖问题可选,最多 3 个,只进入文章主体和结尾点题。"
},
"template_fields": {
"title": "模板字段",
@@ -73,7 +73,7 @@ var platformTemplateSeeds = []PlatformTemplateSeed{
"cards": {
"choose_title": {
"title": "选择文章标题",
"hint": "AI 会结合当前关键词和竞品上下文给出 5 个更适合的标题候选,你可以直接选择或改写。"
"hint": "AI 会结合品牌主问题和竞品上下文给出 5 个更适合的标题候选,你可以直接选择或改写。"
},
"outline": {
"title": "文章结构",
@@ -109,7 +109,7 @@ var platformTemplateSeeds = []PlatformTemplateSeed{
{ "key": "intro", "label": "引言", "default": true },
{ "key": "site_list", "label": "网站列表", "default": true },
{ "key": "key_points", "label": "文章关键要点", "default": true },
{ "key": "what_is_keyword", "label_template": "什么是{{primary_keyword}}", "default": false },
{ "key": "what_is_question", "label_template": "围绕{{brand_question}}展开", "default": false },
{ "key": "features", "label": "特点", "default": false },
{ "key": "pros_cons", "label": "优缺点", "default": false },
{ "key": "audience", "label": "适合人群", "default": false },
@@ -141,11 +141,11 @@ var platformTemplateSeeds = []PlatformTemplateSeed{
"cards": {
"brand": {
"title": "品牌信息",
"hint": "可直接输入品牌,也可从品牌库选择。AI 会补充适合评测文章的关键词和品牌背景。"
"hint": "可直接输入品牌,也可从品牌库选择。AI 会结合品牌问题补充适合评测文章的品牌背景。"
},
"keywords": {
"title": "关键词",
"hint": "关键词会影响标题和正文的评测切入点,可手动修改。"
"title": "品牌问题",
"hint": "品牌主问题控制标题和主线;补充覆盖问题只进入文章主体与结尾点题。"
},
"competitors": {
"visible": false
@@ -222,7 +222,7 @@ var platformTemplateSeeds = []PlatformTemplateSeed{
},
"wizard": {
"steps": {
"basic": { "title": "基本信息", "description": "品牌与关键词背景信息" },
"basic": { "title": "基本信息", "description": "品牌与品牌问题背景信息" },
"structure": { "title": "文章结构", "description": "标题方案、研究结构与重点补充" },
"generate": { "title": "生成文章", "description": "确认参数并进入生成队列" }
},
@@ -234,8 +234,8 @@ var platformTemplateSeeds = []PlatformTemplateSeed{
"hint": "如研究对象与品牌有关,可补充品牌与官网信息帮助 AI 建立上下文。"
},
"keywords": {
"title": "关键词",
"hint": "关键词会影响报告核心视角、结论表达和标题风格。"
"title": "品牌问题",
"hint": "品牌主问题控制报告核心视角;补充覆盖问题只进入主体覆盖和结尾点题。"
},
"competitors": {
"visible": false
@@ -305,7 +305,7 @@ var platformTemplateSeeds = []PlatformTemplateSeed{
},
"wizard": {
"steps": {
"basic": { "title": "基本信息", "description": "品牌、关键词与搜索意图上下文" },
"basic": { "title": "基本信息", "description": "品牌、品牌问题与搜索意图上下文" },
"structure": { "title": "文章结构", "description": "标题方案、结构组合与重点补充" },
"generate": { "title": "生成文章", "description": "确认参数并进入生成队列" }
},
@@ -317,8 +317,8 @@ var platformTemplateSeeds = []PlatformTemplateSeed{
"hint": "品牌和官网会帮助 AI 理解搜索对象,也便于后续沉淀到品牌库。"
},
"keywords": {
"title": "关键词",
"hint": "这里的关键词会直接参与标题与正文的搜索意图组织。"
"title": "品牌问题",
"hint": "品牌主问题参与标题与主线;补充覆盖问题只进入正文主体和结尾点题。"
},
"template_fields": {
"title": "模板字段",