## Why 系统存在 4 个影响资金安全和业务正确性的 BUG,且即将启动客户端(C 端)接口体系开发。本提案作为客户端接口系列提案的**前置基础**,解决三类问题: 1. **资金/业务 BUG 修复**:代理零售价缺失导致价格计算错误(BUG-1)、后台订单误触发一次性佣金(BUG-2)、充值回调事务半提交风险(BUG-4) 2. **基础字段准备**:为客户端接口和换货系统新增必要的模型字段(`asset_status`、`generation`、`source`、`operator_type`、`realname_link_type`) 3. **旧接口清理**:删除基于 B 端认证体系的旧 H5 接口和旧个人客户登录接口,为新的 `/api/c/v1` 体系腾出空间 ## What Changes ### BUG 修复 - **BUG-1 代理零售价修复**:`ShopPackageAllocation` 新增 `retail_price` 字段;`GetPurchasePrice()` 改为代理渠道查 `allocation.retail_price`、平台渠道用 `Package.SuggestedRetailPrice`;`validatePackages()` 内部价格累加同步修正;新增 cost_price 分配锁定规则(存在下级分配时禁止修改 cost_price);`BatchUpdatePricing` 接口仅支持成本价批量调整;新增独立接口 `PATCH /api/admin/packages/:id/retail-price` 供代理修改自己的零售价;**代理套餐列表(`PackageResponse`)新增 `retail_price` 字段**,代理可查看自己的零售价;**利润计算修正**为 `RetailPrice - CostPrice`(代理实际利润 = 零售价 - 成本价,而非建议售价 - 成本价) - **BUG-2 一次性佣金触发修复**:`Order` 新增 `source` 字段(`admin`/`client`);佣金触发条件从 `!order.IsPurchaseOnBehalf` 改为 `!order.IsPurchaseOnBehalf && order.Source == "client"`,确保只有客户端个人客户购买才触发 - **BUG-4 充值回调事务修复**:`HandlePaymentCallback` 中 `UpdateStatusWithOptimisticLock` 和 `UpdatePaymentInfo` 从 `s.db.WithContext(ctx)` 改为事务内 `tx`,确保充值单状态变更和钱包入账原子完成 ### 新增模型字段 - **`IotCard`/`Device` 新增 `asset_status`**:业务生命周期状态(1-在库 2-已销售 3-已换货 4-已停用),与运营商 `network_status` 独立 - **`IotCard`/`Device` 新增 `generation`**:资产世代编号,换货转新时 +1,客户端按当前 generation 过滤历史数据 - **`Order`/`PackageUsage`/`AssetRechargeRecord` 新增 `generation`**:创建时快照资产当前 generation,客户端查询按此字段过滤 - **`AssetRechargeRecord` 新增 `operator_type`**:区分操作人类型(`admin_user`/`personal_customer`),配合 `user_id` 区分不同 ID 体系 - **`AssetRechargeRecord` 新增强充关联字段**:`linked_package_ids`、`linked_order_type`、`linked_carrier_type`、`linked_carrier_id`,支持强充两阶段处理 - **`Carrier` 新增实名链接配置**:`realname_link_type`(none/template/gateway)、`realname_link_template`(支持 `{iccid}`/`{msisdn}`/`{virtual_no}` 占位符)。**同步更新 Carrier admin DTO**(`CarrierCreateRequest`/`CarrierUpdateRequest`)包含这两个字段,使后台管理员可通过 API 配置运营商实名链接方式 - **`PersonalCustomer` 索引变更**:`wx_open_id` 从唯一索引改为普通索引(支持后续多 OpenID 方案) ### 旧接口删除 - **删除全部旧 H5 接口**:`internal/handler/h5/` 下所有文件(auth、order、recharge、package_usage、enterprise_device)、`internal/routes/h5*.go` 路由注册 - **删除旧个人客户登录接口**:`internal/handler/app/personal_customer.go` 中的 Login、SendCode、WechatOAuthLogin、BindWechat、Profile 方法 - **同步清理**:bootstrap 中 H5 Handler 注册、docs.go/gendocs 中引用 ## Capabilities ### New Capabilities - `asset-lifecycle-status`:资产业务生命周期状态管理。IotCard/Device 新增 `asset_status` 字段(在库→已销售→已换货→已停用),定义状态流转规则,与运营商 `network_status` 完全独立 - `asset-generation`:资产世代机制。IotCard/Device 的 `generation` 字段,关联表(Order/PackageUsage/AssetRechargeRecord)的 generation 写时快照规则,客户端按世代过滤、后台不过滤的查询规则 - `carrier-realname-config`:运营商实名链接配置。Carrier 模型新增 `realname_link_type`/`realname_link_template` 字段,支持 none/template/gateway 三种模式,URL 模板占位符替换。**Carrier admin DTO 同步更新**,后台可通过现有运营商管理接口配置实名链接 - `agent-retail-price`:代理零售价管理。ShopPackageAllocation 新增 `retail_price` 字段,支持代理设定面向终端客户的零售价,约束 `retail_price >= cost_price`,cost_price 分配锁定规则。新增独立接口 `PATCH /api/admin/packages/:id/retail-price` 供代理修改自己的零售价;**代理套餐列表展示 retail_price**;**利润计算修正**为 `RetailPrice - CostPrice` - `asset-manual-deactivation`:资产手动停用。新增后台接口 `PATCH /api/admin/iot-cards/:id/deactivate` 和 `PATCH /api/admin/devices/:id/deactivate`,将 `asset_status` 设为 4(已停用),仅 `asset_status=1`(在库)或 `asset_status=2`(已销售)时可操作 - `h5-legacy-cleanup`:旧 H5 接口和旧登录接口的完整删除,包括 handler、route、bootstrap 注册、文档生成器引用的清理 ### Modified Capabilities - `package-purchase-validation`:`GetPurchasePrice()` 价格来源改为按渠道区分(代理→retail_price,平台→SuggestedRetailPrice);`validatePackages()` 价格累加逻辑同步修正 - `package-list`:代理查询套餐列表时,`PackageResponse` 新增 `retail_price` 字段;`ProfitMargin` 计算从 `SuggestedRetailPrice - CostPrice` 改为 `RetailPrice - CostPrice` - `batch-pricing`:`BatchUpdatePricing` 接口仅支持 `cost_price` 批量调整;保留 `cost_price` 锁定校验(存在下级分配时不可修改) - `one-time-commission-trigger`:触发条件增加 `order.Source == "client"` 判断,确保仅客户端个人客户购买才触发 - `wallet-recharge`:`HandlePaymentCallback` 事务一致性修复,Store 方法支持传入事务 `tx` - `iot-order`:Order 模型新增 `source`(订单来源)和 `generation`(世代)字段;`CreateAdminOrder()` 创建订单时从资产快照当前 `generation` 写入订单(而非依赖默认值 1) - `iot-card`:IotCard 模型新增 `asset_status` 和 `generation` 字段 - `device`:Device 模型新增 `asset_status` 和 `generation` 字段 - `personal-customer`:`wx_open_id` 索引从唯一改为普通索引 - `asset-recharge-adaptation`:AssetRechargeRecord 新增 `operator_type`、`generation`、强充关联字段 ## Impact - **模型文件**:`shop_package_allocation.go`、`carrier.go`、`order.go`、`iot_card.go`、`device.go`、`package_usage.go`、`asset_recharge_record.go`、`personal_customer.go` - **Service 文件**:`purchase_validation/service.go`(价格计算)、`commission_calculation/service.go`(佣金触发)、`recharge/service.go`(回调事务)、`shop_package_batch_pricing/service.go`(仅成本价批量调价 + cost_price 锁定)、`shop_series_grant/service.go`(cost_price 锁定)、`order/service.go`(source 设置 + generation 快照)、`package/service.go`(新增代理改零售价接口逻辑 + 利润计算修正 + PackageResponse 新增 retail_price) - **Handler/DTO 文件**:`shop_package_batch_pricing.go` Handler(仅保留成本价批量调价)、`shop_package_batch_pricing_dto.go`(移除 `pricing_target` 字段)、`package.go` Handler(新增 `PATCH /packages/:id/retail-price`)、`package_dto.go`(`PackageResponse` 新增 `retail_price` + 新增更新零售价请求 DTO)、`carrier_dto.go`(新增实名链接字段) - **Store 文件**:`asset_recharge_store.go`(支持事务传入) - **删除文件**:`internal/handler/h5/` 全部(5 个文件)、`internal/routes/h5*.go`(3 个文件)、`internal/handler/app/personal_customer.go` 中旧方法 - **数据库迁移**:7 张表共 15+ 个字段变更,1 个索引变更 - **文档生成器**:`cmd/api/docs.go`、`cmd/gendocs/main.go` 移除 H5 Handler 引用 - **Bootstrap**:移除 H5 Handler 注册 - **性能**:所有变更为字段新增/修复,无查询性能影响;新增字段均带 DEFAULT 值,迁移可在线执行