Files
junhong_cmp_fiber/openspec/changes/archive/2026-03-19-client-exchange-system/proposal.md
huang b9733c4913
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m12s
fix: 修正零售价架构错误 + 清理旧微信配置 + 归档提案 + 前端接口文档
1. 修正 retail_price 架构:
   - 删除 batch-pricing 接口的 pricing_target 字段和 retail_price 分支
     (上级只能改下级成本价,不能改零售价)
   - 新增 PATCH /api/admin/packages/:id/retail-price 接口
     (代理自己改自己的零售价,校验 retail_price >= cost_price)

2. 清理旧微信 YAML 配置(已全部迁移到数据库 tb_wechat_config):
   - 删除 config.yaml 中 wechat.official_account 配置节
   - 删除 NewOfficialAccountApp() 旧工厂函数
   - 清理 personal_customer service 中的死代码(旧登录/绑定微信方法)
   - 清理 docker-compose.prod.yml 中旧微信环境变量和证书挂载注释

3. 归档四个已完成提案到 openspec/changes/archive/

4. 新增前端接口变更说明文档(docs/前端接口变更说明.md)

5. 修正归档提案和 specs 中关于 pricing_target 的错误描述
2026-03-19 17:39:43 +08:00

163 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Why
现有 `CardReplacementRecord` 模型仅支持简单换卡,无法满足完整换货需求:缺少收货地址、快递信息、设备换货、全量数据迁移等功能。客户端换货场景中,后台发起换货 → 客户端收到通知填写收货信息 → 后台发货+确认完成(含全量迁移)→ 旧资产可"转新"重新销售,是一个跨后台/客户端的完整业务闭环。
**前置依赖**:提案 0`asset_status`/`generation` 字段已就位)、提案 1客户端认证
## What Changes
### 新增模型
- **ExchangeOrder换货单**:完整的换货生命周期模型,包含旧/新资产信息、收货地址、物流信息、迁移状态。状态机:`1-待填写信息 → 2-待发货 → 3-已发货待确认 → 4-已完成`1 或 2 时可取消(→5)
### 删除旧模型
- **CardReplacementRecord**:表改名为 `tb_card_replacement_record_legacy`,代码引用替换为 ExchangeOrder
### 后台换货管理(模块 H7 个接口)
- **H1 发起换货** `POST /api/admin/exchanges`:验证资产无进行中换货单,创建 status=1
- **H2 换货列表** `GET /api/admin/exchanges`:支持状态筛选、资产标识符搜索、时间范围
- **H3 换货详情** `GET /api/admin/exchanges/:id`
- **H4 发货** `POST /api/admin/exchanges/:id/ship`:填写物流信息+新资产标识符,验证 status=2、同类型资产、新资产 asset_status=1在库
- **H5 确认完成** `POST /api/admin/exchanges/:id/complete`:验证 status=3如 migrate_data=true 则执行全量迁移11 张表事务内操作),旧资产 asset_status→3
- **H6 取消换货** `POST /api/admin/exchanges/:id/cancel`:验证 status IN (1,2),已发货不可取消
- **H7 旧资产转新** `POST /api/admin/exchanges/:id/renew`:旧资产 asset_status 从 3→1generation+1清除客户绑定和累计状态创建新空钱包
### 客户端换货(模块 G2 个接口)
- **G1 查询换货通知** `GET /api/c/v1/exchange/pending?identifier=xxx`:查是否有进行中的换货单
- **G2 填写收货信息** `POST /api/c/v1/exchange/:id/shipping-info`:验证 status=1更新收货信息status→2
### 换货状态机
```
后台发起换货
┌─────────────────────┐
│ 1-待填写信息 │ ←── ExchangeOrder 创建
│ (等待客户端填写) │
└──────────┬──────────┘
客户端填写收货信息 [G2]
┌─────────────────────┐
│ 2-待发货 │
│ (等待后台填写物流) │
└──────────┬──────────┘
后台发货 [H4] (填物流+新资产)
┌─────────────────────┐
│ 3-已发货待确认 │
│ (等待后台确认完成) │
└──────────┬──────────┘
后台确认完成 [H5]
(可选: 全量迁移)
┌─────────────────────┐
│ 4-已完成 │
└─────────────────────┘
取消: status=1 或 2 时可取消 → 5-已取消
已发货(status=3)后不可取消
```
### 全量迁移流程H5 确认完成时触发)
```
确认完成 (migrate_data=true)
事务开始
├── 1. 钱包余额转移
│ 旧 AssetWallet.Balance → 新 AssetWallet
│ 生成迁移流水 AssetWalletTransaction
├── 2. 生效中套餐关联新资产
│ PackageUsage WHERE iot_card_id/device_id=旧 AND status IN (生效中)
│ → UPDATE iot_card_id/device_id = 新
├── 3. 累计充值/首充状态迁移
│ IotCard/Device 的 AccumulatedRecharge/FirstCommissionPaid
│ 等字段复制到新资产
├── 4. 标签复制
│ ResourceTag WHERE resource_type=? AND resource_id=旧
│ → 为新资产创建相同标签
├── 5. 个人客户绑定更新
│ PersonalCustomerDevice WHERE virtual_no=旧虚拟号
│ → UPDATE virtual_no = 新虚拟号
├── 6. 旧资产标记
│ 旧资产 asset_status → 3已换货
└── 7. 记录迁移信息
ExchangeOrder: migration_completed=true,
migration_balance=转移金额
事务提交
ExchangeOrder status → 4已完成
注意:
- 设备换设备时不迁移 DeviceSimBinding卡绑定关系
- 新设备自带新的 SIM 卡,旧设备的卡绑定保持不变
- 保留不修改的表: tb_order, tb_commission, tb_data_usage_record,
tb_asset_recharge_record历史记录保留通过 generation 隔离)
```
### 转新流程H7
```
旧资产 (asset_status=3 已换货)
POST /api/admin/exchanges/:id/renew
├── 1. asset_status: 3 → 1在库
├── 2. generation: +1进入新世代
├── 3. 清除: 累计充值状态、首充触发状态
├── 4. 清除: PersonalCustomerDevice 绑定
├── 5. 创建新空钱包(新 wallet_id
└── 6. 不删除历史数据(通过 generation 隔离)
旧资产可重新销售给新客户
新客户查询时按当前 generation 过滤
看不到旧周期数据
```
## Capabilities
### New Capabilities
- `exchange-order-model`ExchangeOrder 模型定义、状态机、状态常量、换货单号生成规则
- `exchange-admin-management`:后台换货管理 CRUDH1~H3、发货H4含同类型资产校验+新资产在库校验、确认完成H5含全量迁移事务、取消H6、转新H7含 generation 自增+状态重置)
- `exchange-data-migration`全量迁移逻辑11 张数据表的事务内操作规则,设备不迁移卡绑定的特殊规则
- `exchange-client-notification`客户端换货通知查询G1、收货信息填写G2
### Modified Capabilities
- `iot-card`IotCard 新增换货相关行为——`asset_status=3` 标记、转新时 generation 自增+状态重置
- `device`Device 同上
- `personal-customer`PersonalCustomerDevice 绑定关系在换货迁移时更新虚拟号
- `card-replacement`**REMOVED** — CardReplacementRecord 模型废弃,表改名为 legacy代码引用替换为 ExchangeOrder
## Impact
- **新增文件**`internal/model/exchange_order.go`(模型);`internal/handler/admin/exchange.go`(后台 Handler`internal/handler/app/client_exchange.go`(客户端 Handler`internal/service/exchange/service.go`Service含迁移逻辑`internal/store/postgres/exchange_order_store.go`StoreDTO 文件;迁移文件;常量和错误码
- **修改文件**`internal/model/card_replacement.go`(删除或标记废弃);`internal/store/postgres/iot_card_store.go`(移除 `is_replaced` 过滤改为查新表);`internal/model/system.go`AutoMigrate 移除旧模型+注册新模型);`internal/routes/`(新增后台+客户端路由);`internal/bootstrap/`(注册新模块);`cmd/api/docs.go` + `cmd/gendocs/main.go`(文档生成器)
- **新增 API 路由**:后台 `/api/admin/exchanges/` 下 7 个端点 + 客户端 `/api/c/v1/exchange/` 下 2 个端点
- **数据库变更**:新建 `tb_exchange_order` 表;旧表 `tb_card_replacement_record` 改名为 `tb_card_replacement_record_legacy`
- **全量迁移涉及 11 张表**`tb_asset_wallet``tb_asset_wallet_transaction``tb_asset_recharge_record``tb_package_usage``tb_package_usage_daily_record``tb_order``tb_commission``tb_data_usage_record``tb_resource_tag``tb_personal_customer_device``tb_iot_card`/`tb_device`