geo/.qoder/repowiki/zh/content/数据库设计/数据模型.md

443 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 数据模型
<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 数据模型,覆盖模型类定义、字段映射与关系配置,解释模型间的关系映射(含级联与外键约束)、序列化/反序列化与数据验证机制、生命周期管理与事件钩子、以及使用示例与最佳实践。目标是帮助开发者快速理解并正确使用数据模型,确保在查询、任务调度、引用记录与订阅管理等场景中保持一致性与可维护性。
## 项目结构
后端采用异步 SQLAlchemySQLAlchemy 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、subscriptionsQuery 的 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_sessionmakerBase 作为 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)
## 性能考量
- 索引设计
- queriesuser_id、status、next_query_at
- citation_recordsquery_id、queried_at、platform
- query_tasksstatus
- 建议:基于实际查询模式持续评估与补充索引。
- 查询优化
- 使用 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_URLPostgreSQL 异步连接字符串
- 参考路径:[配置](file://backend/app/config.py#L12)
- 模型导出入口
- models/__init__.py 统一导出所有模型
- 参考路径:[模型导出:1-14](file://backend/app/models/__init__.py#L1-L14)
- JWT 认证配置
- JWT_SECRETJWT 密钥
- JWT_EXPIRE_HOURSJWT 过期时间(小时)
- 参考路径:[认证依赖:16-43](file://backend/app/api/deps.py#L16-L43)