## 1. 数据契约 - [x] 1.1 新增成对迁移 `migrations/000222_add_exchange_migration_status.up.sql` / `.down.sql`,为 `tb_exchange_order` 添加迁移状态和失败原因字段:ADD COLUMN(带默认值)→ 按既有 `migrate_data`、`migration_completed` 回填历史记录 → 添加 `chk_exchange_order_migration_status` 约束;实施时按 `migrations/` 根目录最大编号复核,保持 up/down 成对。 - [x] 1.2 在换货模型和常量中定义四种迁移状态及中文名称,保留现有布尔字段的兼容语义。 - [x] 1.3 扩展换货列表、详情 DTO 及两个读侧投影,返回迁移状态、中文名称及仅失败时的安全失败原因。 - [x] 1.4 在 `internal/exporter/exchange_scene.go` 最小改动表头、SQL Select、行结构与行拼接四处,在「状态」列之后新增一列迁移状态并使用中文名称,不导出失败原因。 ## 2. 换货完成与恢复 - [x] 2.1 在创建、发货和成功完成的写路径维护不迁移、待迁移和已迁移状态,并在成功后清除失败原因。 - [x] 2.2 保持完整换货和业务数据迁移在同一 GORM 事务;迁移失败时回滚全部业务修改,再在同一个回滚后短事务内保存物流换货单的失败状态、安全化失败原因和审计事实。该短事务不与已回滚的主事务共用连接或事务,条件为「换货单仍处于可确认完成状态」,`RowsAffected` 为 0 时跳过状态写入但仍写审计;失败原因只拼接 AppError 链上的中文 `Message`、丢弃非 AppError 的 `cause`、按 rune 截断至不超过 500 字符,基础设施错误降级为固定安全摘要;符合 `ENG-TX-001` 例外条件。 - [x] 2.3 限制迁移失败的物流换货重试仅由超级管理员或平台用户发起,授权以 `FOR UPDATE` 锁定后读到的 `migration_status` 为准(事务外预读只做快速拒绝);重试须重新执行全套迁移、禁止只重试子项,并防止并发重复完成;非 failed 单沿用既有完成门禁,不削弱代理既有权限。 - [x] 2.4 保持直接换货失败时整体回滚且不持久化失败换货单,确认迁移范围不包含手机号—资产关联。 ## 3. 文档与验证 - [x] 3.1 更新换货接口 OpenAPI 描述并运行 `go run cmd/gendocs/main.go`,核对状态枚举及失败原因的响应契约。 - [x] 3.2 在隔离数据库按 `scripts/migrate.sh` 使用显式 `DB_*` 参数验证新迁移 up/down/up、历史状态映射及回滚后的 Schema。 - [x] 3.3 运行 `gofmt -w`(变更 Go 文件)、`go build ./cmd/api ./cmd/worker`、`openspec validate add-exchange-data-migration-status --strict` 和 `openspec doctor --json`;自动化测试按项目决策为 N/A。