Files
junhong_cmp_fiber/openspec/changes/archive/2026-03-19-client-api-data-model-fixes/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

8.2 KiB
Raw Blame History

Why

系统存在 4 个影响资金安全和业务正确性的 BUG且即将启动客户端C 端)接口体系开发。本提案作为客户端接口系列提案的前置基础,解决三类问题:

  1. 资金/业务 BUG 修复代理零售价缺失导致价格计算错误BUG-1、后台订单误触发一次性佣金BUG-2、充值回调事务半提交风险BUG-4
  2. 基础字段准备:为客户端接口和换货系统新增必要的模型字段(asset_statusgenerationsourceoperator_typerealname_link_type
  3. 旧接口清理:删除基于 B 端认证体系的旧 H5 接口和旧个人客户登录接口,为新的 /api/c/v1 体系腾出空间

What Changes

BUG 修复

  • BUG-1 代理零售价修复ShopPackageAllocation 新增 retail_price 字段;GetPurchasePrice() 改为代理渠道查 allocation.retail_price、平台渠道用 Package.SuggestedRetailPricevalidatePackages() 内部价格累加同步修正;新增 cost_price 分配锁定规则(存在下级分配时禁止修改 cost_priceBatchUpdatePricing 接口仅支持成本价批量调整;新增独立接口 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 充值回调事务修复HandlePaymentCallbackUpdateStatusWithOptimisticLockUpdatePaymentInfos.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_idslinked_order_typelinked_carrier_typelinked_carrier_id,支持强充两阶段处理
  • Carrier 新增实名链接配置realname_link_typenone/template/gatewayrealname_link_template(支持 {iccid}/{msisdn}/{virtual_no} 占位符)。同步更新 Carrier admin DTOCarrierCreateRequest/CarrierUpdateRequest)包含这两个字段,使后台管理员可通过 API 配置运营商实名链接方式
  • PersonalCustomer 索引变更wx_open_id 从唯一索引改为普通索引(支持后续多 OpenID 方案)

旧接口删除

  • 删除全部旧 H5 接口internal/handler/h5/ 下所有文件auth、order、recharge、package_usage、enterprise_deviceinternal/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_pricecost_price 分配锁定规则。新增独立接口 PATCH /api/admin/packages/:id/retail-price 供代理修改自己的零售价;代理套餐列表展示 retail_price利润计算修正RetailPrice - CostPrice
  • asset-manual-deactivation:资产手动停用。新增后台接口 PATCH /api/admin/iot-cards/:id/deactivatePATCH /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-validationGetPurchasePrice() 价格来源改为按渠道区分代理→retail_price平台→SuggestedRetailPricevalidatePackages() 价格累加逻辑同步修正
  • package-list:代理查询套餐列表时,PackageResponse 新增 retail_price 字段;ProfitMargin 计算从 SuggestedRetailPrice - CostPrice 改为 RetailPrice - CostPrice
  • batch-pricingBatchUpdatePricing 接口仅支持 cost_price 批量调整;保留 cost_price 锁定校验(存在下级分配时不可修改)
  • one-time-commission-trigger:触发条件增加 order.Source == "client" 判断,确保仅客户端个人客户购买才触发
  • wallet-rechargeHandlePaymentCallback 事务一致性修复Store 方法支持传入事务 tx
  • iot-orderOrder 模型新增 source(订单来源)和 generation(世代)字段;CreateAdminOrder() 创建订单时从资产快照当前 generation 写入订单(而非依赖默认值 1
  • iot-cardIotCard 模型新增 asset_statusgeneration 字段
  • deviceDevice 模型新增 asset_statusgeneration 字段
  • personal-customerwx_open_id 索引从唯一改为普通索引
  • asset-recharge-adaptationAssetRechargeRecord 新增 operator_typegeneration、强充关联字段

Impact

  • 模型文件shop_package_allocation.gocarrier.goorder.goiot_card.godevice.gopackage_usage.goasset_recharge_record.gopersonal_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.gocost_price 锁定)、order/service.gosource 设置 + 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.goPackageResponse 新增 retail_price + 新增更新零售价请求 DTOcarrier_dto.go(新增实名链接字段)
  • Store 文件asset_recharge_store.go(支持事务传入)
  • 删除文件internal/handler/h5/ 全部5 个文件)、internal/routes/h5*.go3 个文件)、internal/handler/app/personal_customer.go 中旧方法
  • 数据库迁移7 张表共 15+ 个字段变更1 个索引变更
  • 文档生成器cmd/api/docs.gocmd/gendocs/main.go 移除 H5 Handler 引用
  • Bootstrap:移除 H5 Handler 注册
  • 性能:所有变更为字段新增/修复,无查询性能影响;新增字段均带 DEFAULT 值,迁移可在线执行