Files
geo/docs/ai-brand-monitoring-tech-design-v2.md
T
root 446f865cdf feat: add knowledge management functionality with CRUD operations and database schema
- Implemented KnowledgeHandler for managing knowledge groups and items, including listing, creating, updating, and deleting operations.
- Added database migration scripts to create necessary tables for knowledge management, including knowledge_groups, knowledge_items, knowledge_parse_tasks, and knowledge_chunks_meta.
- Introduced prompt_rule_knowledge_groups table to associate prompt rules with knowledge groups.
2026-04-05 17:14:13 +08:00

53 KiB
Raw Blame History

AI 品牌曝光监测系统技术方案 V2

1. 文档信息

项目 内容
文档名称 AI 品牌曝光监测系统技术方案 V2
文档版本 V2.0
文档状态 待评审
创建日期 2026-04-05
适用范围 数据追踪模块全部页面
关联文档 docs/geo-platform-prd-v1.md (PRD 8.4 数据追踪)
关联文档 docs/question-driven-monitoring-design-v1.md (V1 数据模型)
容量目标 5 万并发在线用户

2. 与 V1 设计的关系

本方案基于 V1 (question-driven-monitoring-design-v1.md) 的核心结论,保留其"问题驱动 + 日级汇总 + 版本绑定"的数据模型哲学,在此基础上扩展以下能力:

V1 已定义 V2 扩展
品牌曝光趋势 + 高频问题 + 引用排行 新增:主页总览、平台占比分析、竞争对手分析、业务主题分析、AI 对话问题列表、AI 引用来源排名
单维度(品牌+关键词+模型) 多维度:按 AI 平台、按竞品、按主题、按引用来源交叉分析
汇总表 3 张 扩展至 8 张汇总表 + 2 张原始采集表
未涉及性能架构 明确 5 万并发的缓存层、连接池、部署拓扑
未涉及前端 6 个页面 + ECharts 可视化完整方案

V1 定义的采集逻辑、版本化策略、去重策略、保留策略 全部沿用,不再重复

3. 目标页面与数据需求

基于产品截图,数据追踪模块共 6 个页面:

3.1 主页(Dashboard

区域 数据需求 数据来源
曝光趋势图 近 7/30 天品牌在所有 AI 平台的综合曝光度日趋势 monitoring_brand_daily_overview
前五竞争对手 品牌及竞品的情感得分、曝光度排名 monitoring_competitor_daily
引用来源类型占比 编辑类、企业、UGC、参考资料、机构、其他的占比饼图 monitoring_citation_source_daily
前五引用平台 引用次数最多的外部平台,含百度权重、引用次数、覆盖度 monitoring_citation_source_daily
最新 AI 对话 最近被触发的 AI 问答问题、排名、前三品牌 monitoring_question_daily
AI 品牌印象 品牌相关的高频词词云 monitoring_brand_impression

3.2 平台占比分析

区域 数据需求 数据来源
提及次数排名 各 AI 平台对品牌的提及总次数横向柱状图 monitoring_platform_daily
各平台排名表现 按平台展开:AI 对话问题、提及次数、平均位置、曝光度 monitoring_platform_question_daily

3.3 竞争对手分析

区域 数据需求 数据来源
竞争对手曝光度排名 品牌 + 竞品在各 AI 平台上的曝光度分组柱状图 monitoring_competitor_daily
竞争对手排名情况 按品牌/竞品展开:各平台提及次数、平均位置、曝光度 monitoring_competitor_platform_daily

3.4 业务主题分析

区域 数据需求 数据来源
业务主题 主题名称、问题数、曝光度、提及次数 monitoring_topic_daily
引用最高的前五平台 外部引用来源平台排名 monitoring_citation_source_daily
引用次数最多的页面 具体被引用的外部 URL monitoring_citation_page_daily

3.5 AI 对话问题

区域 数据需求 数据来源
问题列表 AI 对话问题、热度指数、曝光度、平均位置、情感倾向、前三品牌 monitoring_question_daily
查看效果 / 查看引用 单条问题的详细回答效果和引用来源 question_monitor_parse_results

3.6 AI 引用排名

区域 数据需求 数据来源
最高引用来源平台趋势 前 5 引用来源平台的引用次数日趋势折线图 monitoring_citation_source_daily
引用页面详情 引用平台、百度权重、引用页面 URL、引用次数、覆盖率 monitoring_citation_page_daily

4. 与现有数据模型的关系

4.1 已有表复用

监测系统直接复用以下现有表,不新建冗余表

现有表 用途 关键字段
brands 被监测的品牌主体 id, tenant_id, name
brand_keywords 监测问题的分类容器 id, brand_id, name
brand_questions 监测采集的最小单元 id, brand_id, keyword_id, current_version_id
brand_question_versions 保证历史口径稳定 id, question_id, question_text, question_hash, version_no
competitors 竞品品牌数据 id, brand_id, name, website
articles 被引用的系统文章 id, tenant_id, source_type

4.2 数据流向

现有品牌词库                         新建监测模块
┌──────────────┐                   ┌──────────────────┐
│ brands       │──────────────────▶│ 采集调度器        │
│ keywords     │  品牌+关键词+问题  │ (按问题×模型调度)  │
│ questions    │──────────────────▶│                  │
│ question_    │  绑定问题版本      └────────┬─────────┘
│  versions    │                            │
│ competitors  │──── 竞品清单 ──────▶        │ 调用 AI 平台 API
└──────────────┘                            ▼
                                   ┌──────────────────┐
现有文章表                          │ 原始采集表         │
┌──────────────┐                   │ question_monitor_ │
│ articles     │◀── 引用检测 ──────│  runs / results   │
│ article_     │                   └────────┬─────────┘
│  versions    │                            │ 日级聚合 Job
└──────────────┘                            ▼
                                   ┌──────────────────┐
                                   │ 汇总表 × 8        │
                                   │ (monitoring_*)    │
                                   └────────┬─────────┘
                                            │
                                            ▼
                                   ┌──────────────────┐
                                   │ Redis 缓存层      │
                                   └────────┬─────────┘
                                            │
                                            ▼
                                   ┌──────────────────┐
                                   │ Dashboard API     │
                                   │ (6 个页面)         │
                                   └──────────────────┘

5. 数据库 Schema

5.1 参考表

ai_platforms — AI 平台注册表

CREATE TABLE ai_platforms (
    id            BIGSERIAL PRIMARY KEY,
    tenant_id     BIGINT NOT NULL REFERENCES tenants(id),
    name          VARCHAR(50) NOT NULL,          -- DeepSeek / 通义千问 / 豆包 / 腾讯元宝 / 文心一言
    icon          VARCHAR(255),
    provider_key  VARCHAR(50) NOT NULL,          -- deepseek / qwen / doubao / hunyuan / ernie
    api_endpoint  TEXT,
    api_model     VARCHAR(100),
    display_order INT NOT NULL DEFAULT 0,
    status        VARCHAR(20) NOT NULL DEFAULT 'active',
    created_at    TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at    TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX uk_ai_platform_tenant_key ON ai_platforms(tenant_id, provider_key);

monitoring_topics — 业务主题分类

CREATE TABLE monitoring_topics (
    id          BIGSERIAL PRIMARY KEY,
    tenant_id   BIGINT NOT NULL REFERENCES tenants(id),
    brand_id    BIGINT NOT NULL REFERENCES brands(id),
    name        VARCHAR(200) NOT NULL,            -- 瑜伽服饰 / 运动生活方式品牌 / 女士运动内衣
    description TEXT,
    status      VARCHAR(20) NOT NULL DEFAULT 'active',
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    deleted_at  TIMESTAMPTZ
);
CREATE UNIQUE INDEX uk_topic_tenant_brand_name ON monitoring_topics(tenant_id, brand_id, name) WHERE deleted_at IS NULL;

monitoring_topic_questions — 主题与问题的多对多关系

CREATE TABLE monitoring_topic_questions (
    id          BIGSERIAL PRIMARY KEY,
    topic_id    BIGINT NOT NULL REFERENCES monitoring_topics(id),
    question_id BIGINT NOT NULL REFERENCES brand_questions(id),
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT uk_topic_question UNIQUE (topic_id, question_id)
);

5.2 原始采集表

沿用 V1 设计,增加 tenant_id 租户隔离字段。

question_monitor_runs — 单次采集执行记录

CREATE TABLE question_monitor_runs (
    id                   BIGSERIAL PRIMARY KEY,
    tenant_id            BIGINT NOT NULL REFERENCES tenants(id),
    brand_id             BIGINT NOT NULL REFERENCES brands(id),
    keyword_id           BIGINT NOT NULL REFERENCES brand_keywords(id),
    question_id          BIGINT NOT NULL REFERENCES brand_questions(id),
    question_version_id  BIGINT NOT NULL REFERENCES brand_question_versions(id),
    ai_platform_id       BIGINT NOT NULL REFERENCES ai_platforms(id),
    run_date             DATE NOT NULL,
    run_at               TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    task_batch_id        VARCHAR(50),
    status               VARCHAR(20) NOT NULL DEFAULT 'pending',  -- pending/running/completed/failed
    raw_answer_key       TEXT,                                      -- MinIO 对象存储路径
    error_message        TEXT,
    created_at           TIMESTAMPTZ NOT NULL DEFAULT NOW()
) PARTITION BY RANGE (run_date);

CREATE INDEX idx_monitor_runs_tenant_date ON question_monitor_runs(tenant_id, run_date DESC);
CREATE INDEX idx_monitor_runs_batch ON question_monitor_runs(task_batch_id);

分区策略:按 run_date 月级 Range 分区,自动创建未来 3 个月分区,到期后可 DROP PARTITION 清理。

question_monitor_parse_results — 单次采集解析结果

CREATE TABLE question_monitor_parse_results (
    id                      BIGSERIAL PRIMARY KEY,
    run_id                  BIGINT NOT NULL,       -- 指向 question_monitor_runs.id(分区表不建 FK
    tenant_id               BIGINT NOT NULL REFERENCES tenants(id),

    -- 品牌提及指标
    brand_mentioned         BOOLEAN NOT NULL DEFAULT false,
    brand_position          INT,                    -- 品牌出现位置(1=首位)
    top1_mentioned          BOOLEAN NOT NULL DEFAULT false,
    first_recommended       BOOLEAN NOT NULL DEFAULT false,
    positive_sentiment      BOOLEAN NOT NULL DEFAULT false,
    sentiment_label         VARCHAR(20),            -- excellent/normal/negative

    -- 竞品提及
    competitor_mentions     JSONB,                  -- [{"competitor_id":1,"name":"Nike","position":2,"sentiment":"positive"}]

    -- 引用检测
    citation_urls           JSONB,                  -- [{"url":"https://...","platform":"什么值得买","platform_type":"UGC","baidu_weight":7}]
    cited_article_ids       BIGINT[],               -- 系统文章 ID 数组
    citation_count          INT NOT NULL DEFAULT 0,

    -- 品牌印象关键词
    brand_keywords_extract  JSONB,                  -- [{"word":"性价比高","weight":5},{"word":"设计时尚","weight":3}]

    parser_version          VARCHAR(20) NOT NULL DEFAULT 'v1',
    created_at              TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_parse_results_run ON question_monitor_parse_results(run_id);
CREATE INDEX idx_parse_results_tenant ON question_monitor_parse_results(tenant_id);

5.3 汇总表

所有汇总表均由后台聚合 Job 写入,Dashboard 查询 只读 汇总表。

1) monitoring_brand_daily_overview — 品牌日级总览(主页指标卡 + 趋势图)

CREATE TABLE monitoring_brand_daily_overview (
    id                      BIGSERIAL PRIMARY KEY,
    tenant_id               BIGINT NOT NULL,
    brand_id                BIGINT NOT NULL,
    metric_date             DATE NOT NULL,

    -- 核心指标
    total_question_count    INT NOT NULL DEFAULT 0,     -- 采集的问题总数
    total_ask_count         INT NOT NULL DEFAULT 0,     -- 总提问次数(跨平台)
    mention_count           INT NOT NULL DEFAULT 0,
    top1_mention_count      INT NOT NULL DEFAULT 0,
    first_recommend_count   INT NOT NULL DEFAULT 0,
    positive_mention_count  INT NOT NULL DEFAULT 0,
    citation_count          INT NOT NULL DEFAULT 0,

    -- 比率
    mention_rate            NUMERIC(5,4) NOT NULL DEFAULT 0,
    top1_mention_rate       NUMERIC(5,4) NOT NULL DEFAULT 0,
    first_recommend_rate    NUMERIC(5,4) NOT NULL DEFAULT 0,
    positive_mention_rate   NUMERIC(5,4) NOT NULL DEFAULT 0,
    exposure_rate           NUMERIC(5,4) NOT NULL DEFAULT 0,     -- 曝光度

    -- 情感得分
    sentiment_score         INT,                        -- 0~100

    updated_at              TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT uk_brand_overview UNIQUE (tenant_id, brand_id, metric_date)
);

2) monitoring_platform_daily — 按 AI 平台维度的品牌日统计(平台占比分析)

CREATE TABLE monitoring_platform_daily (
    id                  BIGSERIAL PRIMARY KEY,
    tenant_id           BIGINT NOT NULL,
    brand_id            BIGINT NOT NULL,
    ai_platform_id      BIGINT NOT NULL,
    metric_date         DATE NOT NULL,

    mention_count       INT NOT NULL DEFAULT 0,
    avg_position        NUMERIC(5,2),               -- 平均排名位置
    exposure_rate       NUMERIC(5,4) NOT NULL DEFAULT 0,
    top1_count          INT NOT NULL DEFAULT 0,
    question_count      INT NOT NULL DEFAULT 0,

    updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT uk_platform_daily UNIQUE (tenant_id, brand_id, ai_platform_id, metric_date)
);

3) monitoring_platform_question_daily — 按平台×问题的明细(平台展开表)

CREATE TABLE monitoring_platform_question_daily (
    id                  BIGSERIAL PRIMARY KEY,
    tenant_id           BIGINT NOT NULL,
    brand_id            BIGINT NOT NULL,
    ai_platform_id      BIGINT NOT NULL,
    question_id         BIGINT NOT NULL,
    metric_date         DATE NOT NULL,

    question_text       TEXT NOT NULL,                -- 快照
    mention_count       INT NOT NULL DEFAULT 0,
    avg_position        NUMERIC(5,2),
    exposure_rate       NUMERIC(5,4) NOT NULL DEFAULT 0,

    updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT uk_platform_question_daily UNIQUE (tenant_id, brand_id, ai_platform_id, question_id, metric_date)
);

4) monitoring_competitor_daily — 竞品对比日统计(竞争对手分析)

CREATE TABLE monitoring_competitor_daily (
    id                  BIGSERIAL PRIMARY KEY,
    tenant_id           BIGINT NOT NULL,
    brand_id            BIGINT NOT NULL,              -- 主品牌
    compared_brand_name VARCHAR(200) NOT NULL,         -- 品牌名或竞品名
    is_self             BOOLEAN NOT NULL DEFAULT false,
    competitor_id       BIGINT,                        -- NULL 表示自身品牌
    metric_date         DATE NOT NULL,

    mention_count       INT NOT NULL DEFAULT 0,
    avg_position        NUMERIC(5,2),
    exposure_rate       NUMERIC(5,4) NOT NULL DEFAULT 0,
    sentiment_score     INT,

    updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT uk_competitor_daily UNIQUE (tenant_id, brand_id, compared_brand_name, metric_date)
);

5) monitoring_competitor_platform_daily — 竞品×平台交叉统计

CREATE TABLE monitoring_competitor_platform_daily (
    id                  BIGSERIAL PRIMARY KEY,
    tenant_id           BIGINT NOT NULL,
    brand_id            BIGINT NOT NULL,
    compared_brand_name VARCHAR(200) NOT NULL,
    ai_platform_id      BIGINT NOT NULL,
    metric_date         DATE NOT NULL,

    mention_count       INT NOT NULL DEFAULT 0,
    avg_position        NUMERIC(5,2),
    exposure_rate       NUMERIC(5,4) NOT NULL DEFAULT 0,

    updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT uk_comp_platform_daily UNIQUE (tenant_id, brand_id, compared_brand_name, ai_platform_id, metric_date)
);

6) monitoring_topic_daily — 业务主题日统计

CREATE TABLE monitoring_topic_daily (
    id                  BIGSERIAL PRIMARY KEY,
    tenant_id           BIGINT NOT NULL,
    brand_id            BIGINT NOT NULL,
    topic_id            BIGINT NOT NULL,
    metric_date         DATE NOT NULL,

    question_count      INT NOT NULL DEFAULT 0,
    exposure_rate       NUMERIC(5,4) NOT NULL DEFAULT 0,
    mention_count       INT NOT NULL DEFAULT 0,

    updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT uk_topic_daily UNIQUE (tenant_id, brand_id, topic_id, metric_date)
);

7) monitoring_question_daily — 单问题日统计(AI 对话问题页)

CREATE TABLE monitoring_question_daily (
    id                  BIGSERIAL PRIMARY KEY,
    tenant_id           BIGINT NOT NULL,
    brand_id            BIGINT NOT NULL,
    question_id         BIGINT NOT NULL,
    question_version_id BIGINT NOT NULL,
    metric_date         DATE NOT NULL,

    question_text       TEXT NOT NULL,
    heat_index          INT NOT NULL DEFAULT 0,        -- 热度指数
    exposure_rate       NUMERIC(5,4) NOT NULL DEFAULT 0,
    avg_position        NUMERIC(5,2),
    sentiment_label     VARCHAR(20),                    -- excellent/normal/negative
    top3_brands         JSONB,                          -- [{"name":"lululemon","position":1},...]
    ask_count           INT NOT NULL DEFAULT 0,
    mention_count       INT NOT NULL DEFAULT 0,

    updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT uk_question_daily UNIQUE (tenant_id, brand_id, question_id, metric_date)
);

8) monitoring_citation_source_daily — 引用来源日统计(AI 引用排名)

CREATE TABLE monitoring_citation_source_daily (
    id                  BIGSERIAL PRIMARY KEY,
    tenant_id           BIGINT NOT NULL,
    brand_id            BIGINT NOT NULL,
    metric_date         DATE NOT NULL,

    source_platform     VARCHAR(200) NOT NULL,          -- 夸克 / 排行榜123网 / 什么值得买
    source_type         VARCHAR(50) NOT NULL,           -- UGC / 编辑类 / 企业 / 参考资料 / 机构 / 其他
    baidu_weight        INT,                            -- 百度权重 0~10
    citation_count      INT NOT NULL DEFAULT 0,
    coverage_rate       NUMERIC(5,4) NOT NULL DEFAULT 0,

    updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT uk_citation_source_daily UNIQUE (tenant_id, brand_id, source_platform, metric_date)
);

9) monitoring_citation_page_daily — 引用页面明细日统计

CREATE TABLE monitoring_citation_page_daily (
    id                  BIGSERIAL PRIMARY KEY,
    tenant_id           BIGINT NOT NULL,
    brand_id            BIGINT NOT NULL,
    metric_date         DATE NOT NULL,

    source_platform     VARCHAR(200) NOT NULL,
    baidu_weight        INT,
    page_url            TEXT NOT NULL,
    citation_count      INT NOT NULL DEFAULT 0,
    coverage_rate       NUMERIC(5,4) NOT NULL DEFAULT 0,

    updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT uk_citation_page_daily UNIQUE (tenant_id, brand_id, page_url, metric_date)
);

10) monitoring_brand_impression — 品牌印象词(词云数据)

CREATE TABLE monitoring_brand_impression (
    id                  BIGSERIAL PRIMARY KEY,
    tenant_id           BIGINT NOT NULL,
    brand_id            BIGINT NOT NULL,
    metric_date         DATE NOT NULL,

    word                VARCHAR(100) NOT NULL,
    weight              INT NOT NULL DEFAULT 1,         -- 词频权重

    updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT uk_impression_word UNIQUE (tenant_id, brand_id, word, metric_date)
);

5.4 分区策略

分区方式 保留周期
question_monitor_runs RANGE by run_date(月级) 90 天
question_monitor_parse_results 不分区(通过 run_id 关联) 90 天
所有 monitoring_* 汇总表 不分区(数据量可控) 180 天+

5.5 索引策略

所有汇总表的 UNIQUE 约束自动创建复合索引,天然覆盖 Dashboard 的查询路径(WHERE tenant_id = ? AND brand_id = ? AND metric_date BETWEEN ? AND ?)。

额外索引:

-- 主页趋势图和指标卡的高频查询
CREATE INDEX idx_brand_overview_range ON monitoring_brand_daily_overview(tenant_id, brand_id, metric_date DESC);

-- 竞品分析页的高频查询
CREATE INDEX idx_competitor_range ON monitoring_competitor_daily(tenant_id, brand_id, metric_date DESC);

-- 问题页排序
CREATE INDEX idx_question_daily_heat ON monitoring_question_daily(tenant_id, brand_id, metric_date DESC, heat_index DESC);

6. 后端 API 设计

6.1 路由规划

所有路由挂载在 tenant 认证组下,复用现有 JWT + TenantScope 中间件。

GET /api/tenant/monitoring/dashboard/overview         → 主页指标卡
GET /api/tenant/monitoring/dashboard/exposure-trend    → 主页曝光趋势图
GET /api/tenant/monitoring/dashboard/competitors       → 主页前五竞争对手
GET /api/tenant/monitoring/dashboard/citation-sources   → 主页引用来源类型占比 + 前五引用平台
GET /api/tenant/monitoring/dashboard/recent-questions   → 主页最新 AI 对话
GET /api/tenant/monitoring/dashboard/brand-impression   → 主页品牌印象词云

GET /api/tenant/monitoring/platforms/stats              → 平台占比 - 提及次数排名
GET /api/tenant/monitoring/platforms/:id/questions      → 平台占比 - 各平台问题明细

GET /api/tenant/monitoring/competitors/exposure         → 竞争对手 - 曝光度排名
GET /api/tenant/monitoring/competitors/detail           → 竞争对手 - 排名情况明细

GET /api/tenant/monitoring/topics                       → 业务主题列表
GET /api/tenant/monitoring/topics/:id/citations         → 业务主题 - 引用详情

GET /api/tenant/monitoring/questions                    → AI 对话问题列表(分页)
GET /api/tenant/monitoring/questions/:id/effect         → 问题效果详情
GET /api/tenant/monitoring/questions/:id/citations      → 问题引用详情

GET /api/tenant/monitoring/citations/trend              → AI 引用排名 - 来源平台趋势
GET /api/tenant/monitoring/citations/pages              → AI 引用排名 - 引用页面列表

GET /api/tenant/monitoring/ai-platforms                 → AI 平台下拉列表

6.2 通用查询参数

参数 类型 必填 默认值 说明
brand_id int64 - 品牌 ID
ai_platform_id int64 0 (全部) AI 平台筛选
days int 7 时间范围:7 / 30 / 90
page int 1 分页页码
page_size int 20 分页大小,最大 100

6.3 响应格式

复用现有 response.Success / response.Error 统一格式:

{
  "code": 0,
  "data": { ... },
  "request_id": "req_abc123"
}

6.4 关键 API 响应结构

Dashboard Overview

{
  "exposure_rate": 0.6333,
  "mention_rate": 0.7500,
  "top1_mention_rate": 0.4800,
  "positive_rate": 0.8200,
  "sentiment_score": 83,
  "exposure_rate_change": 0.05,
  "mention_rate_change": -0.02
}

Competitors

{
  "items": [
    {
      "rank": 1,
      "name": "lululemon",
      "is_self": true,
      "sentiment_score": 83,
      "sentiment_label": "excellent",
      "exposure_rate": 0.64
    },
    {
      "rank": 2,
      "name": "Nike",
      "is_self": false,
      "sentiment_score": 69,
      "sentiment_label": "normal",
      "exposure_rate": 0.47
    }
  ]
}

Questions (分页)

{
  "items": [
    {
      "question_id": 101,
      "question_text": "瑜伽垫品牌推荐",
      "heat_index": 5,
      "exposure_rate": 1.0,
      "avg_position": 1,
      "sentiment_label": "excellent",
      "top3_brands": ["lululemon", "Alo Yoga", "Nike"]
    }
  ],
  "total": 42,
  "page": 1,
  "page_size": 20
}

7. 后端架构

7.1 服务层设计

新增文件与现有代码的关系:

server/internal/tenant/
├── app/
│   ├── monitoring_service.go          ← 新建:Dashboard 查询服务
│   ├── monitoring_collector.go        ← 新建:AI 平台采集服务
│   ├── monitoring_aggregator.go       ← 新建:日级聚合 Job
│   ├── monitoring_parser.go           ← 新建:回答解析器
│   ├── brand_service.go               ← 已有:品牌 CRUD
│   └── ...
├── transport/
│   ├── monitoring_handler.go          ← 新建:监测 API Handler
│   ├── router.go                      ← 修改:注册监测路由
│   └── ...
└── repository/
    └── queries/
        ├── monitoring.sql             ← 新建:监测查询 SQL
        └── ...

7.2 MonitoringService 结构

type MonitoringService struct {
    pool  *pgxpool.Pool
    cache cache.Cache        // 复用现有 cache.Cache 接口(Redis
}

// Dashboard 查询方法(全部走缓存 → 汇总表路径)
func (s *MonitoringService) DashboardOverview(ctx, tenantID, brandID, days)  DashboardOverviewResp
func (s *MonitoringService) ExposureTrend(ctx, tenantID, brandID, days)  []TrendPoint
func (s *MonitoringService) TopCompetitors(ctx, tenantID, brandID, days, limit)  []CompetitorItem
func (s *MonitoringService) CitationSources(ctx, tenantID, brandID, days)  CitationSourceResp
func (s *MonitoringService) RecentQuestions(ctx, tenantID, brandID, limit)  []RecentQuestionItem
func (s *MonitoringService) BrandImpression(ctx, tenantID, brandID, days)  []ImpressionWord
func (s *MonitoringService) PlatformStats(ctx, tenantID, brandID, days)  []PlatformStatItem
func (s *MonitoringService) PlatformQuestions(ctx, tenantID, brandID, platformID, days, page)  PagedQuestions
func (s *MonitoringService) CompetitorExposure(ctx, tenantID, brandID, days)  CompetitorExposureResp
func (s *MonitoringService) CompetitorDetail(ctx, tenantID, brandID, days)  []CompetitorDetailItem
func (s *MonitoringService) Topics(ctx, tenantID, brandID, platformID, days)  []TopicItem
func (s *MonitoringService) Questions(ctx, tenantID, brandID, platformID, days, page, pageSize)  PagedQuestions
func (s *MonitoringService) CitationTrend(ctx, tenantID, brandID, platformID, days)  []CitationTrendPoint
func (s *MonitoringService) CitationPages(ctx, tenantID, brandID, days, page, pageSize)  PagedCitationPages
func (s *MonitoringService) AIPlatforms(ctx, tenantID)  []AIPlatform

7.3 MonitoringCollector 结构

type MonitoringCollector struct {
    pool          *pgxpool.Pool
    llmClients    map[string]llm.Client   // 每个 AI 平台一个 LLM Client
    objectStorage objectstorage.Client    // 原始回答存 MinIO
    logger        *zap.Logger
}

// 采集流程
func (c *MonitoringCollector) RunDailyCollection(ctx, tenantID, brandID) error
func (c *MonitoringCollector) CollectSingle(ctx, question, platform) (*MonitorRun, error)
func (c *MonitoringCollector) ParseAnswer(ctx, run, rawAnswer) (*ParseResult, error)

7.4 MonitoringAggregator 结构

type MonitoringAggregator struct {
    pool   *pgxpool.Pool
    cache  cache.Cache
    logger *zap.Logger
}

// 聚合 Job(通常每小时或每天运行一次)
func (a *MonitoringAggregator) AggregateDaily(ctx, tenantID, brandID, date) error
func (a *MonitoringAggregator) InvalidateCache(ctx, tenantID, brandID) error

7.5 Bootstrap 集成

bootstrap.goApp 结构中添加:

type App struct {
    // ... 现有字段 ...
    MonitoringCollector  *app.MonitoringCollector
    MonitoringAggregator *app.MonitoringAggregator
}

New() 中初始化新的 LLM clients(每个 AI 平台一个),注入到 Collector。

7.6 配置扩展

config.yaml 中新增:

monitoring:
  # 采集配置
  collection:
    daily_run_count: 1              # 每题每天默认执行次数
    worker_concurrency: 5           # 并发采集 worker 数
    question_timeout: 30s           # 单次采集超时
    batch_size: 50                  # 每批提交数量

  # 聚合配置
  aggregation:
    schedule: "0 3 * * *"           # 每天凌晨 3 点聚合
    lookback_days: 1                # 回溯天数

  # AI 平台 API
  platforms:
    deepseek:
      base_url: "https://api.deepseek.com/v1"
      api_key: ""
      model: "deepseek-chat"
    qwen:
      base_url: "https://dashscope.aliyuncs.com/compatible-mode/v1"
      api_key: ""
      model: "qwen-max"
    doubao:
      base_url: "https://ark.cn-beijing.volces.com/api/v3"
      api_key: ""
      model: "doubao-seed-2-0-lite-260215"
    hunyuan:
      base_url: "https://hunyuan.tencentcloudapi.com"
      api_key: ""
      model: "hunyuan-lite"
    ernie:
      base_url: "https://aip.baidubce.com/rpc/2.0/ai_custom/v1"
      api_key: ""
      model: "ernie-4.0-8k"

  # 缓存配置
  cache:
    overview_ttl: 5m
    trend_ttl: 15m
    competitor_ttl: 15m
    topic_ttl: 30m
    question_ttl: 10m
    citation_ttl: 15m

8. 缓存策略(5 万并发核心)

8.1 缓存架构

Browser → CDN (静态资源) → Nginx → Go API → Redis Cache → PostgreSQL
                                      ↑
                              90%+ 命中率
                              直接返回

8.2 缓存 Key 设计

复用 V1 的 Key 格式,扩展到新维度:

mon:{tenant}:{brand}:overview:{days}                     → 指标卡
mon:{tenant}:{brand}:trend:{days}                        → 趋势图
mon:{tenant}:{brand}:competitors:{days}                  → 竞品排名
mon:{tenant}:{brand}:citation_sources:{days}             → 引用来源
mon:{tenant}:{brand}:recent_questions                    → 最新对话
mon:{tenant}:{brand}:impression:{days}                   → 品牌印象
mon:{tenant}:{brand}:platform_stats:{days}               → 平台统计
mon:{tenant}:{brand}:platform:{pid}:questions:{days}:{page}  → 平台问题
mon:{tenant}:{brand}:comp_exposure:{days}                → 竞品曝光
mon:{tenant}:{brand}:topics:{pid}:{days}                 → 业务主题
mon:{tenant}:{brand}:questions:{pid}:{days}:{page}       → 对话问题列表
mon:{tenant}:{brand}:citation_trend:{pid}:{days}         → 引用趋势
mon:{tenant}:{brand}:citation_pages:{days}:{page}        → 引用页面

8.3 缓存 TTL 矩阵

数据类型 TTL 原因
Overview 指标卡 5 min 高频访问入口,需要相对实时
趋势图 15 min 日级数据变化慢
竞品排名 15 min 日级数据
引用来源 / 引用页面 15 min 日级聚合
业务主题 30 min 低频变更
问题列表 10 min 带分页,命中粒度细
品牌印象 30 min 日级聚合
AI 平台列表 1 hour 几乎不变

8.4 缓存失效策略

  1. TTL 自动过期:主要策略,简单可靠。
  2. 聚合 Job 主动失效MonitoringAggregator.InvalidateCache() 在每次聚合完成后删除对应品牌的所有缓存 Key。
  3. 不做 Cache-Aside Write-Through:汇总表是离线 Job 写入的,不是用户请求触发的,因此只需被动读取缓存。

8.5 50000 并发容量分析

单机容量 5 万并发时 QPS 估算 需要实例数
Go API 20K+ 并发连接 ~10K QPS 3 实例
Redis 100K+ QPS ~10K QPS90% 命中) 1 主实例
PostgreSQL 5K QPS ~1K QPS(缓存穿透) 1 主 + 1 只读副本

假设:平均用户每 30 秒发起 1 次 API 请求 → 50000/30 ≈ 1700 QPS;峰值 3x → ~5000 QPS。Redis 缓存承接 90%+ → PG 仅 ~500 QPS。

9. 前端架构

9.1 页面结构

apps/admin-web/src/
├── views/monitoring/
│   ├── MonitoringDashboardView.vue       ← 主页
│   ├── MonitoringPlatformView.vue        ← 平台占比分析
│   ├── MonitoringCompetitorView.vue      ← 竞争对手分析
│   ├── MonitoringTopicView.vue           ← 业务主题分析
│   ├── MonitoringQuestionsView.vue       ← AI 对话问题
│   └── MonitoringCitationView.vue        ← AI 引用排名
├── components/monitoring/
│   ├── ExposureTrendChart.vue            ← ECharts 折线图
│   ├── CompetitorBarChart.vue            ← 分组柱状图
│   ├── PlatformHorizontalBar.vue         ← 横向柱状图
│   ├── CitationDonutChart.vue            ← 环形图
│   ├── CitationTrendChart.vue            ← 引用趋势折线图
│   ├── BrandImpressionCloud.vue          ← 词云
│   ├── HeatIndicator.vue                ← 热度指示器(IIIII
│   ├── SentimentBadge.vue               ← 情感标签(优秀/一般)
│   ├── CompetitorRankTable.vue          ← 竞品排名表格
│   └── MonitoringFilters.vue            ← 通用筛选栏(品牌+平台+时间)
└── lib/
    └── api.ts                            ← 修改:新增 monitoringApi

9.2 路由配置

router/index.ts 中新增,替换现有的 /tracking FeatureStubView

// 数据追踪 - 主路由组
{
  path: '/monitoring',
  name: 'monitoring',
  redirect: '/monitoring/dashboard',
  children: [
    { path: 'dashboard',    name: 'monitoring-dashboard',    component: MonitoringDashboardView },
    { path: 'platforms',    name: 'monitoring-platforms',    component: MonitoringPlatformView },
    { path: 'competitors',  name: 'monitoring-competitors',  component: MonitoringCompetitorView },
    { path: 'topics',       name: 'monitoring-topics',       component: MonitoringTopicView },
    { path: 'questions',    name: 'monitoring-questions',    component: MonitoringQuestionsView },
    { path: 'citations',    name: 'monitoring-citations',    component: MonitoringCitationView },
  ]
}

9.3 Chart Library

选用 ECharts + vue-echarts

  • 已在现有 planPhase 18)中确定
  • 原生中文支持,社区生态成熟
  • 支持所有所需图表类型:折线图、分组柱状图、横向柱状图、环形图
  • 词云使用 echarts-wordcloud 扩展

依赖添加:

{
  "echarts": "^5.5",
  "vue-echarts": "^7.0",
  "echarts-wordcloud": "^2.1"
}

9.4 Shared Types

packages/shared-types/src/index.ts 中新增接口:

// AI 平台
export interface AIPlatform {
  id: number
  name: string
  icon: string
  provider_key: string
}

// 主页
export interface MonitoringOverview {
  exposure_rate: number
  mention_rate: number
  top1_mention_rate: number
  positive_rate: number
  sentiment_score: number
  exposure_rate_change: number
  mention_rate_change: number
}

export interface TrendPoint {
  date: string
  exposure_rate: number
  brands: Array<{ name: string; exposure_rate: number }>
}

export interface CompetitorRankItem {
  rank: number
  name: string
  is_self: boolean
  icon?: string
  sentiment_score: number
  sentiment_label: string
  exposure_rate: number
}

export interface CitationSourceItem {
  source_platform: string
  source_type: string
  baidu_weight: number
  citation_count: number
  coverage_rate: number
}

export interface MonitoringQuestionItem {
  question_id: number
  question_text: string
  heat_index: number
  exposure_rate: number
  avg_position: number
  sentiment_label: string
  top3_brands: string[]
}

export interface CitationPageItem {
  source_platform: string
  baidu_weight: number
  page_url: string
  citation_count: number
  coverage_rate: number
}

export interface ImpressionWord {
  word: string
  weight: number
}

export interface PlatformStatItem {
  ai_platform_id: number
  platform_name: string
  platform_icon: string
  mention_count: number
  avg_position: number
  exposure_rate: number
}

export interface TopicItem {
  topic_id: number
  topic_name: string
  question_count: number
  exposure_rate: number
  mention_count: number
}

9.5 API Client

export const monitoringApi = {
  // 主页
  dashboardOverview: (brandId: number, days = 7) =>
    http.get<MonitoringOverview>('/monitoring/dashboard/overview', { params: { brand_id: brandId, days } }),
  exposureTrend: (brandId: number, days = 7) =>
    http.get<TrendPoint[]>('/monitoring/dashboard/exposure-trend', { params: { brand_id: brandId, days } }),
  topCompetitors: (brandId: number, days = 7) =>
    http.get<CompetitorRankItem[]>('/monitoring/dashboard/competitors', { params: { brand_id: brandId, days } }),
  citationSources: (brandId: number, days = 7) =>
    http.get<{ type_distribution: ..., top_platforms: CitationSourceItem[] }>('/monitoring/dashboard/citation-sources', { params: { brand_id: brandId, days } }),
  recentQuestions: (brandId: number) =>
    http.get<MonitoringQuestionItem[]>('/monitoring/dashboard/recent-questions', { params: { brand_id: brandId } }),
  brandImpression: (brandId: number, days = 7) =>
    http.get<ImpressionWord[]>('/monitoring/dashboard/brand-impression', { params: { brand_id: brandId, days } }),

  // 平台占比
  platformStats: (brandId: number, days = 7) =>
    http.get<PlatformStatItem[]>('/monitoring/platforms/stats', { params: { brand_id: brandId, days } }),
  platformQuestions: (brandId: number, platformId: number, days = 7, page = 1) =>
    http.get<Paged<MonitoringQuestionItem>>(`/monitoring/platforms/${platformId}/questions`, { params: { brand_id: brandId, days, page } }),

  // 竞争对手
  competitorExposure: (brandId: number, days = 7) =>
    http.get('/monitoring/competitors/exposure', { params: { brand_id: brandId, days } }),
  competitorDetail: (brandId: number, days = 7) =>
    http.get('/monitoring/competitors/detail', { params: { brand_id: brandId, days } }),

  // 业务主题
  topics: (brandId: number, platformId = 0, days = 7) =>
    http.get<TopicItem[]>('/monitoring/topics', { params: { brand_id: brandId, ai_platform_id: platformId, days } }),

  // AI 对话问题
  questions: (brandId: number, params: { ai_platform_id?: number; days?: number; page?: number; page_size?: number }) =>
    http.get<Paged<MonitoringQuestionItem>>('/monitoring/questions', { params: { brand_id: brandId, ...params } }),

  // AI 引用排名
  citationTrend: (brandId: number, platformId = 0, days = 7) =>
    http.get('/monitoring/citations/trend', { params: { brand_id: brandId, ai_platform_id: platformId, days } }),
  citationPages: (brandId: number, days = 7, page = 1, pageSize = 20) =>
    http.get<Paged<CitationPageItem>>('/monitoring/citations/pages', { params: { brand_id: brandId, days, page, page_size: pageSize } }),

  // 通用
  aiPlatforms: () => http.get<AIPlatform[]>('/monitoring/ai-platforms'),
}

10. 采集流水线设计

10.1 采集流程

┌──────────┐     ┌───────────────┐     ┌──────────────┐     ┌──────────────┐
│ 调度器    │     │  Redis Stream │     │ Collector    │     │  Parser      │
│ (cron)   │────▶│  / 内部队列    │────▶│  Worker ×5   │────▶│  (解析回答)   │
│ 生成任务  │     │               │     │ 调用 AI API  │     │ 提取指标     │
└──────────┘     └───────────────┘     └──────┬───────┘     └──────┬───────┘
                                              │                     │
                                              ▼                     ▼
                                       ┌─────────────┐     ┌──────────────┐
                                       │ MinIO        │     │ PostgreSQL   │
                                       │ (原始回答)    │     │ (解析结果)    │
                                       └─────────────┘     └──────────────┘

10.2 调度策略

// 每天凌晨 1 点执行
func (c *MonitoringCollector) RunDailyCollection(ctx context.Context, tenantID int64) error {
    // 1. 查询该租户所有 active 品牌
    brands := listActiveBrands(ctx, tenantID)
    for _, brand := range brands {
        // 2. 查询品牌下所有 active 问题(含 question_hash 去重)
        questions := listUniqueActiveQuestions(ctx, brand.ID)
        // 3. 查询所有 active AI 平台
        platforms := listActivePlatforms(ctx, tenantID)
        // 4. 生成采集任务 = questions × platforms
        for _, q := range questions {
            for _, p := range platforms {
                enqueueTask(ctx, brand, q, p)
            }
        }
    }
}

10.3 回答解析逻辑

func (c *MonitoringCollector) ParseAnswer(ctx context.Context, question, brand string, competitors []string, rawAnswer string) *ParseResult {
    // 1. 品牌提及检测:在回答中查找品牌名出现位置
    // 2. 首位提及检测:品牌是否出现在推荐列表第一位
    // 3. 首选推荐检测:品牌是否被明确推荐
    // 4. 情感分析:使用 LLM 对品牌相关段落做情感判断
    // 5. 竞品提及检测:检测 competitors 列表中的品牌出现情况
    // 6. 引用 URL 提取:从回答中提取所有外部链接
    // 7. 系统文章引用匹配:与 articles 表做 URL/标题匹配
    // 8. 品牌印象词提取:使用 LLM 从回答中提取品牌相关形容词
}

10.4 聚合 Job

// 每天凌晨 3 点执行(在采集完成后)
func (a *MonitoringAggregator) AggregateDaily(ctx context.Context, date time.Time) error {
    tenants := listAllTenants(ctx)
    for _, t := range tenants {
        brands := listActiveBrands(ctx, t.ID)
        for _, b := range brands {
            // 1. 从 parse_results 聚合 → monitoring_brand_daily_overview
            a.aggregateBrandOverview(ctx, t.ID, b.ID, date)
            // 2. 聚合 → monitoring_platform_daily / monitoring_platform_question_daily
            a.aggregatePlatformStats(ctx, t.ID, b.ID, date)
            // 3. 聚合 → monitoring_competitor_daily / monitoring_competitor_platform_daily
            a.aggregateCompetitorStats(ctx, t.ID, b.ID, date)
            // 4. 聚合 → monitoring_topic_daily
            a.aggregateTopicStats(ctx, t.ID, b.ID, date)
            // 5. 聚合 → monitoring_question_daily
            a.aggregateQuestionStats(ctx, t.ID, b.ID, date)
            // 6. 聚合 → monitoring_citation_source_daily / monitoring_citation_page_daily
            a.aggregateCitationStats(ctx, t.ID, b.ID, date)
            // 7. 聚合 → monitoring_brand_impression
            a.aggregateBrandImpression(ctx, t.ID, b.ID, date)
            // 8. 清除该品牌缓存
            a.InvalidateCache(ctx, t.ID, b.ID)
        }
    }
}

11. 部署拓扑(5 万并发)

11.1 推荐部署架构

                    ┌───────────────────────────┐
                    │     CDN / 云 LB (SLB)     │
                    │   静态资源 + API 路由       │
                    └─────────────┬─────────────┘
                                  │
                    ┌─────────────┼─────────────┐
                    ▼             ▼             ▼
              ┌──────────┐ ┌──────────┐ ┌──────────┐
              │ tenant-api│ │ tenant-api│ │ tenant-api│   ×3 (API 服务)
              │ Pod/容器   │ │ Pod/容器   │ │ Pod/容器   │
              └────┬──────┘ └────┬──────┘ └────┬──────┘
                   │             │             │
              ┌────┴─────────────┴─────────────┴────┐
              │          Redis 6+ (单主)              │   缓存 + 会话
              │          或 Redis Cluster             │
              └───────────────────┬──────────────────┘
                                  │
              ┌───────────────────┼──────────────────┐
              │                   │                  │
              ▼                   ▼                  ▼
        ┌───────────┐     ┌───────────┐      ┌───────────┐
        │ PG Primary │     │ PG Replica │      │   MinIO   │
        │ (读写)     │────▶│ (只读)     │      │ (原始回答) │
        └───────────┘     └───────────┘      └───────────┘

  ╔════════════════════════════════════════════════════════╗
  ║  独立进程 / CronJob(不接入 LB                        ║
  ║                                                        ║
  ║  ┌─────────────────┐   ┌─────────────────┐             ║
  ║  │ Collector Worker │   │ Aggregator Job  │             ║
  ║  │ (采集 AI 平台)   │   │ (日级聚合)      │             ║
  ║  └─────────────────┘   └─────────────────┘             ║
  ╚════════════════════════════════════════════════════════╝

11.2 资源估算

组件 规格建议 说明
API 实例 ×3 2C4G Go 服务轻量,3 实例可处理 ~60K 并发连接
Redis 4G 内存 缓存数据量不大,主要是 Key 数量
PostgreSQL Primary 4C16G 汇总表小,索引少,500 QPS 绰绰有余
PostgreSQL Replica ×1 4C16G 可选,用于聚合 Job 的重查询分流
MinIO 50G+ 原始回答存储,按 90 天保留
Collector Worker ×1 2C4G 采集任务非实时,单实例 5 并发即可
Aggregator Job ×1 2C4G 凌晨运行,单实例即可

11.3 数据库连接池配置

database:
  max_open_conns: 50        # 从 25 提升到 50(3 个 API 实例共享)
  max_idle_conns: 10

按 3 个 API 实例计算:每实例 ~17 连接上限,足够应对缓存穿透后的查询。

12. 数据量估算

12.1 汇总表大小

假设:100 个品牌,每品牌 5 个竞品,5 个 AI 平台,10 个主题,30 天窗口。

汇总表 行数/天 30 天行数 估算大小
monitoring_brand_daily_overview 100 3,000 < 1 MB
monitoring_platform_daily 500 15,000 < 5 MB
monitoring_platform_question_daily 50,000 1,500,000 ~200 MB
monitoring_competitor_daily 600 18,000 < 5 MB
monitoring_competitor_platform_daily 3,000 90,000 ~15 MB
monitoring_topic_daily 1,000 30,000 < 5 MB
monitoring_question_daily 50,000 1,500,000 ~250 MB
monitoring_citation_source_daily 5,000 150,000 ~25 MB
monitoring_citation_page_daily 10,000 300,000 ~60 MB
monitoring_brand_impression 5,000 150,000 ~20 MB

30 天汇总表总计 < 600 MBPostgreSQL 完全可控。

12.2 原始采集表大小

100 品牌 × 100 问题 × 5 平台 × 1 次/天 = 50,000 条/天
90 天保留 = 4,500,000 条原始记录
每条约 1KB(不含原始回答文本)→ ~4.5 GB
原始回答文本(~5KB/条)→ MinIO 约 22 GB/90 天

13. 实施计划

Phase 15: 数据库迁移 + AI 平台注册表

  • 创建所有新表的 migration 文件
  • 种子数据:5 个默认 AI 平台
  • 估时:1 天

Phase 16: 后端 - 监测查询服务 + API

  • 实现 MonitoringService 的所有查询方法
  • 实现 monitoring_handler.go 的所有路由
  • 实现 Redis 缓存层
  • 注册路由到 router.go
  • 估时:3 天

Phase 17: 后端 - 采集流水线

  • 实现 MonitoringCollector + MonitoringParser
  • 对接 5 个 AI 平台 API(复用现有 llm.Client 接口)
  • 原始回答存储 MinIO
  • 估时:3 天

Phase 18: 后端 - 聚合 Job

  • 实现 MonitoringAggregator 的 8 个聚合函数
  • 实现缓存失效逻辑
  • 实现定时调度(可先用 in-process cron
  • 估时:2 天

Phase 19: 种子数据

  • 生成 30 天真实结构的 mock 数据,覆盖所有汇总表
  • 确保前端开发可立即使用
  • 估时:1 天

Phase 20: 前端 - 6 页面 + ECharts

  • 安装 echarts / vue-echarts / echarts-wordcloud
  • 实现 6 个页面 + 10 个图表组件
  • 实现通用筛选栏组件
  • i18n 扩展
  • 估时:4 天

Phase 21: 前端验证 + 集成测试

  • pnpm typecheck:admin && pnpm build:admin 通过
  • go test ./... 通过
  • 全链路验证:种子数据 → API → 前端图表渲染
  • 估时:1 天

总估时:约 15 个工作日

14. 风险与决策项

14.1 待确认

序号 决策项 影响范围 建议
1 各 AI 平台 API Key 如何获取和管理 采集流水线 建议 config.yaml 配置,生产环境用环境变量覆盖
2 业务主题的分类方式(人工还是 LLM 自动归类) 主题分析页 V1 建议人工创建主题并关联问题,V2 可自动归类
3 百度权重数据来源 引用排名页 建议接入第三方 SEO API 或定期手动维护
4 情感分析的准确度要求 竞品分析 + 问题列表 建议先用规则匹配,重要场景用 LLM 二次判断
5 是否需要支持自定义 AI 平台 平台管理 V1 建议固定 5 个平台,用户不可增删
6 部署环境选择(K8s / Docker Compose / 裸机) 部署架构 开发阶段 Docker Compose,生产建议 K8s

14.2 已确认(沿用 V1 决策)

决策 来源
问题是最小监控采集单元 V1 核心结论
原始回答存对象存储,不存 PG V1 Section 8.2
每问题每天默认执行 1 次 V1 Section 8.3
问题 hash 去重 V1 Section 8.4
问题版本化保证历史口径 V1 Section 9
页面只查汇总表 V1 Section 10
原始记录保留 90 天 V1 Section 12

15. 附录

15.1 已有表 DDL 参考

-- brands(已存在)
CREATE TABLE brands (
    id BIGSERIAL PRIMARY KEY,
    tenant_id BIGINT NOT NULL REFERENCES tenants(id),
    name VARCHAR(200) NOT NULL,
    description TEXT,
    status VARCHAR(20) NOT NULL DEFAULT 'active',
    ...
);

-- brand_keywords(已存在)
CREATE TABLE brand_keywords (
    id BIGSERIAL PRIMARY KEY,
    tenant_id BIGINT NOT NULL REFERENCES tenants(id),
    brand_id BIGINT NOT NULL REFERENCES brands(id),
    name VARCHAR(200) NOT NULL,
    ...
);

-- brand_questions(已存在)
CREATE TABLE brand_questions (
    id BIGSERIAL PRIMARY KEY,
    tenant_id BIGINT NOT NULL REFERENCES tenants(id),
    brand_id BIGINT NOT NULL REFERENCES brands(id),
    keyword_id BIGINT NOT NULL REFERENCES brand_keywords(id),
    current_version_id BIGINT,
    ...
);

-- brand_question_versions(已存在)
CREATE TABLE brand_question_versions (
    id BIGSERIAL PRIMARY KEY,
    question_id BIGINT NOT NULL REFERENCES brand_questions(id),
    question_text TEXT NOT NULL,
    question_hash VARCHAR(64) NOT NULL,
    version_no INT NOT NULL,
    is_active BOOLEAN NOT NULL DEFAULT true,
    ...
);

-- competitors(已存在)
CREATE TABLE competitors (
    id BIGSERIAL PRIMARY KEY,
    tenant_id BIGINT NOT NULL REFERENCES tenants(id),
    brand_id BIGINT NOT NULL REFERENCES brands(id),
    name VARCHAR(200) NOT NULL,
    website TEXT,
    description TEXT,
    product_lines_json JSONB,
    ...
);

15.2 指标口径定义

指标 计算公式 说明
曝光度 (exposure_rate) 品牌在 AI 回答中被提及的问题数 / 采集的总问题数 反映品牌在 AI 平台的总体可见度
提及率 (mention_rate) 品牌被提及的回答次数 / 总提问次数 跨平台汇总
首位提及率 (top1_mention_rate) 品牌出现在推荐列表首位的次数 / 总提问次数 衡量品牌在 AI 推荐中的优先级
首选推荐率 (first_recommend_rate) 品牌被明确推荐的次数 / 总提问次数 衡量 AI 对品牌的推荐倾向
正面提及率 (positive_mention_rate) 正面情感的提及次数 / 总提及次数 衡量品牌的 AI 口碑
情感得分 (sentiment_score) 正面提及占比 × 100,修正后 0~100 综合情感指标
热度指数 (heat_index) 近期被触发的 AI 问答频次标准化后 1~5 档 问题热度
覆盖率 (coverage_rate) 该来源被引用的问题数 / 总问题数 引用来源的覆盖广度