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

443 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 结构示例
```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 — 比例分账
```python
# 配置: { 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 — 固定金额
```python
# 配置: { role: 'agent', type: 'fixed', value: 10 }
amount = 10 # 不受 total_amount 影响
# 也支持从费率表读取:
# { role: 'agent', type: 'fixed', rate_table_key: 'guanshi_fc' }
```
### 6.3 TieredStrategy — 阶梯分账
```python
# 配置: { 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 — 剩余归平台
```python
# 配置: { role: 'platform', type: 'remainder' }
amount = total_amount - SUM(其他所有参与者分账)
# 自动排在最后计算,确保剩余金额正确
```
### 6.5 WithdrawalFeeStrategy — 提现手续费(扩展)
```python
# 配置: { role: 'platform', type: 'withdrawal_fee', value: 0.01 }
fee = total_amount × 0.01
```
### 新增策略只需两步
```python
# 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 编排器
```python
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 订单分佣
```python
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 会员购买+邀请链
```python
# 配置会员购买分佣规则
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 阶梯分佣(多次购买)
```python
# 第一次购买
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 提现手续费
```python
# 配置提现费率表
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 跨平台预结算
```python
# 创建预结算
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'))` |