Files
2026-02-02 11:22:35 +08:00

8.1 KiB
Raw Permalink Blame History

DB Design & Migrations数据库设计与迁移Plan

对应规范:spec_kit/Personalized Reco/modules/db-design/spec.md

前置确认(已对齐):

  • content_idMySQL 自增主键;文案微调时 content_id 不变(更新同一条记录)。
  • need_suitability/context_suitabilityJSON 存储。
  • risk_flags:选择更强扩展性的方案(本计划采用 关联表,利于索引与过滤)。
  • 安全池L3方式 Ais_safe_pool)。
  • 迁移:使用 Alembic,目录放 server/alembic/dev/pro 两套库均可运行同一套迁移。
  • 字符集:统一 utf8mb4

1. 目标与交付物

1.1 目标

  • 在“库为空”的前提下,落地推荐系统最小可用的数据模型。
  • 保证后续推荐查询可实现:按画像条件召回、按 ID 批量查、按风险标记过滤、支持安全池兜底。

1.2 交付物

  • server/alembic/Alembic 初始化目录、alembic.ini(或等价配置)、迁移脚本。
  • SQLAlchemy ORM 模型(建议放 server/app/db/models/)。
  • 初始迁移:创建 contentscontent_profilescontent_risk_flags(以及必要索引)。

2. 技术决策V1

2.1 表设计原则

  • 分离主体与画像contents 存文本与来源字段;content_profiles 存画像字段(便于未来画像重算/回填)。
  • JSON 存 suitabilitycontext_suitabilityneed_suitability 用 JSON保持结构与规则文档一致。
  • risk_flags 关联表:用 content_risk_flags(content_id, flag),便于:
    • 快速 Hard Filterblock_health_medical 等)
    • 索引与统计(按 flag 计数)
    • 兼容旧 flag 映射(在写入/读取层)
  • emotion_score 的 general 表示:用 NULL 表示 general与规则文档“可为 general”语义等价
  • personalization_power:存为 TINYINT0/5/10DECIMAL(2,1)0/0.5/1。本计划推荐 TINYINT(更易索引/更省空间),应用层做映射:
    • 0 → 0.0
    • 5 → 0.5
    • 10 → 1.0

2.2 字符集与排序规则

  • 数据库与表:utf8mb4
  • collation建议 utf8mb4_0900_ai_ciMySQL 8 默认更常见;若环境不同以实际为准,但必须 utf8mb4

3. 表结构V1 方案)

以下为“建议 schema”。实际字段名可调整但语义必须严格对齐 句子文案打分规则

3.1 contents(文案主体)

  • content_id BIGINT UNSIGNED PK AUTO_INCREMENT
  • text TEXT NOT NULL
  • author_id VARCHAR(64) NULL
  • template_id VARCHAR(64) NULL
  • created_at DATETIME NOT NULL
  • updated_at DATETIME NOT NULL

索引建议:

  • idx_contents_author_id(author_id)
  • idx_contents_template_id(template_id)

3.2 content_profiles(内容画像)

  • content_id BIGINT UNSIGNED PKFK → contents.content_idON DELETE CASCADE
  • stage ENUM('general','expecting','parenting','unknown') NOT NULL DEFAULT 'general'
  • emotion_score DECIMAL(3,2) NULL
    • 约定NULL 表示 general
  • context_suitability_json JSON NOT NULL
  • need_suitability_json JSON NOT NULL
  • personalization_power TINYINT UNSIGNED NOT NULL DEFAULT 0
    • 约定:只允许 0/5/10
  • review_confidence DECIMAL(3,2) NULL
    • 约定NULL 由推荐模块按 0.7 兜底(对齐规则文档)
  • is_safe_pool BOOLEAN NOT NULL DEFAULT FALSE
  • updated_at DATETIME NOT NULL

索引建议:

  • idx_profiles_stage(stage)
  • idx_profiles_personalization_power(personalization_power)
  • idx_profiles_is_safe_pool(is_safe_pool)

说明suitability 放 JSON 后V1 可以先不做 JSON 路径索引;当候选量上来后再加“生成列/函数索引”做加速(见 6.2)。

3.3 content_risk_flags(风险标记,关联表)

  • id BIGINT UNSIGNED PK AUTO_INCREMENT
  • content_id BIGINT UNSIGNED NOT NULLFK → contents.content_idON DELETE CASCADE
  • flag VARCHAR(64) NOT NULL
  • created_at DATETIME NOT NULL

约束与索引:

  • UNIQUEuniq_content_flag(content_id, flag)(同一 content 不重复插同 flag
  • 索引:idx_flag(flag)(用于 Hard Filter 与统计)
  • 索引:idx_content_id(content_id)(用于按内容批量取 flags

命名约束应用层强制DB 可选):

  • flag 必须以 unsafe_for_ / block_ / soft_ 开头(严格对齐规则文档)

4. Alembic 迁移落地步骤

4.1 依赖与目录

  • 后端依赖:alembic(加入 server/requirements.txt,版本随项目统一管理)
  • 目录:server/alembic/(包含 env.pyversions/
  • 连接串:复用现有 DATABASE_URLmysql+aiomysql://...

4.2 初始化与生成迁移(一次性)

  • alembic init alembic(在 server/ 下)
  • 配置 env.py
    • app/core/config.py 读取 DATABASE_URL
    • 引入 ORM Base 与 models启用 autogenerate
  • 创建初始迁移:
    • alembic revision --autogenerate -m "init content tables"
    • alembic upgrade head

4.3 dev/pro 一致性

  • 迁移脚本保持同一套;通过不同环境的 DATABASE_URL 指向 mindfulness_devmindfulness

5. 入库流程(写入契约)

5.1 一条文案最小入库数据

必须字段V1 最小可用):

  • text
  • content_profiles.stage
  • content_profiles.emotion_score(可为 NULL 表示 general
  • content_profiles.context_suitability_json(必须包含 5 个 keyfamily/work/relationship/friends/health值为 0/0.5/1
  • content_profiles.need_suitability_json(必须包含 5 个 keyemotional_support/parenting_pressure/self_worth/anxiety_relief/rest_balance值为 0/0.5/1
  • content_profiles.personalization_power0/5/10
  • content_risk_flags(可为空集合,但若存在必须按命名规范)

强烈建议字段:

  • author_idtemplate_id
  • review_confidence
  • is_safe_pool(若要参与 L3 安全池)

5.2 写入策略

  • 创建文案时:
    • 先写 contents 得到 content_id(自增)
    • 再写 content_profiles(同 content_id
    • 再批量写 content_risk_flags
  • 文案微调时content_id 不变):
    • 更新 contents.textupdated_at
    • 同步更新 content_profiles(若画像变更)
    • risk_flags 做“全量覆盖”或“差量更新”plan 实现阶段定)

6. 查询与性能规划

6.1 V1 查询策略(先可用)

  • 候选召回:
    • 先按 stagepersonalization_poweris_safe_pool 等可索引字段进行粗过滤
    • 再在应用层结合 suitability JSON 与 risk_flags 做精过滤/打分
  • Hard Filter
    • 通过 content_risk_flags join 或子查询排除指定 flagsblock_health_medical
  • 批量查:
    • content_id IN (...) join content_profiles + left join content_risk_flags

6.2 V1.1 性能增强(候选量上来后再做)

当候选池变大、应用层过滤成本上升时,优先做两类增强:

  • 生成列/函数索引:为常用召回维度(例如 need/context 的某些 key创建 generated columns从 JSON_EXTRACT 取值并映射到 TINYINT再加索引。
  • 风险 flag 位图/派生列:对 block_health_medical 等强规则增加派生布尔列(或维护冗余表),降低 join 成本。

7. 测试与验收DB 子模块)

7.1 迁移验收

  • 在全新库执行 alembic upgrade head 成功。
  • 执行 downgrade(若实现)可回滚(至少在开发环境可用)。

7.2 数据契约验收

插入一条最小文案记录后,能够查询并组装出推荐模块所需的 ContentProfile 字段集合:

  • content_id/text/stage/emotion_score/context_suitability/need_suitability/personalization_power/risk_flags

7.3 规则口径验收(写入侧)

  • 写入 risk_flags 时,若出现旧 flagblock_stage_unknown
    • 写入层需在入库前映射为新命名(或拒绝写入并提示)
    • 推荐侧读取层不得再出现旧 flag 名称

8. 与其他子模块的接口约定

  • Content Repository 只依赖本模块提供的表与字段语义,不依赖具体迁移实现细节。
  • 推荐引擎/打分模块对 review_confidence 的缺省值假设0.7)在 DB 缺失时依然成立。