Files
2026-moving-helper/docs/design/step-1-alembic-foundation.md
T
tliu93 c42cc2ddb6 docs: add LLM integration design and three-step implementation plan
Add docs/: a bilingual repository brief, plus docs/design/ with the high-level design (Alembic migration foundation, LLM integration, basic AI search) and a self-contained per-step implementation plan (step 1-3).
2026-06-01 13:10:59 +02:00

6.7 KiB
Raw Blame History

步骤 1 · Alembic 迁移地基 / Step 1 · Migration Foundation

可独立执行 / Self-contained. 完整背景见设计文档 llm-integration-design.md §3;跨步骤约定见 implementation-plan.md前置 / Prerequisite 无(第一步)/ none. 产出 / Output 一个可独立合入的 PR不改任何业务 schema。A mergeable PR with zero business-schema change.


目标 / Goal

引入 Alembic 并安全接管现有生产库,schema 一点不改,所有现有测试保持绿。 Introduce Alembic and safely adopt the existing prod DB, with zero schema change; all existing tests stay green.


必要背景 / Essential Context(仅凭本文件即可执行 / enough to execute from this file

  • 当前没有 Alembic。 唯一的"迁移"是 app/db.py::_sync_sqlite_image_columns()(启动时缺图片列就 ALTER TABLE ADD COLUMN)。 No Alembic today; the only "migration" is the hand-rolled image-column sync in app/db.py.
  • app/db.py::init_db() 在 FastAPI lifespan 启动时被 create_app() 调用,现在执行 Base.metadata.create_all() + _sync_sqlite_image_columns()。相关符号:BaseengineSessionLocalconfigure_database()init_db() runs at lifespan startup and currently does create_all() + the image-column sync.
  • tests/conftest.pyclient fixtureconfigure_database(tmp_url)create_app()(触发 init_db)。每个测试用临时 SQLite,互不污染。
  • models 在 app/models.pyBox / Item / SubItem 三张表;每张含 image_blob(BLOB) / image_mime_type / image_width / image_height,以及 created_at / updated_at
  • DB URL 来自 app/config.py::get_settings().database_url(默认 sqlite:///./data/app.db)。
  • 生产库是当年 create_all 建的、已装上千件数据、没有 alembic_version

铁律 / The Invariant(不可违背 / non-negotiable

  • 所有数据库最终收敛到同一个 head。All DBs converge to the same head.
  • V1 baseline 必须严格等于"今天的真实 schema"(三张表 + 现有图片列 + 索引),不多一列。新东西放后续 revision。 The V1 baseline must equal today's actual schema exactly — nothing more.
  • 老库:stamp V1(只写版本号,不建表、不碰数据)→ upgrade head。 Existing DB: stamp V1 (writes only the version row, no DDL, no data change) → upgrade head.
  • 新库:跑 V1(真正建表)→ upgrade head。 Fresh DB: run V1 (creates tables) → upgrade head.

任务 / Tasks

  • requirements.txt 增加 alembic(钉一个明确版本 / pin a version)。
  • 初始化 Alembic 工程:alembic.ini + migrations/(含 env.pyversions/)。
  • 配置 migrations/env.py
    • target_metadata = app.db.Base.metadata(确保导入 app.models 以注册三张表)。
    • sqlalchemy.url app.config.get_settings().database_url 动态读取,不写死在 alembic.ini
    • 对 SQLite 设 render_as_batch=True(为未来改列/删列预留 batch 能力)。
  • 生成 V1 baseline 迁移=当前 models 的完整建表(boxes/items/subitems,含图片列与索引)。做法:对空库 --autogenerate。 Author V1 by autogenerating against an empty DB.
  • 验证 baseline:对一份生产库副本alembic check,确认无差异(印证可安全 stamp;SQLite 偶有类型亲和/索引命名假差异,人眼复核)。 Verify with alembic check against a copy of the prod DB → expect no diff.
  • 新增封装 app/migrate.py,导出 run_migrations(database_url: str)
    • 编程方式构造 Alembic Configscript_location 指向打包进镜像的 migrations/sqlalchemy.url = 传入 URL)。
    • 用 SQLAlchemy inspector 实现自动认领:
      • alembic_versioncommand.upgrade(cfg, "head")
      • alembic_version 但有 boxes 表 → command.stamp(cfg, "<V1 rev>")command.upgrade(cfg, "head")
      • 全空 → command.upgrade(cfg, "head")
  • app/db.py::init_db():改为调 run_migrations(resolved_url)删除 _sync_sqlite_image_columns()Alembic 接管后冗余)。保留 configure_database() / engine 装配逻辑。 init_db() calls run_migrations(...); remove _sync_sqlite_image_columns().
  • Dockerfile:加 COPY alembic.ini .COPY migrations ./migrations(否则容器内无迁移脚本)。
  • CI(可选 / optional):.github/workflows/test.yml 加一步 alembic check,防止 model 与迁移漂移。

涉及文件 / Files

requirements.txtalembic.ini(新)、migrations/**(新)、app/migrate.py(新)、app/db.pyDockerfiletests/、(可选).github/workflows/test.yml


测试 / Tests

  • 现有 ~83 个测试全绿(它们经 init_db 现在改走迁移建表)。 All existing ~83 tests pass (schema now built via migrations through init_db).
  • 新增认领老库用例:构造一个"有 boxes 数据、无 alembic_version"的库(可先用 create_all 造),调 run_migrations 后断言:数据保留、alembic_version 到达 head、未重复建表报错。 New adoption test: a "has boxes data, no alembic_version" DB → after run_migrations, data preserved and version at head.
  • 新增全新库用例:空 URL → run_migrations 后三张表存在、版本到 head

验收 / Acceptance

  • 全新库:从 V1 建表,应用正常起。Fresh DB builds from V1; app starts.
  • 模拟老库:自动 stamp + upgrade数据无损。Existing-like DB auto-adopts; data intact.
  • 全部测试绿;schema 与本步骤前逐列一致(本步不改业务 schema)。 All tests green; schema identical to before (no business-schema change).

风险与缓解 / Risks & Mitigations

  • baseline 与现状有偏差 → stamp 失真。 缓解:alembic check 对生产副本校验 + 人眼复核 SQLite 假差异。 Baseline drift → alembic check against a prod copy + manual eyeball.
  • 容器内找不到迁移脚本。 缓解:确认 DockerfileCOPY alembic.inimigrations/script_location 用绝对/相对镜像 WORKDIR(/app) 正确解析。 Migrations missing in image → ensure they're COPY-ed and script_location resolves under /app.

相关约定 / Conventions(详见 implementation-plan.md

  • 不主动 push/commit,除非业主要求。Don't push/commit unless asked.
  • 实现与设计若有偏差 → 回写设计文档 §3 与仓库简报 ../repository-brief.md §10。