# 数据模型

<cite>
**本文档引用的文件**
- [backend/app/models/__init__.py](file://backend/app/models/__init__.py)
- [backend/app/models/user.py](file://backend/app/models/user.py)
- [backend/app/models/query.py](file://backend/app/models/query.py)
- [backend/app/models/query_task.py](file://backend/app/models/query_task.py)
- [backend/app/models/citation_record.py](file://backend/app/models/citation_record.py)
- [backend/app/models/subscription.py](file://backend/app/models/subscription.py)
- [backend/app/database.py](file://backend/app/database.py)
- [backend/alembic/versions/488d0bd5ab01_initial_migration.py](file://backend/alembic/versions/488d0bd5ab01_initial_migration.py)
- [backend/app/schemas/query.py](file://backend/app/schemas/query.py)
- [backend/app/schemas/citation.py](file://backend/app/schemas/citation.py)
- [backend/app/services/query.py](file://backend/app/services/query.py)
- [backend/app/api/queries.py](file://backend/app/api/queries.py)
- [backend/app/services/citation.py](file://backend/app/services/citation.py)
- [backend/app/api/citations.py](file://backend/app/api/citations.py)
- [backend/app/config.py](file://backend/app/config.py)
- [backend/app/api/deps.py](file://backend/app/api/deps.py)
</cite>

## 更新摘要
**所做更改**
- 完善了用户模型的字段映射和关系配置说明
- 详细补充了查询模型的索引策略和生命周期管理
- 新增了查询任务模型的状态机和任务调度机制
- 完善了引用记录模型的统计分析功能说明
- 补充了订阅模型的支付信息字段和状态管理
- 增强了模型间关系映射和级联策略的技术细节
- 完善了序列化、反序列化与数据验证机制
- 新增了使用示例和最佳实践指南

## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)

## 简介
本文件系统性梳理 GEO 平台的 SQLAlchemy ORM 数据模型，覆盖模型类定义、字段映射与关系配置，解释模型间的关系映射（含级联与外键约束）、序列化/反序列化与数据验证机制、生命周期管理与事件钩子、以及使用示例与最佳实践。目标是帮助开发者快速理解并正确使用数据模型，确保在查询、任务调度、引用记录与订阅管理等场景中保持一致性与可维护性。

## 项目结构
后端采用异步 SQLAlchemy（SQLAlchemy 2.x + asyncpg）与 Pydantic 模式进行数据建模与 API 层交互。数据库初始化通过 declarative_base 创建基类，迁移脚本由 Alembic 管理，模型导出统一在 models 包的 __init__.py 中聚合。

```mermaid
graph TB
subgraph "模型层"
U["User<br/>用户"]
Q["Query<br/>查询任务"]
QT["QueryTask<br/>查询任务执行单元"]
CR["CitationRecord<br/>引用记录"]
S["Subscription<br/>订阅"]
end
subgraph "数据库"
DB["PostgreSQL"]
end
subgraph "服务层"
SVCQ["Query 服务"]
SVCC["Citation 服务"]
end
subgraph "API 层"
APIQ["Queries API"]
APIC["Citations API"]
end
U --> Q
Q --> CR
Q --> QT
U --> S
SVCQ --> Q
SVCC --> CR
APIQ --> SVCQ
APIC --> SVCC
Q --- DB
CR --- DB
QT --- DB
S --- DB
U --- DB
```

**图表来源**
- [backend/app/models/user.py:11-41](file://backend/app/models/user.py#L11-L41)
- [backend/app/models/query.py:11-55](file://backend/app/models/query.py#L11-L55)
- [backend/app/models/query_task.py:11-39](file://backend/app/models/query_task.py#L11-L39)
- [backend/app/models/citation_record.py:11-42](file://backend/app/models/citation_record.py#L11-L42)
- [backend/app/models/subscription.py:11-37](file://backend/app/models/subscription.py#L11-L37)
- [backend/app/database.py:1-29](file://backend/app/database.py#L1-L29)
- [backend/app/services/query.py:1-123](file://backend/app/services/query.py#L1-L123)
- [backend/app/services/citation.py:1-359](file://backend/app/services/citation.py#L1-L359)
- [backend/app/api/queries.py:1-86](file://backend/app/api/queries.py#L1-L86)
- [backend/app/api/citations.py:1-78](file://backend/app/api/citations.py#L1-L78)

**章节来源**
- [backend/app/models/__init__.py:1-14](file://backend/app/models/__init__.py#L1-L14)
- [backend/app/database.py:1-29](file://backend/app/database.py#L1-L29)

## 核心组件
本节概述各模型的职责、关键字段与关系，为后续深入分析打基础。

- 用户（User）
  - 负责平台用户信息与配额控制，支持计划类型与最大查询数限制。
  - 关系：一对多到 Query、Subscription，删除时级联删除孤儿对象。
- 查询（Query）
  - 描述关键词、目标品牌、别名、平台集合、频率、状态及下次查询时间等。
  - 关系：多对一到 User；一对多到 CitationRecord、QueryTask；删除时级联删除孤儿对象。
- 查询任务（QueryTask）
  - 记录单次平台查询任务的状态、错误信息与时间戳。
  - 关系：多对一到 Query；删除时级联删除。
- 引用记录（CitationRecord）
  - 记录某次查询在特定平台上的引用情况、竞品品牌列表与原始响应摘要。
  - 关系：多对一到 Query；删除时级联删除。
- 订阅（Subscription）
  - 记录用户的订阅计划、有效期、支付信息与状态。
  - 关系：多对一到 User；删除时级联删除。

**章节来源**
- [backend/app/models/user.py:11-41](file://backend/app/models/user.py#L11-L41)
- [backend/app/models/query.py:11-55](file://backend/app/models/query.py#L11-L55)
- [backend/app/models/query_task.py:11-39](file://backend/app/models/query_task.py#L11-L39)
- [backend/app/models/citation_record.py:11-42](file://backend/app/models/citation_record.py#L11-L42)
- [backend/app/models/subscription.py:11-37](file://backend/app/models/subscription.py#L11-L37)

## 架构总览
下图展示模型与数据库表、索引、外键约束之间的对应关系，以及服务层与 API 层如何通过模型进行数据访问。

```mermaid
erDiagram
USERS {
uuid id PK
string email UK
string password_hash
string name
string plan
integer max_queries
boolean is_active
timestamptz created_at
timestamptz updated_at
}
QUERIES {
uuid id PK
uuid user_id FK
string keyword
string target_brand
jsonb brand_aliases
jsonb platforms
string frequency
string status
timestamptz last_queried_at
timestamptz next_query_at
timestamptz created_at
timestamptz updated_at
}
CITATION_RECORDS {
uuid id PK
uuid query_id FK
string platform
boolean cited
integer citation_position
text citation_text
jsonb competitor_brands
text raw_response
timestamptz queried_at
}
QUERY_TASKS {
uuid id PK
uuid query_id FK
string platform
string status
text error_message
timestamptz scheduled_at
timestamptz started_at
timestamptz completed_at
}
SUBSCRIPTIONS {
uuid id PK
uuid user_id FK
string plan
string status
date start_date
date end_date
numeric amount
string payment_method
string payment_id
timestamptz created_at
}
USERS ||--o{ QUERIES : "拥有"
USERS ||--o{ SUBSCRIPTIONS : "拥有"
QUERIES ||--o{ CITATION_RECORDS : "产生"
QUERIES ||--o{ QUERY_TASKS : "拆分执行"
```

**图表来源**
- [backend/alembic/versions/488d0bd5ab01_initial_migration.py:21-128](file://backend/alembic/versions/488d0bd5ab01_initial_migration.py#L21-L128)
- [backend/app/models/user.py:11-41](file://backend/app/models/user.py#L11-L41)
- [backend/app/models/query.py:11-55](file://backend/app/models/query.py#L11-L55)
- [backend/app/models/citation_record.py:11-42](file://backend/app/models/citation_record.py#L11-L42)
- [backend/app/models/query_task.py:11-39](file://backend/app/models/query_task.py#L11-L39)
- [backend/app/models/subscription.py:11-37](file://backend/app/models/subscription.py#L11-L37)

## 详细组件分析

### 用户模型（User）
- 表名与主键
  - 表名：users；主键：UUID 类型的 id。
- 字段映射
  - 邮箱唯一且必填；密码哈希必填；名称可空；计划默认 free；最大查询数默认 5；激活状态默认 true；创建/更新时间自动填充。
- 关系
  - 一对多到 Query、Subscription，删除用户时级联删除孤儿对象。
- 生命周期与事件
  - created_at/updated_at 使用 server_default/onupdate 注入数据库侧时间戳。
- 序列化/反序列化
  - 通过 Pydantic 模型（如查询响应）在 API 层进行序列化；ORM 对象可直接用于 FastAPI 响应模型（from_attributes）。
- 最佳实践
  - 在创建/更新用户时避免直接修改计划或配额，建议通过专门的服务接口进行校验与审计。

**章节来源**
- [backend/app/models/user.py:11-41](file://backend/app/models/user.py#L11-L41)
- [backend/alembic/versions/488d0bd5ab01_initial_migration.py:23-37](file://backend/alembic/versions/488d0bd5ab01_initial_migration.py#L23-L37)

### 查询模型（Query）
- 表名与主键
  - 表名：queries；主键：UUID 类型的 id。
- 字段映射
  - 外键 user_id 指向 users.id，删除时级联；关键词与目标品牌必填；品牌别名与平台集合默认值合理；频率默认 weekly；状态默认 active；下次查询时间可空；创建/更新时间自动填充。
- 关系
  - 多对一到 User；一对多到 CitationRecord、QueryTask，删除查询时级联删除孤儿对象。
- 索引
  - 为 user_id、status、next_query_at 建立索引以优化查询。
- 生命周期与事件
  - created_at/updated_at 使用 server_default/onupdate 注入数据库侧时间戳。
- 序列化/反序列化
  - 响应模型 QueryResponse 支持 from_attributes，便于 ORM 对象直接转为 API 响应。
- 最佳实践
  - 更新频率时同步更新 next_query_at；在创建查询前检查用户配额。

**章节来源**
- [backend/app/models/query.py:11-55](file://backend/app/models/query.py#L11-L55)
- [backend/alembic/versions/488d0bd5ab01_initial_migration.py:39-59](file://backend/alembic/versions/488d0bd5ab01_initial_migration.py#L39-L59)

### 查询任务模型（QueryTask）
- 表名与主键
  - 表名：query_tasks；主键：UUID 类型的 id。
- 字段映射
  - 外键 query_id 指向 queries.id，删除时级联；平台必填；状态默认 pending；错误信息可空；调度/开始/完成时间可空。
- 关系
  - 多对一到 Query；删除查询任务时级联删除。
- 索引
  - 为 status 建立索引以支持任务调度筛选。
- 生命周期与事件
  - scheduled_at 默认当前时间；其他时间戳按需更新。
- 序列化/反序列化
  - 通过 Pydantic 模型进行 API 层序列化。
- 最佳实践
  - 任务状态机：pending -> started -> completed 或 failed；失败时记录 error_message。

**章节来源**
- [backend/app/models/query_task.py:11-39](file://backend/app/models/query_task.py#L11-L39)
- [backend/alembic/versions/488d0bd5ab01_initial_migration.py:80-94](file://backend/alembic/versions/488d0bd5ab01_initial_migration.py#L80-L94)

### 引用记录模型（CitationRecord）
- 表名与主键
  - 表名：citation_records；主键：UUID 类型的 id。
- 字段映射
  - 外键 query_id 指向 queries.id，删除时级联；平台必填；是否引用默认 false；引用位置可空；引用文本可空；竞品品牌列表默认空数组；原始响应可空；查询时间默认当前时间。
- 关系
  - 多对一到 Query；删除查询时级联删除。
- 索引
  - 为 query_id、queried_at、platform 建立索引以优化统计与检索。
- 生命周期与事件
  - queried_at 默认当前时间。
- 序列化/反序列化
  - 通过 CitationResponse 进行 API 层序列化。
- 最佳实践
  - 统计时按平台与日期聚合，结合索引提升性能。

**章节来源**
- [backend/app/models/citation_record.py:11-42](file://backend/app/models/citation_record.py#L11-L42)
- [backend/alembic/versions/488d0bd5ab01_initial_migration.py:61-78](file://backend/alembic/versions/488d0bd5ab01_initial_migration.py#L61-L78)

### 订阅模型（Subscription）
- 表名与主键
  - 表名：subscriptions；主键：UUID 类型的 id。
- 字段映射
  - 外键 user_id 指向 users.id，删除时级联；计划必填；状态默认 active；起止日期必填；金额可空；支付方式与支付 ID 可空；创建时间默认当前时间。
- 关系
  - 多对一到 User；删除用户时级联删除。
- 生命周期与事件
  - created_at 默认当前时间。
- 序列化/反序列化
  - 通过 Pydantic 模型进行 API 层序列化。
- 最佳实践
  - 订阅到期后应自动调整用户配额与功能权限。

**章节来源**
- [backend/app/models/subscription.py:11-37](file://backend/app/models/subscription.py#L11-L37)
- [backend/alembic/versions/488d0bd5ab01_initial_migration.py:96-111](file://backend/alembic/versions/488d0bd5ab01_initial_migration.py#L96-L111)

### 模型关系与级联策略
- 外键约束
  - 所有子表均设置外键指向父表主键，并在删除时采用 CASCADE，确保数据一致性。
- 级联删除孤儿对象
  - User 的 queries、subscriptions；Query 的 citation_records、query_tasks 均配置了"all, delete-orphan"，保证删除父对象时自动清理其子对象。
- 索引策略
  - 查询高频字段（如 user_id、status、next_query_at、queried_at、platform）建立索引，提升查询性能。

**章节来源**
- [backend/app/models/user.py:35-40](file://backend/app/models/user.py#L35-L40)
- [backend/app/models/query.py:43-48](file://backend/app/models/query.py#L43-L48)
- [backend/app/models/query_task.py:36-38](file://backend/app/models/query_task.py#L36-L38)
- [backend/app/models/citation_record.py:37-41](file://backend/app/models/citation_record.py#L37-L41)

### 序列化、反序列化与数据验证
- Pydantic 验证
  - 查询创建/更新请求体包含平台集合、频率、状态等字段的严格校验，非法值会抛出异常。
- ORM 到 API 的转换
  - 响应模型启用 from_attributes，允许直接将 ORM 对象转为 JSON 响应。
- API 层集成
  - Queries API 将请求体绑定到 Pydantic 模型，调用服务层进行业务处理，再返回 ORM 对象或 Pydantic 响应模型。

**章节来源**
- [backend/app/schemas/query.py:11-94](file://backend/app/schemas/query.py#L11-L94)
- [backend/app/schemas/citation.py:7-50](file://backend/app/schemas/citation.py#L7-L50)
- [backend/app/api/queries.py:1-86](file://backend/app/api/queries.py#L1-L86)

### 生命周期管理与事件钩子
- 时间戳管理
  - 所有模型的 created_at/updated_at 使用 server_default/onupdate 注入数据库侧时间戳，减少应用层负担。
- 任务状态流转
  - QueryTask 的状态从 pending 到 started 再到 completed 或 failed，配合 scheduled_at/started_at/completed_at 字段记录生命周期节点。
- 查询调度
  - 服务层根据频率计算 next_query_at，便于定时任务调度。

**章节来源**
- [backend/app/models/user.py:25-33](file://backend/app/models/user.py#L25-L33)
- [backend/app/models/query.py:32-40](file://backend/app/models/query.py#L32-L40)
- [backend/app/models/query_task.py:27-32](file://backend/app/models/query_task.py#L27-L32)
- [backend/app/services/query.py:62-77](file://backend/app/services/query.py#L62-L77)

### 使用示例与最佳实践
- 创建查询
  - 步骤：校验用户配额 -> 计算 next_query_at -> 构造 Query -> 提交事务 -> 刷新对象。
  - 参考路径：[创建查询服务:45-81](file://backend/app/services/query.py#L45-L81)
- 更新查询
  - 步骤：读取查询 -> 排除未设置字段 -> 若更新频率则重算 next_query_at -> 提交事务 -> 刷新对象。
  - 参考路径：[更新查询服务:84-113](file://backend/app/services/query.py#L84-L113)
- 删除查询
  - 步骤：按用户与查询 ID 定位 -> 删除 -> 提交事务。
  - 参考路径：[删除查询服务:116-129](file://backend/app/services/query.py#L116-L129)
- API 调用
  - GET /queries -> 返回 QueryListResponse
  - POST /queries -> 返回 QueryResponse
  - GET /queries/{query_id} -> 返回 QueryResponse
  - PUT /queries/{query_id} -> 返回 QueryResponse
  - DELETE /queries/{query_id} -> 204 No Content
  - 参考路径：[查询 API:15-86](file://backend/app/api/queries.py#L15-L86)

**章节来源**
- [backend/app/services/query.py:45-129](file://backend/app/services/query.py#L45-L129)
- [backend/app/api/queries.py:15-86](file://backend/app/api/queries.py#L15-L86)

## 依赖关系分析
- 数据库引擎与会话
  - 使用异步引擎与 async_sessionmaker，Base 作为 declarative_base 基类。
- 模型导出
  - models/__init__.py 统一导出所有模型，便于上层模块按需导入。
- 迁移脚本
  - Alembic 初始迁移脚本定义了表结构、索引与外键约束，与模型定义保持一致。

```mermaid
graph LR
CFG["配置<br/>DATABASE_URL"] --> ENG["异步引擎"]
ENG --> SESS["AsyncSessionLocal"]
SESS --> BASE["declarative_base"]
BASE --> MODELS["模型类"]
MODELS --> DB["PostgreSQL"]
```

**图表来源**
- [backend/app/database.py:1-29](file://backend/app/database.py#L1-L29)
- [backend/app/config.py](file://backend/app/config.py#L7)

**章节来源**
- [backend/app/database.py:1-29](file://backend/app/database.py#L1-L29)
- [backend/app/config.py:1-23](file://backend/app/config.py#L1-L23)

## 性能考量
- 索引设计
  - queries：user_id、status、next_query_at
  - citation_records：query_id、queried_at、platform
  - query_tasks：status
  - 建议：基于实际查询模式持续评估与补充索引。
- 查询优化
  - 使用 select + order_by + offset + limit 实现分页与排序。
  - 使用 func.count 统计总数，避免不必要的全量加载。
- 异步 I/O
  - 使用 asyncpg 与 SQLAlchemy 异步引擎，降低并发场景下的阻塞风险。
- 缓存策略
  - 对热点查询结果（如用户配额、订阅状态）可引入 Redis 缓存，减少数据库压力。

## 故障排查指南
- 查询配额超限
  - 现象：创建查询时报错"PermissionError: Query limit exceeded"
  - 处理：检查用户 max_queries 与当前查询数量，必要时升级计划或清理历史查询。
  - 参考路径：[创建查询服务:45-81](file://backend/app/services/query.py#L45-L81)
- 查询不存在
  - 现象：GET/PUT/DELETE 查询返回 404
  - 处理：确认 query_id 与当前用户匹配；检查软删除/级联删除是否生效。
  - 参考路径：[查询 API:42-85](file://backend/app/api/queries.py#L42-L85)
- 平台/频率参数非法
  - 现象：Pydantic 校验失败
  - 处理：核对平台集合是否在允许集合内，频率是否为 daily/weekly。
  - 参考路径：[查询请求体校验:18-33](file://backend/app/schemas/query.py#L18-L33)
- 任务状态异常
  - 现象：任务长时间 pending 或失败
  - 处理：检查 error_message 字段；核对平台可用性与 API 密钥配置。
  - 参考路径：[查询任务模型:11-39](file://backend/app/models/query_task.py#L11-L39)

**章节来源**
- [backend/app/services/query.py:45-129](file://backend/app/services/query.py#L45-L129)
- [backend/app/api/queries.py:42-85](file://backend/app/api/queries.py#L42-L85)
- [backend/app/schemas/query.py:18-33](file://backend/app/schemas/query.py#L18-L33)
- [backend/app/models/query_task.py:11-39](file://backend/app/models/query_task.py#L11-L39)

## 结论
GEO 项目的数据模型围绕用户、查询、任务、引用记录与订阅五大实体构建，采用异步 SQLAlchemy ORM 与 Alembic 迁移管理，配合 Pydantic 的输入输出验证，形成清晰的领域模型与 API 边界。通过合理的外键约束、级联策略与索引设计，既保证了数据一致性，也兼顾了查询性能。建议在生产环境中持续监控查询性能与缓存命中率，并完善事件钩子与审计日志以增强可观测性。

## 附录
- 数据库连接配置
  - DATABASE_URL：PostgreSQL 异步连接字符串
  - 参考路径：[配置](file://backend/app/config.py#L12)
- 模型导出入口
  - models/__init__.py 统一导出所有模型
  - 参考路径：[模型导出:1-14](file://backend/app/models/__init__.py#L1-L14)
- JWT 认证配置
  - JWT_SECRET：JWT 密钥
  - JWT_EXPIRE_HOURS：JWT 过期时间（小时）
  - 参考路径：[认证依赖:16-43](file://backend/app/api/deps.py#L16-L43)