Files
junhong_cmp_fiber/docs/commission-package-model.md
huang b18ecfeb55
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m29s
refactor: 一次性佣金配置从套餐级别提升到系列级别
主要变更:
- 新增 tb_shop_series_allocation 表,存储系列级别的一次性佣金配置
- ShopPackageAllocation 移除 one_time_commission_amount 字段
- PackageSeries 新增 enable_one_time_commission 字段控制是否启用一次性佣金
- 新增 /api/admin/shop-series-allocations CRUD 接口
- 佣金计算逻辑改为从 ShopSeriesAllocation 获取一次性佣金金额
- 删除废弃的 ShopSeriesOneTimeCommissionTier 模型
- OpenAPI Tag '系列分配' 和 '单套餐分配' 合并为 '套餐分配'

迁移脚本:
- 000042: 重构佣金套餐模型
- 000043: 简化佣金分配
- 000044: 一次性佣金分配重构
- 000045: PackageSeries 添加 enable_one_time_commission 字段

测试:
- 新增验收测试 (shop_series_allocation, commission_calculation)
- 新增流程测试 (one_time_commission_chain)
- 删除过时的单元测试(已被验收测试覆盖)
2026-02-04 14:28:44 +08:00

11 KiB
Raw Permalink Blame History

套餐与佣金业务模型

本文档定义了套餐、套餐系列、佣金的完整业务模型,作为系统改造的规范参考。


一、核心概念

1.1 两种佣金类型

系统只有两种佣金类型:

佣金类型 触发时机 触发次数 计算方式
差价佣金 每笔订单 每单都触发 下级成本价 - 自己成本价
一次性佣金 首充/累计充值达标 每张卡/设备只触发一次 上级给的 - 给下级的

1.2 实体关系

┌─────────────────┐
│   套餐系列       │
│  PackageSeries  │
├─────────────────┤
│ • 系列名称      │
│ • 一次性佣金规则 │ ← 可选配置
└────────┬────────┘
         │ 1:N
         ▼
┌─────────────────┐          ┌─────────────────┐
│     套餐        │          │    卡/设备      │
│    Package      │          │   IoT/Device    │
├─────────────────┤          ├─────────────────┤
│ • 成本价        │          │ • 绑定系列ID    │
│ • 建议售价      │          │ • 累计充值金额  │ ← 按系列累计
│ • 真流量(必填)  │          │ • 是否已首充    │ ← 按系列记录
│ • 虚流量(可选)  │          └────────┬────────┘
│ • 虚流量开关    │                   │
└────────┬────────┘                   │ 分配
         │                            ▼
         │ 分配              ┌─────────────────┐
         ▼                   │      店铺       │
┌─────────────────┐          │      Shop       │
│   套餐分配      │◀─────────┤ • 代理层级      │
│ PkgAllocation   │          │ • 上级店铺ID    │
├─────────────────┤          └─────────────────┘
│ • 店铺ID        │
│ • 套餐ID        │
│ • 成本价(加价后)│
│ • 一次性佣金额  │ ← 给该代理的金额
└─────────────────┘

二、套餐模型

2.1 字段定义

字段 类型 必填 说明
cost_price int64 成本价(平台设置的基础成本价,分)
suggested_price int64 建议售价(给代理参考,分)
real_data_mb int64 真实流量额度MB
enable_virtual_data bool 是否启用虚流量
virtual_data_mb int64 虚流量额度(启用时必填,≤ 真实流量MB

2.2 流量停机判断

停机目标值 = enable_virtual_data ? virtual_data_mb : real_data_mb

2.3 不同用户视角

用户类型 看到的成本价 看到的一次性佣金
平台 基础成本价 完整规则
代理A A的成本价已加价 A能拿到的金额
代理A1 A1的成本价再加价 A1能拿到的金额

三、差价佣金

3.1 计算规则

平台设置基础成本价: 100
    │
    │ 分配给代理A设置成本价: 120
    ▼
代理A成本价: 120
    │
    │ 分配给代理A1设置成本价: 130
    ▼
代理A1成本价: 130
    │
    │ A1销售给客户售价: 200
    ▼

结果:
  • A1 收入 = 200 - 130 = 70元销售利润不是佣金
  • A 佣金 = 130 - 120 = 10元差价佣金
  • 平台收入 = 120元

3.2 关键区分

  • 收入/利润:末端代理的 售价 - 自己成本价
  • 差价佣金:上级代理的 下级成本价 - 自己成本价
  • 平台收入:一级代理的成本价

四、一次性佣金

4.1 触发条件

条件类型 说明 强充要求
first_recharge 首充:该卡/设备在该系列下的第一次充值 必须强充
accumulated_recharge 累计充值:累计充值金额达到阈值 可选强充

4.2 规则配置(套餐系列层面)

配置项 类型 说明
enable bool 是否启用一次性佣金
trigger_type string 触发类型:first_recharge / accumulated_recharge
threshold int64 触发阈值(分):首充要求金额 或 累计要求金额
commission_type string 返佣类型:fixed(固定) / tiered(梯度)
commission_amount int64 固定返佣金额fixed类型时
tiers array 梯度配置tiered类型时
validity_type string 时效类型:permanent / fixed_date / relative
validity_value string 时效值(到期日期 或 月数)
enable_force_recharge bool 是否启用强充
force_calc_type string 强充金额计算:fixed(固定) / dynamic(动态差额)
force_amount int64 强充金额fixed类型时

4.3 链式分配

一次性佣金在整条代理链上按约定分配:

系列规则首充100返20

分配配置:
  平台给A20元
  A给A18元
  A1给A25元

触发首充时:
  A2 获得5元
  A1 获得8 - 5 = 3元
  A  获得20 - 8 = 12元
  ─────────────────────
  合计20元 ✓

4.4 首充流程

客户购买套餐
    │
    ▼
预检:系列是否启用一次性佣金且为首充?
    │
   否 ───────────────────▶ 正常购买流程
    │
   是
    │
    ▼
该卡/设备在该系列下是否已首充过?
    │
   是 ───────────────────▶ 正常购买流程(不再返佣)
    │
   否
    │
    ▼
计算强充金额 = max(首充要求, 套餐售价)
    │
    ▼
返回提示:"需要充值 xxx 元"
    │
    ▼
用户确认 → 创建充值订单(金额=强充金额)
    │
    ▼
用户支付
    │
    ▼
支付成功:
  1. 钱进入钱包
  2. 标记该卡/设备已首充
  3. 自动创建套餐购买订单并完成
  4. 扣款(套餐售价)
  5. 触发一次性佣金,链式分配

4.5 累计充值流程

客户充值(直接充值到钱包)
    │
    ▼
累计充值金额 += 本次充值金额
    │
    ▼
该卡/设备是否已触发过累计充值返佣?
    │
   是 ───────────────────▶ 结束(不再返佣)
    │
   否
    │
    ▼
累计金额 >= 累计要求?
    │
   否 ───────────────────▶ 结束(继续累计)
    │
   是
    │
    ▼
触发一次性佣金,链式分配
标记该卡/设备已触发累计充值返佣

累计规则

操作类型 是否累计
直接充值到钱包 累计
直接购买套餐(不经过钱包) 不累计
强充购买套餐(先充值再扣款) 累计(充值部分)

五、梯度佣金

梯度佣金是一次性佣金的进阶版,根据代理销量/销售额动态调整返佣金额。

5.1 配置项

配置项 类型 说明
tier_dimension string 梯度维度:sales_count(销量) / sales_amount(销售额)
stat_scope string 统计范围:self(仅自己) / self_and_sub(自己+下级)
tiers array 梯度档位列表
tiers[].threshold int64 阈值(销量或销售额)
tiers[].amount int64 返佣金额(分)

5.2 示例

梯度规则(销量维度):
┌────────────────┬────────────────────────┐
│    销量区间     │    首充100返佣金额     │
├────────────────┼────────────────────────┤
│    >= 0        │         5元            │
├────────────────┼────────────────────────┤
│    >= 100      │        10元            │
├────────────────┼────────────────────────┤
│    >= 200      │        20元            │
└────────────────┴────────────────────────┘

代理A当前销量150单 → 落在 [100, 200) 区间 → 首充返10元

5.3 梯度升级

初始状态:
  代理A 销量150适用10元档给A1设置5元
  
  触发时A1得5元A得10-5=5元

升级后A销量达到210
  A 适用20元档A1配置仍为5元
  
  触发时A1得5元不变A得20-5=15元增量归上级

5.4 统计周期

  • 统计周期与一次性佣金时效一致
  • 只统计该套餐系列下的销量/销售额

六、约束规则

6.1 套餐分配

  1. 下级成本价 >= 自己成本价(不能亏本卖)
  2. 只能分配自己有权限的套餐给下级
  3. 只能分配给直属下级(不能跨级)

6.2 一次性佣金分配

  1. 给下级的金额 <= 自己能拿到的金额
  2. 给下级的金额 >= 0可以设为0独吞全部

6.3 流量

  1. 虚流量 <= 真实流量

6.4 配置修改

  1. 修改配置只影响之后的新订单
  2. 代理只能修改"给下级多少钱",不能修改触发规则
  3. 平台修改系列规则不影响已分配的代理,需收回重新分配

6.5 触发限制

  1. 一次性佣金每张卡/设备只触发一次
  2. "首充"指该卡/设备在该系列下的第一次充值
  3. 累计充值只统计"充值"操作,不统计"直接购买"

七、操作流程

7.1 理想的线性流程

1. 创建套餐系列
   └─▶ 可选:配置一次性佣金规则

2. 创建套餐
   └─▶ 归属到系列
   └─▶ 设置成本价、建议售价
   └─▶ 设置真流量(必填)、虚流量(可选)

3. 分配套餐给代理
   └─▶ 设置代理成本价(加价)
   └─▶ 如果系列启用一次性佣金:设置给代理的一次性佣金额度

4. 分配资产(卡/设备)给代理
   └─▶ 资产绑定的套餐系列自动跟着走

5. 代理销售
   └─▶ 客户购买套餐
   └─▶ 差价佣金自动计算并入账给上级
   └─▶ 满足一次性佣金条件时,按链式分配入账

八、与现有代码的差异

详见改造提案:refactor-commission-package-model