将已完成的变更(proposal、design、tasks、delta specs)归档至 openspec/changes/archive/2026-03-04-refactor-agent-series-grant/。变更内容:合并系列分配和套餐分配为系列授权(Grant)、新增梯度佣金模式、新增代理层强充层级规则。50/50 任务全部完成。 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
11 KiB
1. 数据库迁移文件准备
- 1.1 使用 db-migration 规范创建迁移文件:
- 删除
tb_shop_series_allocation的 3 列:enable_one_time_commission、one_time_commission_trigger、one_time_commission_threshold - 新增
commission_tiers_json JSONB NOT NULL DEFAULT '[]'(梯度模式专属阶梯金额) - DOWN 脚本添加说明注释(不恢复数据)
- 删除
2. 删除旧接口(Handler / routes / DTO / Service)
- 2.1 删除文件:
internal/handler/admin/shop_series_allocation.gointernal/handler/admin/shop_package_allocation.gointernal/routes/shop_series_allocation.gointernal/routes/shop_package_allocation.gointernal/model/dto/shop_series_allocation.gointernal/model/dto/shop_package_allocation.gointernal/service/shop_series_allocation/(整个目录)internal/service/shop_package_allocation/(整个目录)
- 2.2
internal/bootstrap/types.go:删除ShopSeriesAllocation、ShopPackageAllocation两个 Handler 字段 - 2.3
internal/bootstrap/handlers.go:删除对应 Handler 初始化行;删除对应 service 引用 - 2.4
internal/bootstrap/services.go:删除ShopSeriesAllocation、ShopPackageAllocation两个 Service 字段及初始化;移除对应 import - 2.5
pkg/openapi/handlers.go:删除ShopSeriesAllocation、ShopPackageAllocation两行 - 2.6
internal/routes/admin.go:删除registerShopSeriesAllocationRoutes、registerShopPackageAllocationRoutes两处调用 - 2.7 运行
go build ./...确认无编译错误
3. Model 更新
- 3.1
internal/model/shop_series_allocation.go:- 删除
EnableOneTimeCommission、OneTimeCommissionTrigger、OneTimeCommissionThreshold三个字段 - 新增
CommissionTiersJSON string字段(JSONB,默认'[]') - 新增辅助类型
AllocationCommissionTier struct { Threshold int64; Amount int64 } - 新增
GetCommissionTiers() ([]AllocationCommissionTier, error)方法 - 新增
SetCommissionTiers(tiers []AllocationCommissionTier) error方法
- 删除
- 3.2
internal/model/package.go:OneTimeCommissionTier新增Operator string字段(json:"operator")- 新增运算符常量:
TierOperatorGT = ">"/TierOperatorGTE = ">="/TierOperatorLT = "<"/TierOperatorLTE = "<="
- 3.3 运行
go build ./...确认无编译错误
4. 修复梯度模式计算引擎
- 4.1
internal/service/commission_calculation/service.go: 修改calculateChainOneTimeCommission中梯度模式分支:- 原来:直接把
config.Tiers(全局)传给matchOneTimeCommissionTier - 新的:从
currentSeriesAllocation.GetCommissionTiers()取专属金额列表,结合config.Tiers(取 operator/dimension/stat_scope/threshold)做匹配 - 匹配逻辑:根据代理销售统计和 tier.Operator 判断是否命中 threshold → 查专属列表同 threshold 的 amount → 即 myAmount;未命中任何阶梯时 myAmount = 0
- commission_tiers_json 为空时(历史数据)fallback 到
currentSeriesAllocation.OneTimeCommissionAmount
- 原来:直接把
- 4.2 修改
matchOneTimeCommissionTier:接受 agentTiers[]AllocationCommissionTier参数作为金额来源;根据tier.Operator选择对应比较逻辑(>、>=、<、<=),Operator 为空时默认>= - 4.3 运行
go build ./...确认无编译错误
5. 修复强充层级
- 5.1
internal/service/order/service.gocheckForceRechargeRequirement():config.TriggerType == "first_recharge"时:直接返回需要强充(不变),不查代理配置config.TriggerType == "accumulated_recharge"且config.EnableForceRecharge == false时: 从result.Card.ShopID(或result.Device.ShopID)查询该代理的ShopSeriesAllocation, 若该分配EnableForceRecharge=true则返回代理强充配置,查询不到时降级返回need_force_recharge=false
- 5.2 验证
GetPurchaseCheck调用路径已覆盖新逻辑(复用同一函数,无需额外修改) - 5.3 运行
go build ./...确认无编译错误
6. 新系列授权 DTO
- 6.1 创建
internal/model/dto/shop_series_grant_dto.go,定义:GrantPackageItem(package_id、cost_price、remove *bool)ShopSeriesGrantPackageItem(package_id、package_name、package_code、cost_price、shelf_status、status)GrantCommissionTierItem(operator string、threshold int64、amount int64) —— operator 仅出现在响应中(从 PackageSeries 合并),请求中不传 operator
- 6.2 定义
ShopSeriesGrantResponse:- id、shop_id/name、series_id/name/code、commission_type
- one_time_commission_amount(固定模式有效,梯度模式返回 0)
- commission_tiers []GrantCommissionTierItem(梯度模式有值,固定模式为空)
- force_recharge_locked、force_recharge_enabled、force_recharge_amount
- allocator_shop_id/name、status、packages、created_at、updated_at
- 6.3 定义
CreateShopSeriesGrantRequest:- shop_id、series_id
- one_time_commission_amount *int64(固定模式必填)
- commission_tiers []GrantCommissionTierItem(梯度模式必填)
- enable_force_recharge *bool、force_recharge_amount *int64
- packages []GrantPackageItem
- 6.4 定义
UpdateShopSeriesGrantRequest:- one_time_commission_amount *int64
- commission_tiers []GrantCommissionTierItem
- enable_force_recharge *bool、force_recharge_amount *int64
- 6.5 定义
ManageGrantPackagesRequest(packages []GrantPackageItem) - 6.6 定义
ShopSeriesGrantListRequest(page、page_size、shop_id *uint、series_id *uint、allocator_shop_id *uint、status *int)及列表 DTO(ShopSeriesGrantListItem含 package_count、ShopSeriesGrantPageResult)
7. 新系列授权 Service
-
7.1 创建
internal/service/shop_series_grant/service.go,定义 Service 结构及 New() 构造函数 (依赖:db、shopSeriesAllocationStore、shopPackageAllocationStore、shopPackageAllocationPriceHistoryStore、shopStore、packageStore、packageSeriesStore) -
7.2 实现私有方法
getParentCeilingFixed():固定模式天花板查询- allocatorShopID=0 → 读 PackageSeries.commission_amount
- allocatorShopID>0 → 读分配者自身的 ShopSeriesAllocation.one_time_commission_amount
-
7.3 实现私有方法
getParentCeilingTiered():梯度模式天花板查询- allocatorShopID=0 → 读 PackageSeries.config.Tiers 中各 threshold 的 amount
- allocatorShopID>0 → 读分配者自身 ShopSeriesAllocation.commission_tiers_json
-
7.4 实现
Create():- 查询 PackageSeries 确认 commission_type
- 检查重复授权:shop_id + series_id 已有 active 记录 → 错误"该代理已存在此系列授权"
- allocator 是代理时:查分配者自身的 ShopSeriesAllocation,无记录 → 错误"当前账号无此系列授权,无法向下分配"
- 固定模式:one_time_commission_amount 必填 + 天花板校验
- 梯度模式:commission_tiers 必填 + 阶梯数量和 threshold 必须与 PackageSeries 完全一致(不多不少,amount 可为 0)+ 每档位天花板校验
- 强充层级判断(TriggerType=first_recharge 或平台已设强充 → locked=true 忽略代理传入;仅 accumulated_recharge 且平台未设时接受代理强充配置)
- 事务中创建 ShopSeriesAllocation + N 条 ShopPackageAllocation
- 返回聚合响应
-
7.5 实现
Get():查询 ShopSeriesAllocation → 查 PackageSeries 取全局 tiers(含 operator)→ 关联套餐分配 → 拼装 ShopSeriesGrantResponse (梯度模式下,commission_tiers 响应需将 agent 的 amount 与 PackageSeries tiers 的 operator 按 threshold 合并) -
7.6 实现
List():分页查询 → 统计 package_count → 返回 ShopSeriesGrantPageResult -
7.7 实现
Update():- 固定模式:含 one_time_commission_amount 时做天花板校验
- 梯度模式:含 commission_tiers 时做每档位天花板校验
- 平台已设强充时忽略强充变更
- 保存更新
-
7.8 实现
ManagePackages():事务中处理 packages 列表:- remove=true:查找 active 的 ShopPackageAllocation,找到则软删除,找不到则静默忽略
- 无 remove 标志:校验套餐归属和分配权限,查现有 active 记录(有则更新 cost_price+写历史,无则新建)
-
7.9 实现
Delete():检查子级依赖 → 事务软删除 ShopSeriesAllocation + 所有关联 ShopPackageAllocation -
7.10 运行
go build ./...确认无编译错误
8. Handler、路由及文档生成器
- 8.1 创建
internal/handler/admin/shop_series_grant.go,实现 Create、Get、List、Update、ManagePackages、Delete 六个 Handler 方法 - 8.2 创建
internal/routes/shop_series_grant.go,注册路由(Tag: "代理系列授权"):GET /shop-series-grantsPOST /shop-series-grantsGET /shop-series-grants/:idPUT /shop-series-grants/:idDELETE /shop-series-grants/:idPUT /shop-series-grants/:id/packages
- 8.3 运行
go build ./...确认无编译错误
9. 依赖注入 & Bootstrap
- 9.1
internal/bootstrap/types.go:添加ShopSeriesGrant *admin.ShopSeriesGrantHandler字段 - 9.2
internal/bootstrap/services.go:import shop_series_grant service 包,添加字段并在initServices()中初始化 - 9.3
internal/bootstrap/handlers.go:添加 ShopSeriesGrant Handler 初始化 - 9.4
pkg/openapi/handlers.go:添加ShopSeriesGrant: admin.NewShopSeriesGrantHandler(nil) - 9.5
internal/routes/admin.go:添加registerShopSeriesGrantRoutes()调用 - 9.6 运行
go build ./...确认完整构建通过
10. 执行迁移 & 数据验证
- 10.1 执行迁移(
make migrate-up),用 db-migration 规范验证:3 列已删除,commission_tiers_json 列已添加 - 10.2 db-validation:创建固定模式授权(正常路径),确认 ShopSeriesAllocation 和 ShopPackageAllocation 均创建成功
- 10.3 db-validation:固定模式金额超过父级天花板时接口返回错误
- 10.4 db-validation:梯度模式创建授权,commission_tiers_json 正确写入
- 10.5 db-validation:梯度模式某档位金额超过父级同档位时接口返回错误
- 10.6 db-validation:代理自设强充后,购买预检接口返回 need_force_recharge=true,金额与代理设置一致
- 10.7 db-validation:平台系列已设强充时,代理自设强充被锁定,购买预检使用平台强充金额
- 10.8 db-validation:梯度模式阶梯含
operator="<"时,销售统计低于阈值的代理命中该档位,高于阈值的代理不命中