# GEO 平台需求规格说明书

> **文档版本**: v1.0
> **创建日期**: 2026-05-01
> **状态**: 待评审
> **依据文档**: [GEO平台PRD v1.0](../1-PRD/01-GEO平台PRD-v1.0.md)
> **评审记录**: 详见 [专家讨论总览](../6-专家讨论/00-专家讨论总览.md)

---

## 目录

1. [概述](#1-概述)
2. [功能需求规格](#2-功能需求规格)
3. [接口需求规格](#3-接口需求规格)
4. [数据需求规格](#4-数据需求规格)
5. [非功能需求规格](#5-非功能需求规格)
6. [约束条件](#6-约束条件)
7. [需求追踪矩阵](#7-需求追踪矩阵)
8. [附录](#8-附录)

---

## 1. 概述

### 1.1 文档目的

本需求规格说明书旨在详细定义GEO平台的功能性需求和非功能性需求，作为产品设计、开发、测试和验收的依据。本文档是[PRD文档](./01-GEO平台PRD-v1.0.md)的细化，包含所有用户故事的详细验收标准。

### 1.2 范围

**包含范围**:
- GEO平台v1.0版本的所有功能需求
- 前后端系统的接口定义
- 数据库设计的数据实体
- 性能、安全、可用性等非功能需求

**不包含范围**:
- v1.5及后续版本的情感分析、白标等功能
- 移动App相关需求
- 第三方支付集成细节

### 1.3 定义与缩略语

| 术语 | 定义 |
|------|------|
| GEO | Generative Engine Optimization，生成式引擎优化 |
| AI曝光度评分 | 品牌在AI搜索中的综合表现分数 (0-100) |
| SOV | Share of Voice，品牌提及占比 |
| 引用记录 | AI回答中提及品牌的一次记录 |
| Prompt | 向AI平台发送的查询指令 |
| 品牌别名 | 品牌的常见简称或昵称 |

---

## 2. 功能需求规格

### 2.1 用户认证模块 (F-AUTH)

#### 2.1.1 用户注册

| 属性 | 内容 |
|------|------|
| **需求ID** | F-AUTH-001 |
| **需求名称** | 用户注册 |
| **优先级** | P0 |
| **描述** | 新用户可以通过邮箱注册成为平台用户 |

**详细规格**:

| 字段 | 类型 | 必填 | 验证规则 |
|------|------|------|---------|
| 邮箱 | string | 是 | 邮箱格式，255字符内，唯一 |
| 密码 | string | 是 | 8-32字符，需包含大小写字母和数字 |
| 确认密码 | string | 是 | 必须与密码一致 |
| 验证码 | string | 是 | 6位数字，有效期10分钟 |

**业务流程**:
```
1. 用户输入邮箱、密码、确认密码
2. 系统发送验证码到邮箱
3. 用户输入验证码
4. 系统验证通过后创建用户
5. 发送欢迎邮件
6. 自动登录并跳转到品牌添加页
```

**异常处理**:

| 异常场景 | 处理方式 |
|---------|---------|
| 邮箱已被注册 | 提示"该邮箱已注册"，引导登录 |
| 验证码错误 | 提示"验证码错误"，可重新发送 |
| 验证码过期 | 提示"验证码已过期"，自动重新发送 |
| 密码不符合要求 | 实时显示密码强度提示 |

#### 2.1.2 用户登录

| 属性 | 内容 |
|------|------|
| **需求ID** | F-AUTH-002 |
| **需求名称** | 用户登录 |
| **优先级** | P0 |
| **描述** | 已注册用户可以登录平台 |

**详细规格**:

| 字段 | 类型 | 必填 | 验证规则 |
|------|------|------|---------|
| 邮箱 | string | 是 | 邮箱格式 |
| 密码 | string | 是 | 8-32字符 |
| 记住我 | boolean | 否 | 勾选后30天免登录 |

**登录方式**:
- 邮箱 + 密码登录
- 记住我选项

**业务流程**:
```
1. 用户输入邮箱和密码
2. 系统验证凭证
3. 验证通过后签发JWT Token
4. 跳转到大屏中心
```

**异常处理**:

| 异常场景 | 处理方式 |
|---------|---------|
| 邮箱未注册 | 提示"该邮箱未注册"，引导注册 |
| 密码错误 | 提示"密码错误"，剩余次数显示 |
| 账户被禁用 | 提示"账户已被禁用"，联系客服 |
| 登录过于频繁 | 15分钟后重试，限流5次/分钟 |

#### 2.1.3 密码重置

| 属性 | 内容 |
|------|------|
| **需求ID** | F-AUTH-003 |
| **需求名称** | 密码重置 |
| **优先级** | P1 |

**业务流程**:
```
1. 用户输入注册邮箱
2. 系统发送重置链接到邮箱
3. 用户点击链接（有效期24小时）
4. 用户输入新密码
5. 系统更新密码
6. 登录页提示"密码已重置"
```

### 2.2 品牌管理模块 (F-BRAND)

#### 2.2.1 添加品牌

| 属性 | 内容 |
|------|------|
| **需求ID** | F-BRAND-001 |
| **需求名称** | 添加品牌 |
| **优先级** | P0 |
| **用户故事** | US-001 |
| **描述** | 用户可以添加要监控的品牌 |

**详细规格**:

| 字段 | 类型 | 必填 | 验证规则 | 来源依据 |
|------|------|------|---------|---------|
| 品牌名称 | string | 是 | 2-50字符，唯一 | 用户输入 |
| 品牌别名 | string[] | 否 | 每个2-20字符，最多10个 | 用户输入 |
| 官方网站 | string | 否 | URL格式 | 用户输入 |
| 所属行业 | enum | 否 | 预定义行业列表 | 用户选择 |
| 监控平台 | string[] | 是 | 至少选择1个，最多7个 | 用户选择 |

**支持的AI平台**:

| 平台代码 | 平台名称 | 平台URL |
|---------|---------|---------|
| wenxin | 文心一言 | https://yiyan.baidu.com |
| kimi | Kimi | https://kimi.moonshot.cn |
| tongyi | 通义千问 | https://qianwen.aliyun.com |
| doubao | 豆包 | https://www.doubao.com |
| xinghuo | 讯飞星火 | https://xinghuo.xfyun.cn |
| tiangong | 天工 | https://www.tiangong.cn |
| qingyan | 清言 | https://qingyan.chat |

**业务流程**:
```
1. 用户点击"添加品牌"按钮
2. 填写品牌基本信息
3. 选择要监控的AI平台
4. 设置查询频率（每日/每周/每月）
5. 点击"确认添加"
6. 系统创建品牌记录
7. 触发首次数据采集
8. 跳转到品牌看板
```

**约束条件**:
- 免费用户最多添加1个品牌
- Pro用户最多添加5个品牌
- 品牌名称唯一性校验（不区分大小写）

#### 2.2.2 编辑品牌

| 属性 | 内容 |
|------|------|
| **需求ID** | F-BRAND-002 |
| **需求名称** | 编辑品牌 |
| **优先级** | P1 |

**可编辑字段**:
- 品牌别名
- 官方网站
- 所属行业
- 监控平台
- 查询频率

**不可编辑字段**:
- 品牌名称（删除重建）

#### 2.2.3 删除品牌

| 属性 | 内容 |
|------|------|
| **需求ID** | F-BRAND-003 |
| **需求名称** | 删除品牌 |
| **优先级** | P1 |

**业务流程**:
```
1. 用户点击"删除品牌"
2. 弹出确认对话框："确定删除品牌 [名称]？此操作不可恢复。"
3. 用户确认删除
4. 级联删除该品牌的所有引用记录
5. 刷新品牌列表
```

#### 2.2.4 竞品管理

| 属性 | 内容 |
|------|------|
| **需求ID** | F-BRAND-004 |
| **需求名称** | 竞品管理 |
| **优先级** | P1 |
| **用户故事** | US-003 |

**详细规格**:

| 字段 | 类型 | 必填 | 验证规则 |
|------|------|------|---------|
| 竞品名称 | string | 是 | 2-50字符 |
| 竞品别名 | string[] | 否 | 每个2-20字符 |

**约束条件**:
- 每个品牌最多添加5个竞品
- 竞品名称不能与己方品牌重复

### 2.3 数据采集模块 (F-DATA)

#### 2.3.1 定时数据采集

| 属性 | 内容 |
|------|------|
| **需求ID** | F-DATA-001 |
| **需求名称** | 定时数据采集 |
| **优先级** | P0 |
| **描述** | 系统按照用户设定的频率自动采集数据 |

**采集频率**:

| 频率 | 说明 | 技术实现 |
|------|------|---------|
| 每日 | 每天凌晨2点执行 | APScheduler每日触发 |
| 每周 | 每周一凌晨2点执行 | APScheduler周触发 |
| 每月 | 每月1日凌晨2点执行 | APScheduler月触发 |

**采集流程**:
```
1. 调度器扫描所有待采集品牌
2. 按品牌生成查询任务
3. 遍历各AI平台适配器
4. 构造Prompt并调用平台API
5. 解析响应，提取引用信息
6. 计算品牌匹配得分
7. 存储引用记录
8. 更新品牌下次采集时间
```

**错误处理**:

| 错误类型 | 处理方式 | 重试策略 |
|---------|---------|---------|
| 平台API超时 | 记录错误，继续其他平台 | 3次重试，间隔5分钟 |
| 平台返回错误 | 记录错误码，跳过该平台 | 不重试 |
| 网络中断 | 暂停采集，告警通知 | 恢复后继续 |
| 配额超限 | 暂停采集，记录日志 | 次日重试 |

#### 2.3.2 手动立即查询

| 属性 | 内容 |
|------|------|
| **需求ID** | F-DATA-002 |
| **需求名称** | 手动立即查询 |
| **优先级** | P0 |
| **用户故事** | US-002 |

**业务流程**:
```
1. 用户点击品牌看板的"立即查询"按钮
2. 选择要查询的平台（默认全部）
3. 点击"确认"
4. 显示执行进度条
5. 实时显示各平台执行状态
6. 执行完成后刷新看板数据
```

**限流规则**:
- 免费用户: 10次/天
- Pro用户: 100次/天
- 超过限制提示升级

#### 2.3.3 数据清洗与标准化

| 属性 | 内容 |
|------|------|
| **需求ID** | F-DATA-003 |
| **需求名称** | 数据清洗与标准化 |
| **优先级** | P0 |

**处理流程**:

1. **原始响应清洗**
   - 移除控制字符
   - 截断超长文本（保留前10000字符）
   - 编码转换（统一UTF-8）

2. **品牌匹配检测**
   ```
   匹配流程:
   1. 精确匹配: 品牌名称完整出现在文本中
   2. 别名匹配: 任意别名出现在文本中
   3. 模糊匹配: 相似度 > 0.4 的候选词
   4. 返回匹配结果: cited, confidence, match_type, position
   ```

3. **竞品检测**
   - 基于预定义品牌库匹配
   - 排除己方品牌和无效匹配

### 2.4 分析引擎模块 (F-ANALYTICS)

#### 2.4.1 AI曝光度评分

| 属性 | 内容 |
|------|------|
| **需求ID** | F-ANALYTICS-001 |
| **需求名称** | AI曝光度评分 |
| **优先级** | P0 |
| **用户故事** | US-001 |
| **参考依据** | [Visibili.ai评分模型](https://visibili.ai/blogs/metrics-for-ai-driven-brand-visibility) |

**评分公式**:

```
综合评分 = MIN(100, 提及率得分 × 0.3 + SOV得分 × 0.4 + 引用质量得分 × 0.3)

其中:
- 提及率得分 = (品牌被引用次数 / 查询总次数) × 100  → 范围: 0-100
- SOV得分 = (品牌引用次数 / (品牌+所有竞品引用次数)) × 100  → 范围: 0-100
- 引用质量得分 = (情感系数 × 0.4 + 位置系数 × 0.3 + 长度系数 × 0.3) × 100  → 范围: 0-100

情感系数:
- 正面: 1.0
- 中性: 0.6
- 负面: 0.2

位置系数:
- 第1位: 1.0
- 第2-3位: 0.8
- 第4-5位: 0.6
- 5位以后: 0.4

长度系数:
- 引用文本 > 100字: 1.0
- 引用文本 50-100字: 0.8
- 引用文本 < 50字: 0.6
```

**计算示例**:

```
假设:
- 查询总次数: 100次
- 品牌被引用次数: 60次 → 提及率得分 = 60/100 × 100 = 60
- 品牌引用次数: 60, 竞品引用次数: 40 → SOV得分 = 60/(60+40) × 100 = 60
- 引用质量得分: (1.0×0.4 + 0.8×0.3 + 0.8×0.3) × 100 = 72

综合评分 = MIN(100, 60×0.3 + 60×0.4 + 72×0.3) = MIN(100, 63.6) = 63.6 ≈ 64分
```

**评分展示**:
- 综合评分: 0-100整数
- 平台评分: 0-100整数
- 趋势: 较昨日/上周/上月变化

#### 2.4.2 竞品对比分析

| 属性 | 内容 |
|------|------|
| **需求ID** | F-ANALYTICS-002 |
| **需求名称** | 竞品对比分析 |
| **优先级** | P1 |
| **用户故事** | US-003 |

**分析维度**:

| 维度 | 计算方式 | 展示形式 |
|------|---------|---------|
| 综合评分对比 | 各方综合评分差值 | 雷达图 |
| 平台分别对比 | 各平台评分差值 | 柱状图 |
| 趋势对比 | 30天评分变化趋势 | 折线图 |
| 份额对比 | SOV占比 | 饼图 |

#### 2.4.3 数据统计

| 属性 | 内容 |
|------|------|
| **需求ID** | F-ANALYTICS-003 |
| **需求名称** | 数据统计 |
| **优先级** | P1 |

**统计指标**:

| 指标 | 说明 | 计算方式 |
|------|------|---------|
| 总查询次数 | 累计查询次数 | COUNT(*) |
| 总引用次数 | 被引用次数 | SUM(cited=1) |
| 今日引用数 | 今日被引用 | COUNT(*) WHERE date=today |
| 平均评分 | 历史平均评分 | AVG(score) |
| 最高评分 | 历史最高评分 | MAX(score) |
| 最低评分 | 历史最低评分 | MIN(score) |

### 2.5 报告模块 (F-REPORT)

#### 2.5.1 报告生成

| 属性 | 内容 |
|------|------|
| **需求ID** | F-REPORT-001 |
| **需求名称** | 报告生成 |
| **优先级** | P1 |
| **用户故事** | US-004 |

**报告类型**:

| 类型 | 内容 | 生成频率 |
|------|------|---------|
| 仪表盘快照 | 关键指标截图 | 随时 |
| 周报 | 本周数据汇总 | 每周一自动 |
| 月报 | 月度分析与建议 | 每月1日自动 |
| 竞品分析 | 竞品动态 | 按需 |

**报告内容**:

```
PDF报告结构:
├── 执行摘要
│   ├── 综合评分
│   ├── 环比变化
│   └── 关键洞察
├── 品牌曝光度分析
│   ├── 各平台评分
│   ├── 引用趋势
│   └── 情感分布
├── 竞品对比
│   ├── 综合对比
│   ├── 平台对比
│   └── 差距分析
├── 引用详情
│   ├── 引用片段
│   ├── 引用位置
│   └── 情感倾向
└── 优化建议
    ├── 短期建议
    └── 长期建议
```

#### 2.5.2 报告导出

| 属性 | 内容 |
|------|------|
| **需求ID** | F-REPORT-002 |
| **需求名称** | 报告导出 |
| **优先级** | P1 |

**导出格式**:

| 格式 | 适用场景 | 文件名格式 |
|------|---------|-----------|
| PDF | 正式汇报 | {brand}_report_{date}.pdf |
| CSV | 数据分析 | {brand}_data_{date}.csv |
| PNG | 截图分享 | {brand}_snapshot_{date}.png |

### 2.6 预警模块 (F-ALERT)

#### 2.6.1 预警规则配置

| 属性 | 内容 |
|------|------|
| **需求ID** | F-ALERT-001 |
| **需求名称** | 预警规则配置 |
| **优先级** | P2 |

**预警类型**:

| 类型 | 触发条件 | 通知方式 |
|------|---------|---------|
| 评分骤降 | 综合评分下降 > 20 | 邮件 |
| 竞品超越 | 竞品评分 > 己方评分 | 邮件 |
| 数据异常 | 单日引用量为0 | 邮件 |
| 查询失败 | 连续3次查询失败 | 邮件 |

**配置项**:

| 配置项 | 说明 | 默认值 |
|--------|------|-------|
| 评分骤降阈值 | 百分比 | 20% |
| 预警通知邮箱 | 接收邮箱 | 用户注册邮箱 |
| 预警开关 | 启用/禁用 | 启用 |

### 2.7 系统管理模块 (F-SYSTEM)

#### 2.7.1 用户管理

| 属性 | 内容 |
|------|------|
| **需求ID** | F-SYSTEM-001 |
| **需求名称** | 用户管理 |
| **优先级** | P0 |

**功能点**:

| 功能 | 说明 |
|------|------|
| 个人信息 | 查看/编辑昵称、手机号 |
| 修改密码 | 原密码+新密码+确认密码 |
| 套餐信息 | 查看当前套餐、用量统计 |
| 注销账户 | 30天冷静期后生效 |

#### 2.7.2 套餐管理

| 属性 | 内容 |
|------|------|
| **需求ID** | F-SYSTEM-002 |
| **需求名称** | 套餐管理 |
| **优先级** | P1 |

**套餐规格**:

| 套餐 | 价格 | 品牌数 | 竞品数/品牌 | 每日查询 | 报告导出 |
|------|------|--------|------------|---------|---------|
| Free | ¥0 | 1 | 2 | 5 | 1份/周 |
| Pro | ¥999/月 | 5 | 5 | 100 | 不限 |
| Enterprise | ¥2999/月 | 无限 | 10 | 不限 | 不限 |

---

## 3. 接口需求规格

### 3.1 API设计原则

| 原则 | 说明 |
|------|------|
| RESTful | 使用标准HTTP方法 |
| JSON格式 | 请求/响应均为JSON |
| 版本控制 | URL包含版本号 /api/v1/ |
| 认证 | Bearer Token |
| 限流 | 全局100次/分钟 |

### 3.2 认证接口

#### 3.2.1 用户注册

```
POST /api/v1/auth/register

Request:
{
  "email": "user@example.com",
  "password": "Password123",
  "confirm_password": "Password123",
  "code": "123456"
}

Response 201:
{
  "id": "uuid",
  "email": "user@example.com",
  "name": null,
  "plan": "free",
  "created_at": "2026-05-01T00:00:00Z"
}
```

#### 3.2.2 用户登录

```
POST /api/v1/auth/login

Request:
{
  "email": "user@example.com",
  "password": "Password123"
}

Response 200:
{
  "access_token": "eyJhbG...",
  "token_type": "bearer",
  "expires_in": 86400,
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "name": "张三",
    "plan": "free"
  }
}
```

#### 3.2.3 获取当前用户

```
GET /api/v1/auth/me

Headers:
  Authorization: Bearer {token}

Response 200:
{
  "id": "uuid",
  "email": "user@example.com",
  "name": "张三",
  "plan": "pro",
  "max_queries": 100,
  "is_admin": false
}
```

### 3.3 品牌接口

#### 3.3.1 品牌列表

```
GET /api/v1/brands/

Headers:
  Authorization: Bearer {token}

Query:
  skip: int (default 0)
  limit: int (default 20, max 100)

Response 200:
{
  "items": [
    {
      "id": "uuid",
      "name": "示例品牌",
      "aliases": ["示例", "example"],
      "platforms": ["wenxin", "kimi"],
      "frequency": "daily",
      "status": "active",
      "score": 78,
      "last_queried_at": "2026-05-01T00:00:00Z",
      "next_query_at": "2026-05-02T00:00:00Z"
    }
  ],
  "total": 1
}
```

#### 3.3.2 创建品牌

```
POST /api/v1/brands/

Headers:
  Authorization: Bearer {token}

Request:
{
  "name": "示例品牌",
  "aliases": ["示例", "example"],
  "website": "https://example.com",
  "industry": "technology",
  "platforms": ["wenxin", "kimi"],
  "frequency": "daily"
}

Response 201:
{
  "id": "uuid",
  "name": "示例品牌",
  "aliases": ["示例", "example"],
  "platforms": ["wenxin", "kimi"],
  "frequency": "daily",
  "status": "active",
  "created_at": "2026-05-01T00:00:00Z"
}
```

#### 3.3.3 品牌详情

```
GET /api/v1/brands/{brand_id}/

Headers:
  Authorization: Bearer {token}

Response 200:
{
  "id": "uuid",
  "name": "示例品牌",
  "aliases": ["示例", "example"],
  "website": "https://example.com",
  "industry": "technology",
  "platforms": ["wenxin", "kimi", "tongyi"],
  "frequency": "daily",
  "status": "active",
  "score": {
    "overall": 78,
    "wenxin": 85,
    "kimi": 72,
    "tongyi": 68
  },
  "competitors": [
    {
      "id": "uuid",
      "name": "竞品A",
      "score": 65
    }
  ],
  "last_queried_at": "2026-05-01T00:00:00Z",
  "next_query_at": "2026-05-02T00:00:00Z"
}
```

#### 3.3.4 立即查询

```
POST /api/v1/brands/{brand_id}/query/

Headers:
  Authorization: Bearer {token}

Request:
{
  "platforms": ["wenxin", "kimi"] // 可选，默认全部
}

Response 202:
{
  "task_id": "uuid",
  "status": "pending",
  "message": "查询任务已加入队列"
}
```

### 3.4 竞品接口

#### 3.4.1 竞品列表

```
GET /api/v1/brands/{brand_id}/competitors/

Response 200:
{
  "items": [
    {
      "id": "uuid",
      "name": "竞品A",
      "aliases": ["别名"],
      "score": 65,
      "last_queried_at": "2026-05-01T00:00:00Z"
    }
  ],
  "total": 1
}
```

#### 3.4.2 添加竞品

```
POST /api/v1/brands/{brand_id}/competitors/

Request:
{
  "name": "竞品A",
  "aliases": ["别名"]
}

Response 201:
{
  "id": "uuid",
  "name": "竞品A",
  "aliases": ["别名"]
}
```

### 3.5 引用记录接口

#### 3.5.1 引用记录列表

```
GET /api/v1/citations/

Headers:
  Authorization: Bearer {token}

Query:
  brand_id: uuid (optional)
  platform: string (optional)
  start_date: ISO date (optional)
  end_date: ISO date (optional)
  skip: int (default 0)
  limit: int (default 20, max 100)

Response 200:
{
  "items": [
    {
      "id": "uuid",
      "brand_id": "uuid",
      "platform": "wenxin",
      "cited": true,
      "citation_text": "示例品牌是...",
      "position": 1,
      "sentiment": "positive",
      "competitor_brands": ["竞品A"],
      "queried_at": "2026-05-01T00:00:00Z"
    }
  ],
  "total": 100
}
```

#### 3.5.2 引用统计

```
GET /api/v1/citations/stats/

Headers:
  Authorization: Bearer {token}

Query:
  brand_id: uuid (optional)

Response 200:
{
  "total_queries": 1000,
  "total_citations": 350,
  "citation_rate": 35.0,
  "platform_breakdown": {
    "wenxin": {"queries": 150, "citations": 60, "rate": 40.0},
    "kimi": {"queries": 150, "citations": 45, "rate": 30.0}
  },
  "sentiment_breakdown": {
    "positive": 200,
    "neutral": 120,
    "negative": 30
  },
  "trend": [
    {"date": "2026-04-01", "score": 72},
    {"date": "2026-04-02", "score": 75}
  ]
}
```

### 3.6 报告接口

#### 3.6.1 导出CSV

```
GET /api/v1/reports/export/csv?brand_id={brand_id}&start_date={date}&end_date={date}

Headers:
  Authorization: Bearer {token}

Response 200:
Content-Type: text/csv
Content-Disposition: attachment; filename="brand_data_2026-05-01.csv"
```

#### 3.6.2 导出PDF

```
GET /api/v1/reports/export/pdf?brand_id={brand_id}&type={weekly|monthly|custom}

Headers:
  Authorization: Bearer {token}

Response 200:
Content-Type: application/pdf
Content-Disposition: attachment; filename="brand_report_2026-05-01.pdf"
```

---

## 4. 数据需求规格

### 4.1 数据实体设计

#### 4.1.1 User (用户)

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK | 主键 |
| email | VARCHAR(255) | UNIQUE, NOT NULL | 邮箱 |
| password_hash | VARCHAR(255) | NOT NULL | 密码哈希 |
| name | VARCHAR(100) | NULL | 昵称 |
| plan | VARCHAR(20) | DEFAULT 'free' | 套餐 |
| max_queries | INT | DEFAULT 5 | 最大查询数 |
| is_active | BOOLEAN | DEFAULT TRUE | 账户状态 |
| email_verified | BOOLEAN | DEFAULT FALSE | 邮箱验证 |
| verification_code | VARCHAR(6) | NULL | 验证码 |
| reset_token | VARCHAR(255) | NULL | 重置令牌 |
| created_at | TIMESTAMP | NOT NULL | 创建时间 |
| updated_at | TIMESTAMP | NOT NULL | 更新时间 |

#### 4.1.2 Brand (品牌)

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK | 主键 |
| user_id | UUID | FK(users.id), NOT NULL | 所属用户 |
| name | VARCHAR(50) | UNIQUE, NOT NULL | 品牌名称 |
| aliases | JSON | DEFAULT [] | 品牌别名 |
| website | VARCHAR(500) | NULL | 官网 |
| industry | VARCHAR(50) | NULL | 行业 |
| platforms | JSON | NOT NULL | 监控平台 |
| frequency | VARCHAR(20) | DEFAULT 'weekly' | 查询频率 |
| status | VARCHAR(20) | DEFAULT 'active' | 状态 |
| last_queried_at | TIMESTAMP | NULL | 上次查询 |
| next_query_at | TIMESTAMP | NULL | 下次查询 |
| created_at | TIMESTAMP | NOT NULL | 创建时间 |
| updated_at | TIMESTAMP | NOT NULL | 更新时间 |

**索引**:
- idx_brands_user_id
- idx_brands_status

#### 4.1.3 Competitor (竞品)

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK | 主键 |
| brand_id | UUID | FK(brands.id), NOT NULL | 所属品牌 |
| name | VARCHAR(50) | NOT NULL | 竞品名称 |
| aliases | JSON | DEFAULT [] | 竞品别名 |
| created_at | TIMESTAMP | NOT NULL | 创建时间 |

**索引**:
- idx_competitors_brand_id

#### 4.1.4 CitationRecord (引用记录)

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK | 主键 |
| brand_id | UUID | FK(brands.id), NOT NULL | 所属品牌 |
| platform | VARCHAR(50) | NOT NULL | 平台 |
| cited | BOOLEAN | NOT NULL | 是否引用 |
| citation_text | TEXT | NULL | 引用文本 |
| citation_position | INT | NULL | 引用位置 |
| sentiment | VARCHAR(20) | NULL | 情感倾向 |
| competitor_brands | JSON | DEFAULT [] | 竞品列表 |
| raw_response | TEXT | NULL | 原始响应 |
| confidence | FLOAT | NULL | 置信度 |
| match_type | VARCHAR(20) | NULL | 匹配类型 |
| queried_at | TIMESTAMP | NOT NULL | 查询时间 |

**索引**:
- idx_citations_brand_id
- idx_citations_platform
- idx_citations_queried_at

#### 4.1.5 QueryTask (查询任务)

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK | 主键 |
| brand_id | UUID | FK(brands.id), NOT NULL | 所属品牌 |
| platform | VARCHAR(50) | NOT NULL | 平台 |
| status | VARCHAR(20) | NOT NULL | 状态 |
| error_message | TEXT | NULL | 错误信息 |
| scheduled_at | TIMESTAMP | NOT NULL | 计划时间 |
| started_at | TIMESTAMP | NULL | 开始时间 |
| completed_at | TIMESTAMP | NULL | 完成时间 |

**索引**:
- idx_query_tasks_brand_id
- idx_query_tasks_status

### 4.2 数据保留策略

| 数据类型 | 保留期限 | 处理方式 |
|---------|---------|---------|
| 引用记录 | 2年 | 2年前的记录归档到冷存储 |
| 查询任务 | 90天 | 90天后删除 |
| 用户行为日志 | 180天 | 180天后删除 |
| 审计日志 | 1年 | 1年后归档 |

---

## 5. 非功能需求规格

### 5.1 性能需求

| 指标 | 目标值 | 说明 |
|------|-------|------|
| 页面首次加载 (FCP) | < 3秒 | Dashboard首屏 |
| API平均响应时间 | < 200ms | 查询类接口 |
| API P99响应时间 | < 500ms | 查询类接口 |
| 并发用户数 | 1000+ | v1.0目标 |
| 数据采集延迟 | < 24小时 | 定时任务完成后 |

### 5.2 可用性需求

| 指标 | 目标值 | 说明 |
|------|-------|------|
| 系统可用性 | 99.5% | 月度计算 |
| 故障恢复时间 (MTTR) | < 4小时 | 平均恢复时间 |
| 故障间隔时间 (MTBF) | > 720小时 | 30天内 |
| 数据备份 | 每日全量 | 保留30天 |

### 5.3 安全需求

| 需求 | 实现方式 |
|------|---------|
| 传输加密 | TLS 1.2+ |
| 密码存储 | bcrypt |
| 会话管理 | JWT，24小时过期 |
| 访问控制 | RBAC |
| 数据隔离 | 租户级逻辑隔离 |
| SQL注入防护 | 参数化查询 |
| XSS防护 | 输出编码 |
| CSRF防护 | Token验证 |

### 5.4 兼容性需求

| 类型 | 要求 |
|------|------|
| 浏览器 | Chrome/Firefox/Safari/Edge 最近2个版本 |
| 移动端 | 响应式，支持1024px+ |
| API版本 | 保持v1版本兼容2年 |

---

## 6. 约束条件

### 6.1 技术约束

| 约束项 | 说明 |
|--------|------|
| 技术栈 | 现有Python FastAPI + Next.js不可变更 |
| 数据库 | PostgreSQL + Redis |
| 部署方式 | Docker Compose |

### 6.2 时间约束

| 里程碑 | 截止日期 |
|--------|---------|
| v1.0 MVP完成 | 2026-Q2末 |

### 6.3 资源约束

| 资源 | 限制 |
|------|------|
| 开发团队 | 5人 |
| 服务器成本 | < ¥10万/月 |

---

## 7. 需求追踪矩阵

### 7.1 需求-用户故事追踪

| 需求ID | 用户故事 | 功能模块 | 优先级 | 状态 |
|--------|---------|---------|--------|------|
| F-AUTH-001 | - | 认证模块 | P0 | 待开发 |
| F-AUTH-002 | - | 认证模块 | P0 | 待开发 |
| F-AUTH-003 | - | 认证模块 | P1 | 待开发 |
| F-BRAND-001 | US-001 | 品牌管理 | P0 | 待开发 |
| F-BRAND-002 | - | 品牌管理 | P1 | 待开发 |
| F-BRAND-003 | - | 品牌管理 | P1 | 待开发 |
| F-BRAND-004 | US-003 | 竞品管理 | P1 | 待开发 |
| F-DATA-001 | US-002 | 数据采集 | P0 | 待开发 |
| F-DATA-002 | US-002 | 数据采集 | P0 | 待开发 |
| F-DATA-003 | - | 数据采集 | P0 | 待开发 |
| F-ANALYTICS-001 | US-001 | 分析引擎 | P0 | 待开发 |
| F-ANALYTICS-002 | US-003 | 分析引擎 | P1 | 待开发 |
| F-ANALYTICS-003 | - | 分析引擎 | P1 | 待开发 |
| F-REPORT-001 | US-004 | 报告模块 | P1 | 待开发 |
| F-REPORT-002 | US-004 | 报告模块 | P1 | 待开发 |
| F-ALERT-001 | - | 预警模块 | P2 | 待开发 |
| F-SYSTEM-001 | - | 系统管理 | P0 | 待开发 |

### 7.2 需求-接口追踪

| 需求ID | 对应API端点 | 方法 |
|--------|------------|------|
| F-AUTH-001 | /api/v1/auth/register | POST |
| F-AUTH-002 | /api/v1/auth/login | POST |
| F-AUTH-003 | /api/v1/auth/forgot-password | POST |
| F-BRAND-001 | /api/v1/brands/ | POST |
| F-BRAND-002 | /api/v1/brands/{id} | PUT |
| F-BRAND-003 | /api/v1/brands/{id} | DELETE |
| F-BRAND-004 | /api/v1/brands/{id}/competitors/ | POST |
| F-DATA-002 | /api/v1/brands/{id}/query/ | POST |
| F-ANALYTICS-001 | /api/v1/citations/stats/ | GET |
| F-ANALYTICS-002 | /api/v1/analytics/compare/ | GET |
| F-REPORT-001 | /api/v1/reports/generate/ | POST |
| F-REPORT-002 | /api/v1/reports/export/{format} | GET |

---

## 8. 附录

### 8.1 错误码定义

| 错误码 | HTTP状态码 | 说明 |
|--------|-----------|------|
| AUTH_001 | 400 | 邮箱格式错误 |
| AUTH_002 | 409 | 邮箱已被注册 |
| AUTH_003 | 401 | 认证失败 |
| AUTH_004 | 403 | 账户已被禁用 |
| BRAND_001 | 404 | 品牌不存在 |
| BRAND_002 | 400 | 品牌数量超限 |
| BRAND_003 | 409 | 品牌名称重复 |
| QUERY_001 | 429 | 查询次数超限 |
| QUERY_002 | 500 | 平台API调用失败 |

### 8.2 变更记录

| 版本 | 日期 | 变更内容 | 作者 |
|------|------|---------|------|
| v1.0 | 2026-05-01 | 初稿创建 | GEO产品团队 |

---

**审批签名**:

| 角色 | 姓名 | 日期 | 签名 |
|------|------|------|------|
| 产品负责人 | | | |
| 技术负责人 | | | |
| 测试负责人 | | | |
