--- id: T-209 title: 把核心 schema / 状态机迁移为单趟模型 phase: 2 deps: [T-004, T-111] status: DONE created: 2026-08-04 vikunja_task_id: 29 context_ref: da540bf work_branch: task/t-209-single-pass-schema needs_device: false needs_human_review: false write_paths: - docs/tasks/T-209.md - admin/migrations/** - admin/internal/migrations/** - admin/internal/domain/** --- ## 问题 / 背景 T-111 已把业务契约改为管理员点击“开始采购”即授权桌面端在同一趟创建待付款订单;现有 SQLite v1 与领域模型仍是旧两趟结构,包含 spec_trials、authorized_unit_price 及 WAITING_CONFIRMATION/PENDING_RETRIAL/AUTHORIZED/RUNNING 等旧状态。T-203 不能在旧结构上继续实现。 ## 关联需求与交互 T-111;F-004、F-005、F-008、F-017、F-018;docs/04-architecture.md 第四、五节;不包含页面选择器或真机操作。 ## 方案 1. 保留 00001_core_data.sql 作为不可变迁移历史,新增 00002 单趟模型迁移;迁移只允许保留既有 MANUAL+DRAFT 任务,发现任一旧 spec_trials/authorization/submission 数据或非 DRAFT 任务即整体失败并回滚,不能猜测映射。 2. 重建 tasks 的状态约束为 DRAFT、PENDING、CLAIMED、ORDERING、NEEDS_MANUAL、WAITING_PAYMENT、RECONCILIATION_REQUIRED、SUCCEEDED、FAILED、CANCELED;保留 DRAFT 内容、版本与时间。 3. 删除旧 spec_trials 结构;重建 order_authorizations,锁定 task_version/start_key/goods_id/颜色/尺码/数量/total_price_cap,状态仅 ACTIVE/CLAIMED/FENCED/CONSUMED/EXPIRED/ABANDONED,不保存 observed/authorized unit price。 4. 新增 purchase_attempts 保存 claim generation、三闸门摘要和固定 failure code;重建 order_submissions,原子关联同一 task/authorization/attempt,保存 gate1、gate2、quantity、confirm amount,并限制一份授权/attempt 最多一条围栏。 5. 同步 Go 领域实体和 fail-closed 状态机;围栏前允许安全失败/重置,FENCED 后授权只能 CONSUMED,提交结果不明只能调和同一记录,任何未列出转移拒绝。 6. Down 迁移同样只在没有新业务执行数据且任务仍可无损回退时执行,否则失败并保持 v2;覆盖 up/down、幂等、DRAFT 保留、未知旧数据回滚、外键/唯一约束、金额 TEXT、状态转移与旧标识符消失测试。 ## 验收要点 - 新 migration 不修改 00001;从空 v1 和仅含 DRAFT 的 v1 升级成功,DRAFT 字段逐项不变。 - 任一旧执行/授权/提交记录或非 DRAFT 状态都使升级失败,版本和原数据保持 v1;没有半迁移。 - 新 schema 无 spec_trials、authorized_unit_price、spec_trial_id、command_id、dry_run_id;包含 purchase_attempts 与架构规定的关系、唯一性、金额字符串和状态 CHECK。 - 领域模型不再暴露旧两趟状态;未知状态或未列出的转移全部失败。第一趟试选/下单函数均不在本任务范围。 - go test ./...、go test -race ./...、go vet ./...、go build ./...、完整 init.ps1、上下文校验和 diff-check 通过。 ## 执行记录 (暂无) ## 边界 - 本任务只迁移数据结构和纯领域状态机,不实现管理员“开始采购”HTTP/usecase、设备领取、租约、 attempt 写入 API、截图上传、提交围栏 API、页面自动化或任何真机动作;这些能力仍分别属于 T-203、T-205、T-208 与 Phase 3/4 任务。 - 不修改已经发布的 `00001_core_data.sql`;只能追加 `00002`。Up/Down 都必须置于单个事务,前置 检查失败时版本、schema 和数据原样保留,不能删除、转换或猜测任何旧执行记录。 - Up 只允许空库或仅含可无损保留的 `MANUAL + DRAFT` 任务。任一 `spec_trials`、旧 `order_authorizations`、旧 `order_submissions` 数据,或任一非 DRAFT / 非 MANUAL 任务均拒绝升级。 - Down 只允许没有授权、attempt、submission 且全部任务都能无损回到 v1 DRAFT 的 v2 数据库;否则 拒绝回退。迁移测试不得为通过而临时关闭外键后漏恢复,也不得留下临时表或 guard 表。 - 金额继续只用严格正十进制 `TEXT`;不得引入浮点数。新授权只锁定 `total_price_cap`,不得重新加入 `authorized_unit_price`、观察价格或 `spec_trial_id`。 - `FENCED` 授权不得回到可领取、可过期或可放弃状态;围栏后的提交只能记录明确提交或进入同一记录 调和,不能提供重试、释放或第二次点击的状态转移。 - 本任务不实现、不引用点击“提交订单”的函数,更不涉及支付、免密支付、先用后付或任何扣款动作。