fix: 修正零售价架构错误 + 清理旧微信配置 + 归档提案 + 前端接口文档
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m12s

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 的错误描述
This commit is contained in:
2026-03-19 17:39:43 +08:00
parent 9bd55a1695
commit b9733c4913
98 changed files with 3665 additions and 571 deletions

View File

@@ -0,0 +1,162 @@
## 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`