Files
SMANAGE/docs/commission_engine.md
2026-06-16 11:55:14 +08:00

15 KiB
Raw Permalink Blame History

SMANAGE 抽象分佣引擎 — 设计文档

一、设计背景

通过对阿龙电竞平台分佣机制的完整分析,提取出 6 种核心分佣模式: 订单分佣、邀请链分佣、阶梯分佣、提现抽成、罚款分红、跨平台预结算。

这些模式都有一个共同的本质:事件 → 规则 → 分账。SMANAGE 分佣引擎将这一本质抽象为 SAAS 级通用模块,子服务只管说"发生了什么",SAAS 负责"怎么分"。


二、设计原则

原则 说明
事件驱动 子服务触发分佣事件(order_complete / member_purchase / recharge / penalty),引擎自动匹配规则计算分账
规则外置 分账比例、层级、公式全部存储于 CommissionRule 表,零硬编码
策略模式 比例/固定/阶梯/剩余 四种策略独立封装,新增分账逻辑只需添加策略类
参与方可配置 参与者身份和金额由 JSON 配置,适配任意业务场景
邀请链通用化 不管叫"管事/组长"还是"上级/上上级",都是链式分佣
幂等保护 (TenantUUID, EventKey) 联合唯一约束,防止重复分账
快照不可变 CommissionRecord 生成后冻结,规则变更不影响历史记录
与支付解耦 分佣算出"该分多少",支付模块负责"怎么打到账"

三、架构分层

子服务(如阿龙电竞)
    │
    │  订单完成 / 会员购买 / 用户充值 / 罚款缴纳
    │
    └──→ CommissionEngine.Dispatch(event_type, event_key, total_amount, ...)
            │
            ├── 1. 写入 CommissionEvent(幂等检查)
            ├── 2. 匹配 CommissionRule(按优先级)
            ├── 3. 调用策略引擎计算分账
            │       ├── PercentageStrategy  → total × rate
            │       ├── FixedStrategy       → 固定金额
            │       ├── TieredStrategy      → 按次数/档位查表
            │       └── RemainderStrategy   → 剩余归平台
            ├── 4. 写入 CommissionRecord(不可变流水)
            └── 5. 返回分账结果

四、文件清单(10 个文件)

层级 文件 职责
模型 models.py 6 张核心表:Event / Rule / RateTable / RateEntry / Record / PreSettlement
策略 strategies/engine.py 5 种策略类 + 策略工厂 CreateStrategy()
编排器 manager.py CommissionEngine — Dispatch / PreSettle / 邀请链 / 费率计算 / 统计查询
API views.py 10 个 REST 端点
序列化 serializers.py 12 个 Serializer
路由 urls.py 10 个 URL 路由
配置 apps.py AppConfig
管理 admin.py 6 个 Django Admin 注册

五、核心数据模型

5.1 CommissionEvent — 分佣事件

子服务触发的分佣事件记录:

字段 类型 说明
EventUUID UUID 主键
TenantUUID UUID 租户
EventType CharField(32) order_complete / member_purchase / recharge / penalty / register / custom
EventKey CharField(128) 业务唯一键(幂等保护),如订单ID
TotalAmount DecimalField 可分佣总金额
SourceUserUUID UUID 触发者(如付费用户)
EventPayload JSONField 业务上下文(由子服务自行解析)
Participants JSONField 参与者映射 {role: user_id}
EventStatus SmallIntegerField 0=待处理 1=处理中 2=已结算 3=失败

唯一约束:(TenantUUID, EventKey) — 同一事件不可重复分账

5.2 CommissionRule — 分佣规则

定义"怎么分":

字段 类型 说明
RuleUUID UUID 主键
TenantUUID UUID 租户
EventType CharField(32) 绑定的分佣事件类型
RuleName CharField(128) 规则名称
ParticipantRules JSONField 参与者分账规则列表
RateTable ForeignKey 可选关联费率表
MaxInviteLevel SmallIntegerField 邀请链最大层级(默认2级)
RulePriority IntegerField 优先级

ParticipantRules JSON 结构示例

[
    { "role": "worker",   "type": "percentage", "value": 0.80 },
    { "role": "agent",    "type": "fixed",      "value": 10 },
    { "role": "leader",   "type": "tiered",     "tiers": { "1": 5, "2": 8, "3": 10 } },
    { "role": "platform", "type": "remainder" }
]

5.3 RateTable + RateEntry — 费率表

可复用的动态费率配置:

RateTable: { TableCode: 'dashou_order_split', TableName: '打手订单分成费率' }
    ├── RateEntry: { RateKey: '1', RateLabel: '平台订单打手分成', RateValue: 0.8000 }
    ├── RateEntry: { RateKey: '3', RateLabel: '商家订单打手分成', RateValue: 0.7000 }
    ├── RateEntry: { RateKey: '5', RateLabel: '打手提现抽成',     RateValue: 0.0100 }
    └── RateEntry: { RateKey: '6', RateLabel: '管事提现抽成',     RateValue: 0.0100 }

5.4 CommissionRecord — 分佣记录(不可变)

字段 类型 说明
RecordUUID UUID 主键
TenantUUID UUID 租户
Event ForeignKey 关联分佣事件
Rule ForeignKey 关联分佣规则(可空,规则删除后保留快照)
ParticipantID CharField(128) 分账对象ID
ParticipantRole CharField(64) 分账角色(worker / agent / platform / ...)
SplitType CharField(16) percentage / fixed / tiered / remainder
Amount DecimalField 分账金额
RuleSnapshot JSONField 规则快照(规则变更后保留历史)
TierIndex IntegerField 阶梯索引(多次分红时标识第几次)
Status SmallIntegerField 0=待结算 1=已结算 2=已提现 3=已取消

5.5 PreSettlement — 预结算

跨平台订单待确认款项:

字段 类型 说明
EventKey CharField(128) 业务唯一键
ExternalPlatform CharField(64) 外部平台标识
ExternalOrderID CharField(128) 外部订单ID
ParticipantID CharField(128) 应分账对象
Amount DecimalField 预分金额
Status SmallIntegerField 0=待确认 1=已确认 2=已拒绝

六、策略引擎

策略模式实现,四种核心策略 + 一种扩展策略:

6.1 PercentageStrategy — 比例分账

# 配置: { role: 'worker', type: 'percentage', value: 0.80 }
amount = total_amount × 0.80
# 支持保底/封顶:
# { role: 'worker', type: 'percentage', value: 0.80, min_amount: 5, max_amount: 500 }

6.2 FixedStrategy — 固定金额

# 配置: { role: 'agent', type: 'fixed', value: 10 }
amount = 10  # 不受 total_amount 影响
# 也支持从费率表读取:
# { role: 'agent', type: 'fixed', rate_table_key: 'guanshi_fc' }

6.3 TieredStrategy — 阶梯分账

# 配置: { role: 'agent', type: 'tiered', tiers: { "1": 5, "2": 8, "3": 10 } }
# tier_index=1 → amount=5
# tier_index=2 → amount=8
# tier_index=3 → amount=10
# 支持百分比阶梯: { "1": "5%", "2": "8%" }

6.4 RemainderStrategy — 剩余归平台

# 配置: { role: 'platform', type: 'remainder' }
amount = total_amount - SUM(其他所有参与者分账)
# 自动排在最后计算,确保剩余金额正确

6.5 WithdrawalFeeStrategy — 提现手续费(扩展)

# 配置: { role: 'platform', type: 'withdrawal_fee', value: 0.01 }
fee = total_amount × 0.01

新增策略只需两步

# 1. 实现策略类
class FormulaStrategy(BaseSplitStrategy):
    def Calculate(self, total_amount, tier_index=0, **context):
        return total_amount * Decimal('0.5') + Decimal('10')

# 2. 注册到策略映射
STRATEGY_MAP['formula'] = FormulaStrategy

七、CommissionEngine 编排器

class CommissionEngine:
    def __init__(self, tenant_uuid)

    # 规则管理
    def ConfigureRule(event_type, rule_name, participant_rules, ...)     # 配置分佣规则
    def ConfigureRateTable(table_code, table_name, entries, ...)          # 配置费率表

    # 核心分佣
    def Dispatch(event_type, event_key, total_amount, participants, ...)    # 触发分佣
    def BuildInviteChainCommission(event_type, event_key, ...)               # 邀请链分佣

    # 预结算
    def PreSettle(event_key, external_platform, external_order_id, ...)      # 创建预结算
    def ConfirmPreSettlement(event_key, participant_id, ...)                 # 确认
    def RejectPreSettlement(event_key, participant_id, ...)                  # 拒绝

    # 查询
    def CalculateFee(total_amount, rate_table_code, rate_key)               # 计算手续费
    def QueryRecords(participant_id, event_type, status, ...)               # 查询分佣记录
    def TotalByParticipant(participant_id, start_date, end_date)           # 统计总额

八、API 端点

# 规则管理
GET    /api/commission/rule/              # 查询分佣规则列表
POST   /api/commission/rule/              # 创建分佣规则
DELETE /api/commission/rule/              # 禁用分佣规则

# 费率表管理
GET    /api/commission/rate/              # 查询费率表列表
POST   /api/commission/rate/              # 创建费率表

# 分佣操作
POST   /api/commission/dispatch/          # 触发分佣事件
POST   /api/commission/dispatch/invite_chain/  # 邀请链分佣

# 查询统计
GET    /api/commission/query/             # 查询分佣记录
GET    /api/commission/total/<participant_id>/  # 统计某人分佣总额
POST   /api/commission/fee/               # 计算手续费

# 预结算
POST   /api/commission/presettle/         # 创建预结算
POST   /api/commission/presettle/confirm/ # 确认预结算

# 事件查询
GET    /api/commission/event/             # 查询分佣事件列表

九、子服务集成示例

9.1 订单分佣

from gvsds.apps.commission.manager import CommissionEngine

engine = CommissionEngine(tenant_uuid='al_long_001')

# 配置分佣规则
engine.ConfigureRule(
    event_type='order_complete',
    rule_name='平台订单打手8成',
    participant_rules=[
        {'role': 'worker',   'type': 'percentage', 'value': 0.80},
        {'role': 'platform', 'type': 'remainder'},
    ],
)

# 订单完成时触发分佣
records = engine.Dispatch(
    event_type='order_complete',
    event_key='ORDER-20260611-001',    # 幂等键
    total_amount=Decimal('100.00'),
    participants={
        'worker': 'dashou_123',
    },
    payload={'order_id': 'xxx', 'game': 'LoL', 'shop_id': 'shop_001'},
)

# records = [
#   {ParticipantID: 'dashou_123', ParticipantRole: 'worker',   Amount: 80},
#   {ParticipantID: 'platform_default', ParticipantRole: 'platform', Amount: 20},
# ]

9.2 会员购买+邀请链

# 配置会员购买分佣规则
engine.ConfigureRule(
    event_type='member_purchase',
    rule_name='会员购买管事+组长分红',
    participant_rules=[
        {'role': 'agent',  'type': 'fixed', 'value': 10},
        {'role': 'leader', 'type': 'fixed', 'value': 5},
        {'role': 'platform', 'type': 'remainder'},
    ],
)

# 打手购买会员 → 触发邀请链分佣
records = engine.BuildInviteChainCommission(
    event_type='member_purchase',
    event_key='MEMBER-VIP001-20260611-001',
    total_amount=Decimal('50.00'),
    source_user_uuid='dashou_123',
    invite_chain=[
        {'user_uuid': 'guanshi_456', 'level': 1, 'role': 'agent'},
        {'user_uuid': 'zuzhang_789', 'level': 2, 'role': 'leader'},
    ],
    rule_name='会员购买管事+组长分红',
    participant_rules=[
        {'role': 'agent',  'type': 'fixed', 'value': 10},
        {'role': 'leader', 'type': 'fixed', 'value': 5},
        {'role': 'platform', 'type': 'remainder'},
    ],
)

# records = [
#   {ParticipantID: 'guanshi_456', ParticipantRole: 'agent',    Amount: 10},
#   {ParticipantID: 'zuzhang_789', ParticipantRole: 'leader',   Amount: 5},
#   {ParticipantID: 'platform_default', ParticipantRole: 'platform', Amount: 35},
# ]

9.3 阶梯分佣(多次购买)

# 第一次购买
engine.Dispatch(
    event_type='member_purchase',
    event_key='STAGED-VIP001-DASHOU_123-1',
    total_amount=Decimal('50.00'),
    tier_index=1,
    participants={'agent': 'guanshi_456'},
)

# 第二次购买(金额不同)
engine.Dispatch(
    event_type='member_purchase',
    event_key='STAGED-VIP001-DASHOU_123-2',
    total_amount=Decimal('50.00'),
    tier_index=2,
    participants={'agent': 'guanshi_456'},
)

9.4 提现手续费

# 配置提现费率表
engine.ConfigureRateTable(
    table_code='withdrawal_fee',
    table_name='提现手续费率',
    entries={
        'dashou':  '0.0100',   # 打手提现 1%
        'guanshi': '0.0100',   # 管事提现 1%
        'zuzhang': '0.0100',   # 组长提现 1%
    },
)

# 计算手续费
fee = engine.CalculateFee(
    total_amount=Decimal('1000.00'),
    rate_table_code='withdrawal_fee',
    rate_key='dashou',
)
# fee = 10.00  → 实际到账 990.00

9.5 跨平台预结算

# 创建预结算
ps = engine.PreSettle(
    event_key='ORDER-CROSS-001',
    external_platform='partner_club_002',
    external_order_id='P-ORDER-888',
    participant_id='dashou_123',
    amount=Decimal('80.00'),
    order_amount=Decimal('100.00'),
)

# 对方确认后
engine.ConfirmPreSettlement(
    event_key='ORDER-CROSS-001',
    confirmed_by='partner_admin',
)

十、与支付模块的关系

┌────────────────────────────────────────────────────────────┐
│                      子服务业务层                           │
│                                                             │
│  订单完成 ──→ CommissionEngine.Dispatch() ──→ 算出分账     │
│                                                     │       │
│                                                     ▼       │
│  用户提现 ──→ PaymentManager.Transfer() ──→ 执行打款       │
│                                                             │
└────────────────────────────────────────────────────────────┘

分佣引擎 → 算出来"该分多少"
支付网关 → 执行"怎么打到账"

两者通过 CommissionRecord.Amount 桥接:
  · CommissionRecord 中的分账金额作为 PaymentManager.Transfer 的输入
  · 打款完成后,CommissionRecord 状态从 'settled' → 'withdrawn'

十一、配置变更记录

文件 变更
settings.py 新增 'gvsds.apps.commission' 到 INSTALLED_APPS
db_router.py 新增 'commission': 'default' 到 app_db_map
urls.py 新增 path('api/commission/', include('gvsds.apps.commission.urls'))