feat(换货): AUG26-005 换货业务数据迁移状态与失败恢复
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 11m42s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 11m42s
- 新增成对迁移 000222:tb_exchange_order 增加非空 migration_status 与 migration_failure_reason,按既有 migrate_data/migration_completed 回填历史, 并加四值 CHECK 约束,不新增索引 - 模型与常量定义四种迁移状态及中文名称,保留既有布尔字段兼容语义 - 物流换货创建恒 not_migrated,发货按请求落 pending/not_migrated, 完成成功写 migrated/not_migrated 并清空失败原因、同步兼容字段 - 直接换货创建即完成,任一步失败整体回滚,不持久化换货单、不产生 failed - 迁移失败回滚全部业务修改后,在独立短事务内条件更新 failed 与安全失败原因 并写失败审计,RowsAffected 为 0 时跳过状态写入但仍写审计 - failed 物流单重试仅限超级管理员或平台用户,授权以锁内 FOR UPDATE 判定为准, 重试从钱包余额起整表重跑;非 failed 单沿用既有完成门禁 - 列表与详情返回迁移状态与中文名称,仅 failed 返回失败原因;既有三字段保持兼容 - 换货导出在「状态」列后新增中文「迁移状态」列,不导出失败原因 - 同步 order-refund-exchange 主 spec 与验证证据,归档本 Change - 登记 KNOWN-ISSUE-001:既有标签复制 OnConflict 未声明部分索引谓词(42P10), 旧资产带标签时迁移最后一步失败,待另立变更修复
This commit is contained in:
@@ -1,18 +0,0 @@
|
||||
## 1. 数据契约
|
||||
|
||||
- [ ] 1.1 新增一对当前根迁移,为 `tb_exchange_order` 添加迁移状态和失败原因字段,并按既有迁移布尔字段回填历史记录。
|
||||
- [ ] 1.2 在换货模型和常量中定义四种迁移状态及中文名称,保留现有布尔字段的兼容语义。
|
||||
- [ ] 1.3 扩展换货列表、详情 DTO 及两个读侧投影,返回迁移状态、中文名称及仅失败时的安全失败原因。
|
||||
|
||||
## 2. 换货完成与恢复
|
||||
|
||||
- [ ] 2.1 在创建、发货和成功完成的写路径维护不迁移、待迁移和已迁移状态,并在成功后清除失败原因。
|
||||
- [ ] 2.2 保持完整换货和业务数据迁移在同一 GORM 事务;迁移失败时回滚全部业务修改,再以条件短事务保存物流换货单的失败状态、经安全处理的失败原因和审计事实。
|
||||
- [ ] 2.3 限制迁移失败的物流换货重试仅由超级管理员或平台用户发起;重试须锁定换货单、重新执行全套迁移并防止并发重复完成。
|
||||
- [ ] 2.4 保持直接换货失败时整体回滚且不持久化失败换货单,确认迁移范围不包含手机号—资产关联。
|
||||
|
||||
## 3. 文档与验证
|
||||
|
||||
- [ ] 3.1 更新换货接口 OpenAPI 描述并运行 `go run cmd/gendocs/main.go`,核对状态枚举及失败原因的响应契约。
|
||||
- [ ] 3.2 在隔离数据库按 `scripts/migrate.sh` 使用显式 `DB_*` 参数验证新迁移 up/down/up、历史状态映射及回滚后的 Schema。
|
||||
- [ ] 3.3 运行 `gofmt -w`(变更 Go 文件)、`go build ./cmd/api ./cmd/worker`、`openspec validate add-exchange-data-migration-status --strict` 和 `openspec doctor --json`;自动化测试按项目决策为 N/A。
|
||||
@@ -14,7 +14,8 @@
|
||||
**Non-Goals:**
|
||||
- 不改变直接换货创建失败即整体回滚、无换货单留存的现有行为。
|
||||
- 不修改迁移项目、增加迁移明细表、迁移手机号—资产关联,或改变资产归属和个人客户—资产绑定的既有换货动作。
|
||||
- 不新增列表筛选、导出或路由。
|
||||
- 不新增列表筛选或路由;换货导出仅新增一列迁移状态,不导出失败原因。
|
||||
- 不提供迁移失败换货单的放弃或取消出口:`failed` 单只能通过重试成功离开,如需放弃能力须另立需求。
|
||||
|
||||
## Decisions
|
||||
|
||||
@@ -22,7 +23,9 @@
|
||||
|
||||
在 `tb_exchange_order` 新增非空 `migration_status` 和非空 `migration_failure_reason`。状态使用字符串 `not_migrated`、`pending`、`migrated`、`failed`,中文名称仅在应用层投影;失败原因最长 500 字符且为空表示无失败原因。
|
||||
|
||||
新字段表达完整结果,保留 `migrate_data`、`migration_completed` 和 `migration_balance` 供兼容客户端及既有业务使用。新建/发货时按是否选择迁移写入 `not_migrated` 或 `pending`;成功完成写入 `migrated` 并清空失败原因。
|
||||
新字段表达完整结果,保留 `migrate_data`、`migration_completed` 和 `migration_balance` 供兼容客户端及既有业务使用。物流换货创建即写 `not_migrated`(创建阶段不写 `migrate_data`);发货时按请求 `migrate_data` 写入 `pending` 或 `not_migrated`;成功完成写入 `migrated`、清空失败原因,并同步既有 `migration_completed` 与 `migration_balance`。
|
||||
|
||||
新增列带默认值,迁移按「ADD COLUMN(带默认值)→ 以既有字段回填 → 添加 `chk_exchange_order_migration_status` 约束校验四值」顺序执行;数据库层白名单与 `pkg/constants` 取值一致。
|
||||
|
||||
备选方案是在现有两个布尔字段上叠加前端规则。放弃原因是无法表达失败和失败原因,且容易把待迁移与失败混淆。
|
||||
|
||||
@@ -30,13 +33,15 @@
|
||||
|
||||
业务数据迁移仍与换货完成共享原有 GORM 事务。任何一步失败均回滚资产状态、归属、客户绑定、钱包、套餐和标签的本次修改,确保重试从完整且未部分迁移的事实开始。
|
||||
|
||||
外层识别到迁移失败后,另开短事务,以换货单仍处于可确认完成状态为条件更新 `migration_status=failed` 与经安全截断的失败原因,并写入对应失败审计。失败状态的持久化不得与已回滚的业务数据迁移共用事务。
|
||||
外层识别到迁移失败后,另开短事务,在同一个短事务内写入失败状态与失败审计;该短事务不得与已回滚的主事务共用连接或事务。条件更新为「换货单仍处于可确认完成状态」:`RowsAffected` 为 0 时跳过状态写入但仍写失败审计,避免并发确认下把已成功完成的换货单改回 `failed`。
|
||||
|
||||
失败原因按固定规则安全化:只拼接 AppError 链上的中文 `Message`,丢弃非 AppError 的底层 `cause`,按 rune 截断至不超过 500 字符;数据库等基础设施错误降级为固定安全摘要;业务类原因(例如旧资产钱包存在冻结余额)保留可见。禁止把数据库、外部服务或敏感载荷原文写入该字段。
|
||||
|
||||
备选方案是让迁移失败提交部分换货结果。放弃原因是会产生无法可靠补偿的钱包和套餐事实,且违背本期整套迁移原子执行的产品边界。
|
||||
|
||||
### 3. 只为失败的物流换货增加受限重试
|
||||
|
||||
保持既有确认完成入口。换货单为物流流程、业务状态仍为已发货待确认且迁移状态为 `failed` 时,仅超级管理员或平台用户可再次确认;用例在事务内重新锁定并验证状态,然后从钱包余额开始重新执行全部迁移。未失败的换货沿用现有可确认权限和状态门禁。
|
||||
保持既有确认完成入口。换货单为物流流程、业务状态仍为已发货待确认且迁移状态为 `failed` 时,仅超级管理员或平台用户可再次确认。授权以锁内判定为准:用例在 `FOR UPDATE` 锁定换货单后读取 `migration_status`;事务外预读只用于快速拒绝,不作为授权依据。授权通过后从钱包余额开始重新执行全部迁移。未失败的换货沿用现有可确认权限和状态门禁,代理既有完成权限不被削弱。
|
||||
|
||||
直接换货继续创建即完成;其迁移失败会回滚整笔创建,不留换货单或失败状态,避免为单一失败路径引入新的直接换货中间状态与重试接口。
|
||||
|
||||
@@ -50,34 +55,35 @@
|
||||
|
||||
### 数据投影与历史映射
|
||||
|
||||
- `tb_exchange_order` 新增 `migration_status varchar(20) NOT NULL`、`migration_failure_reason varchar(500) NOT NULL DEFAULT ''`;DTO 列表与详情新增 `migration_status`、`migration_status_name`,并仅在状态为 `failed` 时返回 `migration_failure_reason`。
|
||||
- `tb_exchange_order` 新增 `migration_status varchar(20) NOT NULL DEFAULT 'not_migrated'`、`migration_failure_reason varchar(500) NOT NULL DEFAULT ''`,并以 `chk_exchange_order_migration_status` 约束四值;DTO 列表与详情新增 `migration_status`、`migration_status_name`,并仅在状态为 `failed` 时返回 `migration_failure_reason`。
|
||||
- 上线迁移将 `migrate_data=false` 映射 `not_migrated`,`migrate_data=true AND migration_completed=true` 映射 `migrated`,其余已存在 `migrate_data=true` 映射 `pending`;不推断历史失败原因。
|
||||
|
||||
### 创建、发货与确认完成
|
||||
|
||||
- `POST /exchanges`:沿用现有创建入参和权限。物流单创建时按 `migrate_data` 初始化 `not_migrated` 或 `pending`;直接换货在同一创建事务中执行完成和可选迁移,任一步失败则整个创建回滚,不返回换货单或 `failed` 状态。
|
||||
- `POST /exchanges/:id/ship`:沿用既有物流状态机和发货字段;不改变迁移状态,选择迁移的单仍为 `pending`。
|
||||
- `POST /exchanges/:id/complete`:先在同一事务锁定换货单并验证既有“已发货待确认”状态和数据范围。`not_migrated` 只执行固有资产归属及个人客户绑定;`pending` 执行钱包余额、有效套餐使用、累计充值、资产标签的完整迁移及固有动作。成功时写 `migrated`、清空失败原因、写完成时间和既有成功审计。
|
||||
- `POST /exchanges`:沿用现有创建入参和权限。物流单创建时恒为 `not_migrated`(创建阶段不写 `migrate_data`,迁移意图在发货时确定);直接换货仍在同一创建事务内完成,任一步失败则整个创建回滚,不持久化换货单、不产生 `failed` 状态。
|
||||
- `POST /exchanges/:id/ship`:沿用既有物流状态机和发货字段;按请求 `migrate_data` 写入 `pending` 或 `not_migrated`,不改变既有物流状态机。
|
||||
- `POST /exchanges/:id/complete`:先在同一事务锁定换货单并验证既有“已发货待确认”状态和数据范围。`not_migrated` 只执行固有资产归属及个人客户绑定;`pending` 执行钱包余额、有效套餐使用、累计充值、资产标签的完整迁移及固有动作。成功时写 `migrated`、清空失败原因、同步 `migration_completed` 与 `migration_balance`,并写完成时间和既有成功审计。
|
||||
|
||||
### 失败与受限重试
|
||||
|
||||
- 当 `pending` 迁移任一步失败时,主事务必须回滚资产归属、个人客户绑定、钱包、套餐、累计字段、标签和完成状态;外层另开短事务,条件为换货单仍是可确认完成状态,写 `failed`、安全截断至 500 字符的失败原因及失败审计。
|
||||
- 对 `migration_status=failed` 的物流单,`POST /exchanges/:id/complete` 仅超级管理员或平台用户可重试;锁定后从钱包余额开始重跑全部迁移,禁止仅重试某一子项。非平台账号返回无权,非失败单沿用既有完成状态门禁,不将完成接口变成通用重复执行入口。
|
||||
- 当 `pending` 迁移任一步失败时,主事务必须回滚资产归属、个人客户绑定、钱包、套餐、累计字段、标签和完成状态;外层另开短事务,在同一短事务内写 `failed`、按上述规则安全化的失败原因与失败审计,条件为换货单仍是可确认完成状态;`RowsAffected` 为 0 时跳过状态写入但仍写审计。该短事务不与已回滚的主事务共用连接或事务,符合 `ENG-TX-001` 的例外条件(回滚后的 failed/denied 事实与审计使用独立短事务,且以业务单仍处于允许该失败事实的状态为条件更新)。
|
||||
- 对 `migration_status=failed` 的物流单,`POST /exchanges/:id/complete` 仅超级管理员或平台用户可重试;授权以 `FOR UPDATE` 锁定后读到的 `migration_status` 为准,事务外预读只做快速拒绝。授权通过后从钱包余额开始重跑全部迁移,禁止仅重试某一子项。非超级管理员、非平台账号返回无权,非失败单沿用既有完成状态门禁、不削弱代理既有完成权限,不将完成接口变成通用重复执行入口。
|
||||
- 手机号—资产关联永不在上述动作中读取、复制或删除;新资产后续按自身 H5 手机号绑定规则处理。
|
||||
|
||||
### 读取行为
|
||||
|
||||
- `GET /exchanges` 与 `GET /exchanges/:id` 沿用既有换货数据范围,返回新状态字段;旧 `migrate_data`、`migration_completed`、`migration_balance` 保持原响应兼容,但调用方不得再以其组合判断迁移结果。
|
||||
- 换货导出沿用既有导出数据范围,在「状态」列之后新增一列迁移状态并使用中文名称;导出不输出失败原因,导出列不参与任何迁移、完成或数据范围规则。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [失败原因可能包含底层敏感或不稳定信息] → 使用稳定错误的安全摘要并限制长度,禁止直接返回数据库、外部服务或敏感载荷。
|
||||
- [失败状态更新与失败审计二次事务异常] → 复用既有换货失败审计的次级故障记录方式;状态更新与成功必达审计同事务,更新失败时返回原失败并保留诊断。
|
||||
- [失败原因可能包含底层敏感或不稳定信息] → 只拼接 AppError 链上的中文 `Message`、丢弃非 AppError 的 `cause` 并按 rune 截断至 500 字符;数据库等基础设施错误降级为固定安全摘要,业务类原因保留可见,禁止写入数据库、外部服务或敏感载荷原文。
|
||||
- [失败状态写入与失败审计的短事务二次异常] → 状态与审计同属一个回滚后短事务,`RowsAffected` 为 0 时跳过状态写入仍写审计;短事务异常复用既有换货失败审计的次级故障记录方式,并返回原失败、保留诊断。
|
||||
- [并发确认造成重复迁移] → 重用换货单 `FOR UPDATE` 锁与预期业务状态更新;只有仍为 `failed` 的失败重试可以进入受限路径。
|
||||
- [旧客户端只读取布尔字段] → 保持原字段及其成功语义,新字段只增不删。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 在隔离数据库执行新迁移,核对历史映射、非空约束和 down 后 Schema。
|
||||
2. 发布同时包含迁移、写侧状态转换、列表/详情 DTO 投影和审计更新的版本。
|
||||
1. 在隔离数据库执行新迁移(ADD COLUMN 带默认值 → 按既有字段回填 → 添加 `chk_exchange_order_migration_status` 约束),核对历史映射、非空与四值约束以及 down 后 Schema。
|
||||
2. 发布同时包含迁移、写侧状态转换、列表/详情 DTO 投影、换货导出新列和审计更新的版本。
|
||||
3. 发生应用回滚时,先回滚应用至仍兼容新增列的版本;仅在确认没有依赖新状态的数据或功能后执行 down 迁移。
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
- 将换货业务数据迁移结果统一为不迁移、待迁移、已迁移、迁移失败四个可观察状态,并保留最近一次失败原因。
|
||||
- 在换货完成的迁移失败场景中保留换货单可完成状态及失败事实;超级管理员或平台用户修复条件后可再次确认完成,并重新原子执行完整迁移。
|
||||
- 在换货列表和详情返回迁移状态及失败原因(仅迁移失败时),替代前端对现有布尔字段的推断。
|
||||
- 在换货列表和详情返回迁移状态及失败原因(仅迁移失败时),替代前端对现有布尔字段的推断;换货导出仅新增迁移状态列(中文名称),不导出失败原因。
|
||||
- 将迁移范围明确限定为资产钱包余额、有效套餐使用记录、累计充值字段和资产标签;资产归属、个人客户—资产绑定及手机号—资产关联不属于该迁移范围。
|
||||
|
||||
## Capabilities
|
||||
@@ -19,10 +19,10 @@
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `order-refund-exchange`: 明确换货业务数据迁移的状态、失败恢复、范围和列表/详情可见性。
|
||||
- `order-refund-exchange`: 明确换货业务数据迁移的状态、失败恢复、范围和列表、详情及导出可见性。
|
||||
|
||||
## Impact
|
||||
|
||||
- 影响 `internal/model/exchange_order.go`、换货完成写用例、换货列表 Query、换货 DTO 及既有换货审计。
|
||||
- 影响 `internal/model/exchange_order.go`、换货完成写用例、换货列表 Query、换货 DTO、换货导出场景及既有换货审计。
|
||||
- 需要新增成对数据库迁移,以保存迁移状态和最近失败原因;不修改既有迁移。
|
||||
- 既有换货列表与详情接口将新增/明确迁移状态字段,前端应改按状态展示。
|
||||
- 既有换货列表与详情接口将新增/明确迁移状态字段,前端应改按状态展示;换货导出新增一列迁移状态(中文名称),不导出失败原因。
|
||||
@@ -39,3 +39,14 @@
|
||||
#### Scenario: 不选择业务数据迁移完成换货
|
||||
- **WHEN** 换货单未选择业务数据迁移并确认完成
|
||||
- **THEN** 系统仍完成资产归属与个人客户—资产绑定的固有换货动作,但不迁移钱包余额、套餐使用记录、累计充值字段或资产标签
|
||||
|
||||
### Requirement: 换货导出业务数据迁移状态
|
||||
换货导出 SHALL 包含业务数据迁移状态,取值使用中文状态名称(不迁移、待迁移、已迁移、迁移失败),并 MUST NOT 导出迁移失败原因。该列 MUST 仅用于展示与追溯,MUST NOT 改变换货完成、业务数据迁移、失败恢复或数据访问范围任何规则。
|
||||
|
||||
#### Scenario: 导出包含迁移状态
|
||||
- **WHEN** 调用方导出换货记录
|
||||
- **THEN** 每行在「状态」之后返回该记录对应的中文迁移状态
|
||||
|
||||
#### Scenario: 迁移失败的记录被导出
|
||||
- **WHEN** 换货记录的迁移状态为 `failed`,且该记录保存了最近一次失败原因
|
||||
- **THEN** 导出行的迁移状态为“迁移失败”,且导出不输出该失败原因
|
||||
@@ -0,0 +1,19 @@
|
||||
## 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。
|
||||
@@ -283,6 +283,70 @@
|
||||
- **WHEN** 系统解析并返回这两个展示字段
|
||||
- **THEN** 退款金额校验、冻结实收金额、套餐失效、接续下一套餐、停机评估与佣金回溯行为均不发生变化
|
||||
|
||||
### Requirement: 换货业务数据迁移状态与失败恢复
|
||||
|
||||
系统 SHALL 为每张已持久化的物流换货单返回业务数据迁移状态 `not_migrated`(不迁移)、`pending`(待迁移)、`migrated`(已迁移)或 `failed`(迁移失败),以及对应的中文状态名称。未选择业务数据迁移的换货单状态 MUST 为 `not_migrated`;选择迁移但尚未成功完成的换货单状态 MUST 为 `pending`;完整迁移成功后状态 MUST 为 `migrated`;迁移执行失败后状态 MUST 为 `failed`,并保存最近一次可安全展示的失败原因。
|
||||
|
||||
换货列表和详情 SHALL 返回迁移状态及中文名称;仅当状态为 `failed` 时返回最近一次失败原因。既有 `migrate_data`、`migration_completed` 和迁移余额字段 SHALL 保持兼容,但客户端不得再通过它们推断迁移结果。直接换货创建失败继续按既有原子性整体回滚,不产生可查询的失败换货单。
|
||||
|
||||
#### Scenario: 不迁移的换货单
|
||||
|
||||
- **WHEN** 创建或发货时未选择业务数据迁移
|
||||
- **THEN** 换货列表和详情返回 `not_migrated` 及“不迁移”,且不返回迁移失败原因
|
||||
|
||||
#### Scenario: 待迁移的换货单
|
||||
|
||||
- **WHEN** 换货单已选择业务数据迁移但尚未成功完成换货
|
||||
- **THEN** 换货列表和详情返回 `pending` 及“待迁移”
|
||||
|
||||
#### Scenario: 成功完成业务数据迁移
|
||||
|
||||
- **WHEN** 换货完成时全部业务数据迁移成功
|
||||
- **THEN** 系统原子完成换货及业务数据迁移,列表和详情返回 `migrated` 及“已迁移”,并清除最近一次失败原因
|
||||
|
||||
#### Scenario: 迁移失败后保留可恢复事实
|
||||
|
||||
- **WHEN** 换货完成时任一业务数据迁移步骤失败
|
||||
- **THEN** 系统不得提交本次换货完成及任何部分迁移结果,换货单保持可确认完成状态,返回迁移失败,并在独立持久化事实中将迁移状态更新为 `failed` 和最近一次失败原因
|
||||
|
||||
#### Scenario: 管理员重试失败迁移
|
||||
|
||||
- **WHEN** 超级管理员或平台用户对处于可确认完成状态且迁移状态为 `failed` 的换货单再次确认完成
|
||||
- **THEN** 系统重新原子执行完整业务数据迁移;成功后将状态更新为 `migrated`,再次失败则保留 `failed` 并覆盖为最近一次失败原因
|
||||
|
||||
#### Scenario: 非平台账号重试失败迁移
|
||||
|
||||
- **WHEN** 非超级管理员且非平台用户尝试再次确认迁移状态为 `failed` 的换货单
|
||||
- **THEN** 系统拒绝该操作,换货单及迁移状态不变
|
||||
|
||||
### Requirement: 换货业务数据迁移范围
|
||||
|
||||
系统 SHALL 仅在选择业务数据迁移的换货完成中迁移旧资产的钱包余额、有效套餐使用记录、累计充值字段和资产标签。资产归属与个人客户—资产绑定 SHALL 继续作为换货完成固有动作,不受业务数据迁移选项控制;手机号—资产关联 MUST NOT 随换货或业务数据迁移转移,新资产首次访问时按其适用的手机号绑定规则处理。
|
||||
|
||||
#### Scenario: 选择业务数据迁移完成换货
|
||||
|
||||
- **WHEN** 换货单选择业务数据迁移并成功确认完成
|
||||
- **THEN** 系统迁移钱包余额、有效套餐使用记录、累计充值字段和资产标签,且不迁移手机号—资产关联
|
||||
|
||||
#### Scenario: 不选择业务数据迁移完成换货
|
||||
|
||||
- **WHEN** 换货单未选择业务数据迁移并确认完成
|
||||
- **THEN** 系统仍完成资产归属与个人客户—资产绑定的固有换货动作,但不迁移钱包余额、套餐使用记录、累计充值字段或资产标签
|
||||
|
||||
### Requirement: 换货导出业务数据迁移状态
|
||||
|
||||
换货导出 SHALL 包含业务数据迁移状态,取值使用中文状态名称(不迁移、待迁移、已迁移、迁移失败),并 MUST NOT 导出迁移失败原因。该列 MUST 仅用于展示与追溯,MUST NOT 改变换货完成、业务数据迁移、失败恢复或数据访问范围任何规则。
|
||||
|
||||
#### Scenario: 导出包含迁移状态
|
||||
|
||||
- **WHEN** 调用方导出换货记录
|
||||
- **THEN** 每行在「状态」之后返回该记录对应的中文迁移状态
|
||||
|
||||
#### Scenario: 迁移失败的记录被导出
|
||||
|
||||
- **WHEN** 换货记录的迁移状态为 `failed`,且该记录保存了最近一次失败原因
|
||||
- **THEN** 导出行的迁移状态为“迁移失败”,且导出不输出该失败原因
|
||||
|
||||
## 可达操作索引
|
||||
|
||||
本节只用于入口导航,不是行为 Requirement;业务义务以上述 Requirements 为准。
|
||||
|
||||
Reference in New Issue
Block a user