Files
huang b52cb9a078
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m27s
fix: 修复梯度佣金档位字段缺失,补全授权接口响应字段及强充有效状态
- OneTimeCommissionTierDTO 补充 operator 字段映射
- GrantCommissionTierItem 补充 dimension/stat_scope 字段(从全局配置合并)
- 系列授权列表/详情补充强充锁定状态和强充金额的有效值计算
- 同步 OpenSpec 主规范并归档变更文档
2026-03-05 11:23:28 +08:00

5.5 KiB
Raw Permalink Blame History

Context

refactor-agent-series-grant 变更在 Model 层(model.OneTimeCommissionTier)已正确新增 Operator 字段,并添加了运算符常量(TierOperatorGT/GTE/LT/LTE),但遗漏了同步更新 DTO 层和 Service 层的映射逻辑。

具体遗漏:

  1. OneTimeCommissionTierDTOpackage_series_dto.go未加 Operator 字段 → API 无法读写 operator
  2. GrantCommissionTierItemshop_series_grant_dto.go未加 DimensionStatScope 字段 → grant 响应中梯度档位条件不透明
  3. package_series/service.godtoToModelConfig() / modelToDTO() 未处理 Operator 映射
  4. shop_series_grant/service.gobuildGrantResponse() 合并全局 tiers 时未携带 DimensionStatScope
  5. 系列授权列表/详情响应未正确反映 force_recharge_enabled 有效状态,且列表缺少 force_recharge_lockedforce_recharge_amount 字段

无数据库结构变更,纯 DTO + Service 层修复。

Bug 3 根因详述forceRechargeLocked = config.TriggerType == FirstRecharge || config.EnableForceRecharge。当此条件为 trueService 正确跳过写 allocation.EnableForceRecharge=true,分配表中该字段保持 false。但 List 只返回 a.EnableForceRechargeDetail buildGrantResponse 也返回 allocation.EnableForceRecharge,两处均未计算有效状态,导致前端看到 force_recharge_enabled=false

Goals / Non-Goals

Goals:

  • OneTimeCommissionTierDTO 支持 operator 字段的读写(创建/更新套餐系列时可传,查询时返回)
  • GrantCommissionTierItem 响应中补充展示 dimensionstat_scope(只读,从 PackageSeries 全局配置合并)
  • 向前兼容:operator 字段均使用 omitempty,老客户端请求不传时默认 >=(沿用 Model 层现有 fallback 逻辑)
  • ShopSeriesGrantListItem 补充 force_recharge_lockedforce_recharge_amount 字段
  • 列表和详情 force_recharge_enabled 反映有效状态(allocation.EnableForceRecharge || forceRechargeLocked);锁定时 force_recharge_amountconfig.ForceAmount

Non-Goals:

  • 不修改数据库结构、迁移文件
  • 不修改 model.OneTimeCommissionTier(已正确)
  • 不修改佣金计算引擎(commission_calculation/service.go
  • 不修改 GrantCommissionTierItem 请求侧(代理创建授权时仍不传 operator/dimension/stat_scope这三个字段来自全局配置不可修改
  • 不修改梯度档位的校验逻辑threshold 匹配等已正确)

Decisions

决策 1Operator 在 DTO 中用 omitempty

OneTimeCommissionTierDTO.OperatorGrantCommissionTierItem.Operator 均使用 json:"operator,omitempty"

理由:现有未设 operator 的套餐系列JSONB 中该字段为空字符串,omitempty 避免返回无意义的 "" 给前端,且与 Model 层的"Operator 为空时 fallback 到 >="语义一致。

备选:永远返回字符串(空或 >=)→ 否决,因为"空"在 JSON 中会被序列化为 "",语义不清晰,而写入 JSONB 时可能覆盖原本为空的历史数据。

决策 2Dimension/StatScope 仅出现在响应中,请求侧不开放

GrantCommissionTierItemDimensionStatScope 仅用于 GET/POST/PUT 的响应输出(从 PackageSeries 全局配置按 threshold 合并),请求体中代理仍只传 thresholdamount

理由:这两个字段是套餐系列级别的全局配置,代理在创建授权时不能修改条件,只能修改金额。与 Operator 的处理方式保持一致(已在上次变更中确立该设计原则)。

决策 3buildGrantResponse 按 threshold 索引合并全部条件字段

现有代码按 threshold 构建 agentAmountMap,在遍历 config.Tiers 时合并 Operator。本次同步合并 DimensionStatScope无需额外查询O(N) 时间复杂度N 为档位数(通常 ≤ 5性能无影响。

决策 4force_recharge_enabled 返回有效状态而非存储状态

列表和详情响应中,force_recharge_enabled = allocation.EnableForceRecharge || forceRechargeLocked

理由:前端关心的是「强充是否实际生效」而非「代理是否主动开启」。当系列锁定强充时,即使 allocation 存储 falseService 正确设计:锁定时不覆盖),对用户而言强充仍然生效。返回 false 会让前端误判,需在响应层修正。

备选:返回存储值(allocation.EnableForceRecharge)并依靠 force_recharge_locked 由前端推导 → 否决,因为历史已有前端对接问题,且两个字段冗余更易出错。统一在后端计算有效状态是更稳健的 API 设计。

Risks / Trade-offs

风险 缓解措施
历史套餐系列 JSONB 中梯度档位无 operator/dimension/stat_scope 响应用 omitempty,缺失字段不出现在响应中而非返回空值;前端应做好字段缺失的降级处理
CreateShopSeriesGrantRequest.CommissionTiers 中含有 operator/dimension/stat_scope 字段(因共用同一 DTO 结构) GrantCommissionTierItem 在请求侧这三个字段会被忽略Service 层不读取),无副作用;可在 description 注释中说明仅响应有效
锁定时 force_recharge_amount 来源变更config 而非 allocation 列表新增字段详情原有字段调整逻辑前端只需使用响应值无需区分来源allocation 表 force_recharge_amount 在锁定时仍存 0不需迁移