# GEO 平台技术详细设计文档

> **文档版本**: v1.0
> **创建日期**: 2026-05-01
> **状态**: 待评审
> **依据文档**:
> - [PRD v1.0](../1-PRD/01-GEO平台PRD-v1.0.md)
> - [需求规格说明书](../2-需求规格/01-GEO平台需求规格说明书-v1.0.md)
> - [第二轮专家讨论](../6-专家讨论/01-第二轮专家讨论-技术架构.md)

---

## 目录

1. [系统架构设计](#1-系统架构设计)
2. [核心模块设计](#2-核心模块设计)
3. [接口设计](#3-接口设计)
4. [数据模型设计](#4-数据模型设计)
5. [安全设计](#5-安全设计)
6. [部署架构](#6-部署架构)
7. [监控与运维](#7-监控与运维)

---

## 1. 系统架构设计

### 1.1 整体架构

```
┌─────────────────────────────────────────────────────────────────┐
│                         用户层 (Client)                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐            │
│  │   Web App   │  │  Mobile Web │  │    API      │            │
│  │  (Next.js)  │  │  (响应式)   │  │  (REST)     │            │
│  └─────────────┘  └─────────────┘  └─────────────┘            │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                         网关层 (Gateway)                         │
│  ┌─────────────────────────────────────────────────────────────┐ │
│  │                     Nginx / API Gateway                     │ │
│  │  - 负载均衡    - SSL终止    - 限流      - 认证            │ │
│  └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                         应用层 (Application)                     │
│  ┌──────────────────┐          ┌──────────────────┐            │
│  │   前端服务        │          │   后端服务        │            │
│  │   (Next.js)      │          │   (FastAPI)      │            │
│  │   Port: 3000     │          │   Port: 8000     │            │
│  └──────────────────┘          └──────────────────┘            │
│                                      │                          │
│                           ┌──────────┴──────────┐               │
│                           ▼                     ▼               │
│                    ┌──────────────┐      ┌──────────────┐       │
│                    │   Worker 1   │      │   Worker 2   │       │
│                    │  (Scheduler) │      │  (Scheduler) │       │
│                    └──────────────┘      └──────────────┘       │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                         数据层 (Data)                            │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐         │
│  │  PostgreSQL  │  │    Redis     │  │   MinIO      │         │
│  │   主数据库    │  │   缓存/队列   │  │   文件存储   │         │
│  └──────────────┘  └──────────────┘  └──────────────┘         │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                         外部服务 (External)                       │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐         │
│  │  LLM APIs    │  │  Email API   │  │  短信 API    │         │
│  │ (DeepSeek等) │  │   (SendGrid) │  │  (阿里云)    │         │
│  └──────────────┘  └──────────────┘  └──────────────┘         │
└─────────────────────────────────────────────────────────────────┘
```

### 1.2 技术栈总览

| 层级 | 技术选型 | 版本 | 说明 |
|------|---------|------|------|
| **前端框架** | Next.js | 14.x | App Router, React 18 |
| **UI组件** | shadcn/ui | latest | Radix UI + TailwindCSS |
| **图表库** | Recharts | 2.x | 数据可视化 |
| **后端框架** | FastAPI | 0.109+ | 异步高性能 |
| **ORM** | SQLAlchemy | 2.0+ | 异步ORM |
| **数据库** | PostgreSQL | 15+ | 主数据存储 |
| **缓存** | Redis | 7+ | 缓存/队列 |
| **任务调度** | APScheduler | 3.10+ | 定时任务 |
| **LLM SDK** | OpenAI SDK | 1.x | 统一LLM调用 |
| **文档** | Alembic | - | 数据库迁移 |

### 1.3 模块架构

```
backend/
├── app/
│   ├── api/                    # API路由层
│   │   ├── v1/
│   │   │   ├── auth.py       # 认证接口
│   │   │   ├── brands.py     # 品牌接口
│   │   │   ├── competitors.py # 竞品接口
│   │   │   ├── citations.py   # 引用接口
│   │   │   ├── reports.py     # 报告接口
│   │   │   └── admin.py      # 管理接口
│   │   └── deps.py           # 依赖注入
│   │
│   ├── core/                  # 核心模块
│   │   ├── config.py         # 配置管理
│   │   ├── security.py       # 安全认证
│   │   └── exceptions.py     # 异常定义
│   │
│   ├── models/                # 数据模型
│   │   ├── user.py
│   │   ├── brand.py
│   │   ├── competitor.py
│   │   ├── citation.py
│   │   └── query_task.py
│   │
│   ├── schemas/               # Pydantic模型
│   │   ├── auth.py
│   │   ├── brand.py
│   │   ├── citation.py
│   │   └── report.py
│   │
│   ├── services/              # 业务逻辑层
│   │   ├── auth_service.py
│   │   ├── brand_service.py
│   │   ├── citation_service.py
│   │   ├── scoring_service.py # 评分计算
│   │   └── report_service.py
│   │
│   ├── workers/              # 后台任务
│   │   ├── scheduler.py      # 任务调度
│   │   ├── citation_engine.py # 引用检测引擎
│   │   ├── adapters/         # 平台适配器
│   │   │   ├── base.py      # 基类
│   │   │   ├── llm_adapter.py # LLM适配器
│   │   │   └── platforms/   # 各平台实现
│   │   └── cache.py         # 缓存管理
│   │
│   ├── db/                   # 数据库
│   │   ├── session.py
│   │   └── repositories/
│   │
│   └── main.py               # 应用入口
│
├── alembic/                  # 数据库迁移
│   └── versions/
│
├── tests/                    # 测试
│
└── requirements.txt
```

---

## 2. 核心模块设计

### 2.1 引用检测引擎 (CitationEngine)

**模块职责**: 统一管理品牌引用检测的核心业务逻辑

**设计原则**:
- 单一职责: 仅负责引用检测流程编排
- 开闭原则: 新增平台只需实现适配器
- 依赖倒置: 通过抽象接口调用平台

**核心类图**:

```python
# 伪代码描述，实际代码见实现文件
class CitationEngine:
    """引用检测引擎核心类"""

    def __init__(self):
        self.llm_adapter: LLMAdapter      # LLM调用适配器
        self.platform_adapters: dict      # 平台适配器映射
        self.cache: CacheService           # 缓存服务
        self.scoring_service: ScoringService # 评分服务

    async def query_brand(
        self,
        keyword: str,
        brand: Brand,
        platforms: list[str]
    ) -> QueryResult:
        """执行品牌查询"""
        pass

    async def calculate_score(
        self,
        citations: list[Citation]
    ) -> BrandScore:
        """计算品牌评分"""
        pass
```

**核心流程**:

```
query_brand 流程:
1. 检查缓存
   └── 命中 → 返回缓存结果
2. 构建查询任务
3. 遍历平台适配器
   ├── LLMAdapter.query()  # 主路径
   └── PlaywrightAdapter.query()  # 兜底
4. 解析响应
5. 品牌匹配检测
6. 竞品识别
7. 计算评分
8. 存储结果
9. 更新缓存
10. 返回结果
```

### 2.2 LLM适配器 (LLMAdapter)

**模块职责**: 统一封装LLM API调用

**LLM API定价依据** (来源: DeepSeek官方API文档 [1], OpenAI官方 [2]):

| LLM | 输入价格 | 输出价格 | 单次成本估算 |
|-----|---------|---------|-------------|
| DeepSeek-V3 | ¥0.002/千tokens | ¥0.008/千tokens | ¥0.006/次 |
| GPT-4o-mini | ¥0.001/千tokens | ¥0.004/千tokens | ¥0.003/次 |

**单次成本计算依据**:
- 输入Prompt: ~1000 tokens (品牌名、别名、查询词)
- 输出响应: ~500 tokens (JSON格式检测结果)
- DeepSeek-V3: 1000×¥0.002 + 500×¥0.008 = ¥0.006
- GPT-4o-mini: 1000×¥0.001 + 500×¥0.004 = ¥0.003

**成本优化建议**:
- 使用DeepSeek-V3作为主LLM (成本最低)
- 启用Prompt缓存进一步降低成本 (~50%节省)
- 批量查询合并请求减少API调用次数

**Prompt模板设计**:

```python
# 品牌引用检测Prompt
BRAND_CITATION_PROMPT = """
你是一个专业的品牌分析助手。请分析以下查询中是否提到了目标品牌。

查询关键词: {keyword}
目标品牌: {brand_name}
品牌别名: {brand_aliases}

请分析AI平台对上述品牌的提及情况，并返回JSON格式的结果:
{{
    "cited": true/false,           // 是否被提及
    "position": 1,                  // 提及位置(1-based)
    "citation_text": "...",         // 引用文本片段(最多200字)
    "sentiment": "positive/neutral/negative",  // 情感倾向
    "confidence": 0.95              // 置信度
}}

请仅返回JSON，不要有其他内容。
"""
```

**错误处理策略**:

| 错误类型 | 处理策略 | 重试次数 |
|---------|---------|---------|
| API超时 | 重试 | 3次 |
| 配额超限 | 切换LLM/排队 | - |
| 响应格式错误 | 使用备用Parser | 1次 |
| 网络错误 | 重试 | 3次 |

### 2.3 平台适配器 (PlatformAdapter)

**模块职责**: 封装各AI平台的查询接口

**适配器结构**:

```python
class BasePlatformAdapter(ABC):
    """平台适配器基类"""

    platform_name: str
    platform_code: str
    api_endpoint: str

    @abstractmethod
    async def query(
        self,
        keyword: str,
        context: QueryContext
    ) -> PlatformResponse:
        """执行平台查询"""
        pass

    def build_prompt(
        self,
        keyword: str,
        brand_name: str,
        brand_aliases: list[str]
    ) -> str:
        """构建查询Prompt"""
        return BRAND_CITATION_PROMPT.format(
            keyword=keyword,
            brand_name=brand_name,
            brand_aliases=", ".join(brand_aliases)
        )


class WenxinAdapter(BasePlatformAdapter):
    """文心一言适配器"""

    platform_name = "文心一言"
    platform_code = "wenxin"
    api_endpoint = "https://api.deepseek.com/v1/chat/completions"

    async def query(self, keyword, context):
        # 实现文心一言特定逻辑
        pass
```

**支持的平台**:

| 平台 | 代码 | API类型 | 备注 |
|------|------|--------|------|
| 文心一言 | wenxin | LLM API | 优先 |
| Kimi | kimi | LLM API | 优先 |
| 通义千问 | tongyi | LLM API | 优先 |
| 豆包 | doubao | LLM API | 优先 |
| 讯飞星火 | xinghuo | LLM API | 优先 |
| 天工 | tiangong | LLM API | 优先 |
| 清言 | qingyan | LLM API | 优先 |

### 2.4 评分服务 (ScoringService)

**模块职责**: 计算品牌AI曝光度评分

**评分算法** (依据 [Visibili.ai模型](https://visibili.ai/blogs/metrics-for-ai-driven-brand-visibility)):

```python
class ScoringService:
    """评分计算服务"""

    WEIGHTS = {
        "mention_rate": 0.30,    # 提及率权重
        "sov": 0.40,            # SOV权重
        "quality": 0.30,        # 引用质量权重
    }

    async def calculate_brand_score(
        self,
        brand_id: UUID,
        time_range: TimeRange
    ) -> BrandScore:
        """计算品牌综合评分"""

        # 1. 获取引用数据
        citations = await self.citation_repo.get_by_brand_and_time(
            brand_id, time_range
        )

        # 2. 计算提及率
        mention_rate = self._calc_mention_rate(citations)

        # 3. 计算SOV
        sov = await self._calc_sov(brand_id, citations)

        # 4. 计算引用质量
        quality = self._calc_quality(citations)

        # 5. 综合评分 (加权求和后取上限100)
        overall = min(100, (
            mention_rate * self.WEIGHTS["mention_rate"] +
            sov * self.WEIGHTS["sov"] +
            quality * self.WEIGHTS["quality"]
        ))

        return BrandScore(
            overall=round(overall, 1),
            mention_rate=mention_rate,
            sov=sov,
            quality=quality,
            platform_breakdown=...,  # 平台分别评分
            trend=...,               # 趋势变化
        )

    def _calc_mention_rate(self, citations: list[Citation]) -> float:
        """计算提及率 = 被引用次数 / 总查询次数"""
        if not citations:
            return 0.0
        cited_count = sum(1 for c in citations if c.cited)
        return (cited_count / len(citations)) * 100

    def _calc_sov(self, brand_id: UUID, citations: list[Citation]) -> float:
        """计算SOV = 品牌引用次数 / (品牌+竞品引用次数)"""
        # 获取竞品引用数据
        competitor_citations = await self._get_competitor_citations(brand_id)
        total_citations = len(citations) + len(competitor_citations)
        if total_citations == 0:
            return 0.0
        return (len(citations) / total_citations) * 100

    def _calc_quality(self, citations: list[Citation]) -> float:
        """计算引用质量"""
        if not citations:
            return 0.0

        total_quality = 0.0
        for c in citations:
            # 情感系数
            sentiment_score = {"positive": 1.0, "neutral": 0.6, "negative": 0.2}.get(c.sentiment, 0.5)
            # 位置系数
            position_score = self._get_position_score(c.position)
            # 长度系数
            length_score = self._get_length_score(c.citation_text)

            quality = sentiment_score * 0.4 + position_score * 0.3 + length_score * 0.3
            total_quality += quality

        return (total_quality / len(citations)) * 100

    def _get_position_score(self, position: int) -> float:
        """获取位置系数"""
        if position == 1:
            return 1.0
        elif position <= 3:
            return 0.8
        elif position <= 5:
            return 0.6
        else:
            return 0.4

    def _get_length_score(self, text: str) -> float:
        """获取引用长度系数"""
        if not text:
            return 0.5
        length = len(text)
        if length > 100:
            return 1.0
        elif length >= 50:
            return 0.8
        else:
            return 0.6
```

### 2.5 缓存服务 (CacheService)

**模块职责**: 管理Redis缓存

**缓存策略**:

| 缓存键 | 数据 | TTL | 说明 |
|--------|------|-----|------|
| brand:{id}:score | 品牌评分 | 5min | 实时性要求高 |
| brand:{id}:stats | 统计数据 | 5min | 聚合数据 |
| platform:config | 平台配置 | 1h | 相对稳定 |
| user:{id}:session | 会话信息 | 24h | 用户会话 |
| rate_limit:{id} | 限流计数 | 1min | 滑动窗口 |

**缓存操作示例**:

```python
class CacheService:
    """缓存服务"""

    def __init__(self, redis: Redis):
        self.redis = redis

    async def get_brand_score(self, brand_id: UUID) -> Optional[BrandScore]:
        """获取品牌评分缓存"""
        key = f"brand:{brand_id}:score"
        data = await self.redis.get(key)
        if data:
            return BrandScore.parse_raw(data)
        return None

    async def set_brand_score(
        self,
        brand_id: UUID,
        score: BrandScore,
        ttl: int = 300
    ):
        """设置品牌评分缓存"""
        key = f"brand:{brand_id}:score"
        await self.redis.setex(key, ttl, score.json())

    async def invalidate_brand(self, brand_id: UUID):
        """失效品牌相关缓存"""
        pattern = f"brand:{brand_id}:*"
        async for key in self.redis.scan_iter(match=pattern):
            await self.redis.delete(key)
```

---

## 3. 接口设计

### 3.1 API设计规范

**版本控制**: URL路径包含版本号 `/api/v1/`

**认证方式**: Bearer Token (JWT)

**请求格式**:
```json
{
  "content-type": "application/json"
}
```

**响应格式**:
```json
{
  "code": 200,
  "message": "success",
  "data": {}
}
```

**错误格式**:
```json
{
  "code": 400,
  "message": "错误描述",
  "detail": "详细错误信息"
}
```

### 3.2 认证接口

#### POST /api/v1/auth/register

**请求**:
```json
{
  "email": "user@example.com",
  "password": "Password123",
  "confirm_password": "Password123"
}
```

**响应** (201):
```json
{
  "code": 201,
  "message": "注册成功",
  "data": {
    "id": "uuid",
    "email": "user@example.com",
    "plan": "free"
  }
}
```

#### POST /api/v1/auth/login

**请求**:
```json
{
  "email": "user@example.com",
  "password": "Password123"
}
```

**响应** (200):
```json
{
  "code": 200,
  "message": "登录成功",
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "token_type": "bearer",
    "expires_in": 86400,
    "user": {
      "id": "uuid",
      "email": "user@example.com",
      "plan": "free"
    }
  }
}
```

### 3.3 品牌接口

#### GET /api/v1/brands/

**查询参数**:
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| skip | int | 0 | 偏移量 |
| limit | int | 20 | 每页数量 |

**响应** (200):
```json
{
  "code": 200,
  "message": "success",
  "data": {
    "items": [
      {
        "id": "uuid",
        "name": "示例品牌",
        "platforms": ["wenxin", "kimi"],
        "frequency": "daily",
        "score": {
          "overall": 78,
          "trend": "+3"
        },
        "last_queried_at": "2026-05-01T00:00:00Z"
      }
    ],
    "total": 1
  }
}
```

#### POST /api/v1/brands/

**请求**:
```json
{
  "name": "示例品牌",
  "aliases": ["示例", "example"],
  "platforms": ["wenxin", "kimi"],
  "frequency": "daily"
}
```

**响应** (201):
```json
{
  "code": 201,
  "message": "品牌创建成功",
  "data": {
    "id": "uuid",
    "name": "示例品牌",
    "status": "active"
  }
}
```

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

**请求**:
```json
{
  "platforms": ["wenxin", "kimi"]
}
```

**响应** (202):
```json
{
  "code": 202,
  "message": "查询任务已加入队列",
  "data": {
    "task_id": "uuid",
    "estimated_time": 30
  }
}
```

### 3.4 引用数据接口

#### GET /api/v1/citations/

**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| brand_id | UUID | 品牌ID |
| platform | string | 平台筛选 |
| start_date | date | 开始日期 |
| end_date | date | 结束日期 |
| skip | int | 偏移量 |
| limit | int | 每页数量 |

**响应** (200):
```json
{
  "code": 200,
  "message": "success",
  "data": {
    "items": [
      {
        "id": "uuid",
        "platform": "wenxin",
        "cited": true,
        "citation_text": "示例品牌是...",
        "position": 1,
        "sentiment": "positive",
        "queried_at": "2026-05-01T00:00:00Z"
      }
    ],
    "total": 100
  }
}
```

#### GET /api/v1/citations/stats/

**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| brand_id | UUID | 品牌ID |

**响应** (200):
```json
{
  "code": 200,
  "message": "success",
  "data": {
    "overall_score": 78,
    "mention_rate": 65.5,
    "sov": 45.2,
    "quality": 72.3,
    "platform_breakdown": {
      "wenxin": {"score": 85, "cited": 45, "total": 50},
      "kimi": {"score": 72, "cited": 36, "total": 50}
    },
    "sentiment_breakdown": {
      "positive": 60,
      "neutral": 30,
      "negative": 10
    },
    "trend": [
      {"date": "2026-04-01", "score": 72},
      {"date": "2026-04-02", "score": 75}
    ]
  }
}
```

### 3.5 报告接口

#### GET /api/v1/reports/export/csv

**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| brand_id | UUID | 品牌ID |
| start_date | date | 开始日期 |
| end_date | date | 结束日期 |

**响应**: CSV文件下载

#### GET /api/v1/reports/export/pdf

**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| brand_id | UUID | 品牌ID |
| type | string | weekly/monthly/custom |

**响应**: PDF文件下载

---

## 4. 数据模型设计

### 4.1 实体关系图

```
┌──────────────┐       ┌──────────────┐       ┌──────────────┐
│    User      │       │    Brand     │       │ Competitor   │
├──────────────┤       ├──────────────┤       ├──────────────┤
│ id (PK)      │───1:N─│ id (PK)      │───1:N─│ id (PK)      │
│ email         │       │ user_id (FK) │       │ brand_id(FK)│
│ password_hash │       │ name         │       │ name         │
│ plan          │       │ aliases      │       │ aliases      │
│ max_queries  │       │ platforms    │       └──────────────┘
│ is_active     │       │ frequency    │
│ created_at    │       │ status       │
└──────────────┘       │ score        │
      │                │ last_queried │
      │                └──────────────┘
      │                      │
      │                ┌─────┴─────┐
      │                │           │
      │           ┌────────────┐ ┌────────────┐
      │           │CitationRec.│ │ QueryTask  │
      │           ├────────────┤ ├────────────┤
      │           │ id (PK)    │ │ id (PK)    │
      └───────────│ brand_id(FK)│ │ brand_id(FK)│
                  │ platform   │ │ platform   │
                  │ cited      │ │ status     │
                  │ sentiment  │ │ scheduled  │
                  │ position   │ │ started_at │
                  │ text       │ │ completed  │
                  │ confidence │ └────────────┘
                  │ match_type │
                  │ queried_at │
                  └────────────┘
```

### 4.2 数据库表结构

#### users

```sql
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    email VARCHAR(255) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    name VARCHAR(100),
    plan VARCHAR(20) DEFAULT 'free',
    max_queries INTEGER DEFAULT 5,
    is_active BOOLEAN DEFAULT TRUE,
    email_verified BOOLEAN DEFAULT FALSE,
    verification_code VARCHAR(6),
    verification_code_expires TIMESTAMP,
    reset_token VARCHAR(255),
    reset_token_expires TIMESTAMP,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_plan ON users(plan);
```

#### brands

```sql
CREATE TABLE brands (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    name VARCHAR(50) UNIQUE NOT NULL,
    aliases JSONB DEFAULT '[]',
    website VARCHAR(500),
    industry VARCHAR(50),
    platforms JSONB NOT NULL DEFAULT '["wenxin", "kimi"]',
    frequency VARCHAR(20) DEFAULT 'weekly',
    status VARCHAR(20) DEFAULT 'active',
    last_queried_at TIMESTAMP,
    next_query_at TIMESTAMP,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_brands_user_id ON brands(user_id);
CREATE INDEX idx_brands_status ON brands(status);
CREATE INDEX idx_brands_next_query ON brands(next_query_at);
```

#### competitors

```sql
CREATE TABLE competitors (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    brand_id UUID NOT NULL REFERENCES brands(id) ON DELETE CASCADE,
    name VARCHAR(50) NOT NULL,
    aliases JSONB DEFAULT '[]',
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_competitors_brand_id ON competitors(brand_id);
```

#### citation_records

```sql
CREATE TABLE citation_records (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    brand_id UUID NOT NULL REFERENCES brands(id) ON DELETE CASCADE,
    platform VARCHAR(50) NOT NULL,
    cited BOOLEAN NOT NULL DEFAULT FALSE,
    citation_text TEXT,
    citation_position INTEGER,
    sentiment VARCHAR(20),
    competitor_brands JSONB DEFAULT '[]',
    raw_response TEXT,
    confidence FLOAT,
    match_type VARCHAR(20),
    queried_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_citations_brand_id ON citation_records(brand_id);
CREATE INDEX idx_citations_platform ON citation_records(platform);
CREATE INDEX idx_citations_queried_at ON citation_records(queried_at);
CREATE INDEX idx_citations_brand_platform ON citation_records(brand_id, platform);
```

#### query_tasks

```sql
CREATE TABLE query_tasks (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    brand_id UUID NOT NULL REFERENCES brands(id) ON DELETE CASCADE,
    platform VARCHAR(50) NOT NULL,
    status VARCHAR(20) NOT NULL DEFAULT 'pending',
    error_message TEXT,
    scheduled_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    started_at TIMESTAMP,
    completed_at TIMESTAMP
);

CREATE INDEX idx_query_tasks_brand_id ON query_tasks(brand_id);
CREATE INDEX idx_query_tasks_status ON query_tasks(status);
CREATE INDEX idx_query_tasks_scheduled ON query_tasks(scheduled_at);
```

---

## 5. 安全设计

### 5.1 认证与授权

**JWT Token结构**:

```json
{
  "sub": "user_id",
  "email": "user@example.com",
  "plan": "free",
  "exp": 1717200000,
  "iat": 1717113600
}
```

**Token配置**:
- 有效期: 24小时
- 刷新策略: 支持刷新Token
- 存储: HttpOnly Cookie

**权限控制**:

| 角色 | 品牌数限制 | 竞品数/品牌 | 每日查询次数 |
|------|-----------|------------|-------------|
| free用户 | 1个品牌 | 2个竞品 | 5次 |
| pro用户 | 5个品牌 | 5个竞品 | 100次 |
| admin | 无限制 | 无限制 | 无限制 |

### 5.2 数据安全

**传输加密**: TLS 1.2+

**存储加密**:
- 密码: bcrypt
- 敏感字段: AES-256

**数据隔离**:
- 租户级隔离 (user_id)
- API级别鉴权

### 5.3 API安全

**限流策略**:

| 接口 | 限制 |
|------|------|
| 登录/注册 | 5次/分钟 |
| 查询接口 | 100次/分钟 |
| 全局 | 1000次/分钟 |

**输入校验**: Pydantic模型自动校验

**SQL注入防护**: SQLAlchemy参数化查询

**XSS防护**: 输出编码

---

## 6. 部署架构

### 6.1 Docker Compose架构

```yaml
version: '3.8'

services:
  # 前端服务
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - NEXT_PUBLIC_API_URL=http://backend:8000
    depends_on:
      - backend
    volumes:
      - ./frontend:/app
      - /app/node_modules

  # 后端服务
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql+asyncpg://postgres:postgres@db:5432/geo_platform
      - REDIS_URL=redis://redis:6379/0
      - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}
    depends_on:
      - db
      - redis
    volumes:
      - ./backend:/app

  # 数据库
  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=geo_platform
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres123
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  # Redis
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

volumes:
  postgres_data:
  redis_data:
```

### 6.2 环境配置

**.env配置示例**:

```bash
# 数据库
DATABASE_URL=postgresql+asyncpg://postgres:postgres123@db:5432/geo_platform

# Redis
REDIS_URL=redis://redis:6379/0

# JWT
JWT_SECRET=your-super-secret-key-change-in-production
JWT_EXPIRE_HOURS=24

# LLM API
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx

# CORS
CORS_ORIGINS=http://localhost:3000,https://geo.example.com
```

---

## 7. 监控与运维

### 7.1 监控指标

**系统指标**:
- CPU使用率
- 内存使用率
- 磁盘使用率
- 网络IO

**应用指标**:
- API响应时间 (P50/P95/P99)
- API错误率
- 活跃用户数
- 查询任务完成率

**业务指标**:
- 品牌数量
- 查询次数
- 引用成功率

### 7.2 日志管理

**日志级别**:
- DEBUG: 开发环境
- INFO: 正常日志
- WARNING: 警告
- ERROR: 错误

**日志格式**:
```json
{
  "timestamp": "2026-05-01T00:00:00Z",
  "level": "INFO",
  "service": "backend",
  "message": "Query completed",
  "context": {
    "brand_id": "uuid",
    "platform": "wenxin",
    "duration_ms": 1234
  }
}
```

### 7.3 告警策略

| 告警类型 | 触发条件 | 通知方式 |
|---------|---------|---------|
| 服务宕机 | HTTP 5xx > 5% | 邮件+钉钉 |
| 响应超时 | P99 > 2s | 邮件 |
| 数据库连接 | 连接数 > 80% | 邮件 |
| API配额 | 使用率 > 90% | 邮件 |

---

## 附录

### A. 变更记录

| 版本 | 日期 | 变更内容 | 作者 |
|------|------|---------|------|
| v1.0 | 2026-05-01 | 初稿创建 | 技术团队 |
| v1.1 | 2026-05-01 | 修正LLM成本估算，增加定价依据来源 | 技术团队 |

### B. 参考资料

1. [FastAPI最佳实践](https://fastapi.tiangolo.net/tutorial/)
2. [SQLAlchemy 2.0文档](https://docs.sqlalchemy.org/en/20/)
3. [Next.js 14文档](https://nextjs.org/docs)
4. [Redis缓存设计模式](https://redis.io/docs/manual/patterns/)
5. [DeepSeek API定价 - 官方文档](https://api-docs.deepseek.com/quick_start/pricing-details-usd/) - 2026-05核实
6. [OpenAI GPT-4o-mini定价 - 官方定价页](https://platform.openai.com/docs/pricing) - 2026-05核实
