c42cc2ddb6
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).
6.7 KiB
6.7 KiB
步骤 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 inapp/db.py. app/db.py::init_db()在 FastAPI lifespan 启动时被create_app()调用,现在执行Base.metadata.create_all()+_sync_sqlite_image_columns()。相关符号:Base、engine、SessionLocal、configure_database()。init_db()runs at lifespan startup and currently doescreate_all()+ the image-column sync.tests/conftest.py的clientfixture:configure_database(tmp_url)→create_app()(触发init_db)。每个测试用临时 SQLite,互不污染。- models 在
app/models.py:Box/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 samehead. - 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: runV1(creates tables) →upgrade head.
任务 / Tasks
requirements.txt增加alembic(钉一个明确版本 / pin a version)。- 初始化 Alembic 工程:
alembic.ini+migrations/(含env.py、versions/)。 - 配置
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 withalembic checkagainst a copy of the prod DB → expect no diff. - 新增封装
app/migrate.py,导出run_migrations(database_url: str):- 编程方式构造 Alembic
Config(script_location指向打包进镜像的migrations/,sqlalchemy.url= 传入 URL)。 - 用 SQLAlchemy inspector 实现自动认领:
- 有
alembic_version→command.upgrade(cfg, "head") - 无
alembic_version但有boxes表 →command.stamp(cfg, "<V1 rev>")→command.upgrade(cfg, "head") - 全空 →
command.upgrade(cfg, "head")
- 有
- 编程方式构造 Alembic
- 改
app/db.py::init_db():改为调run_migrations(resolved_url),删除_sync_sqlite_image_columns()(Alembic 接管后冗余)。保留configure_database()/ engine 装配逻辑。init_db()callsrun_migrations(...); remove_sync_sqlite_image_columns(). Dockerfile:加COPY alembic.ini .与COPY migrations ./migrations(否则容器内无迁移脚本)。- CI(可选 / optional):
.github/workflows/test.yml加一步alembic check,防止 model 与迁移漂移。
涉及文件 / Files
requirements.txt、alembic.ini(新)、migrations/**(新)、app/migrate.py(新)、app/db.py、Dockerfile、tests/、(可选).github/workflows/test.yml。
测试 / Tests
- 现有 ~83 个测试全绿(它们经
init_db现在改走迁移建表)。 All existing ~83 tests pass (schema now built via migrations throughinit_db). - 新增认领老库用例:构造一个"有
boxes数据、无alembic_version"的库(可先用create_all造),调run_migrations后断言:数据保留、alembic_version到达head、未重复建表报错。 New adoption test: a "hasboxesdata, noalembic_version" DB → afterrun_migrations, data preserved and version athead. - 新增全新库用例:空 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 checkagainst a prod copy + manual eyeball. - 容器内找不到迁移脚本。 缓解:确认
Dockerfile已COPYalembic.ini与migrations/;script_location用绝对/相对镜像 WORKDIR(/app) 正确解析。 Migrations missing in image → ensure they'reCOPY-ed andscript_locationresolves under/app.
相关约定 / Conventions(详见 implementation-plan.md)
- 不主动 push/commit,除非业主要求。Don't push/commit unless asked.
- 实现与设计若有偏差 → 回写设计文档 §3 与仓库简报
../repository-brief.md§10。