# 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// # 统计某人分佣总额 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'))` |