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 的错误描述
8.2 KiB
8.2 KiB
Why
系统存在 4 个影响资金安全和业务正确性的 BUG,且即将启动客户端(C 端)接口体系开发。本提案作为客户端接口系列提案的前置基础,解决三类问题:
- 资金/业务 BUG 修复:代理零售价缺失导致价格计算错误(BUG-1)、后台订单误触发一次性佣金(BUG-2)、充值回调事务半提交风险(BUG-4)
- 基础字段准备:为客户端接口和换货系统新增必要的模型字段(
asset_status、generation、source、operator_type、realname_link_type) - 旧接口清理:删除基于 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 - CostPriceasset-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 - CostPricebatch-pricing:BatchUpdatePricing接口仅支持cost_price批量调整;保留cost_price锁定校验(存在下级分配时不可修改)one-time-commission-trigger:触发条件增加order.Source == "client"判断,确保仅客户端个人客户购买才触发wallet-recharge:HandlePaymentCallback事务一致性修复,Store 方法支持传入事务txiot-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.goHandler(仅保留成本价批量调价)、shop_package_batch_pricing_dto.go(移除pricing_target字段)、package.goHandler(新增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 值,迁移可在线执行