157 lines
5.2 KiB
Python
157 lines
5.2 KiB
Python
"""模块:分佣策略引擎
|
||
策略模式实现——每种分账方式独立封装,新增分账逻辑只需添加新策略类。
|
||
|
||
支持四种策略:
|
||
PercentageStrategy: 比例分账 → TotalAmount × rate
|
||
FixedStrategy: 固定金额 → 固定值(受保底/封顶约束)
|
||
TieredStrategy: 阶梯分账 → 按次数/档位查表
|
||
RemainderStrategy: 剩余归平台 → TotalAmount - SUM(其他参与者)
|
||
"""
|
||
|
||
from abc import ABC, abstractmethod
|
||
from decimal import Decimal
|
||
from typing import Dict, List, Optional
|
||
from gvsdsdk.commission.models import RateTable
|
||
|
||
|
||
class BaseSplitStrategy(ABC):
|
||
"""分账策略抽象基类"""
|
||
|
||
def __init__(self, participant_rule: dict, rate_table: Optional[RateTable] = None):
|
||
self._rule = participant_rule
|
||
self._rate_table = rate_table
|
||
|
||
@abstractmethod
|
||
def Calculate(self, total_amount: Decimal, tier_index: int = 0, **context) -> Decimal:
|
||
...
|
||
|
||
@property
|
||
def Role(self):
|
||
return self._rule.get('Role', 'unknown')
|
||
|
||
@property
|
||
def SplitType(self):
|
||
return self._rule.get('Type', 'unknown')
|
||
|
||
def _get_rate_from_table(self, key):
|
||
if self._rate_table:
|
||
rate = self._rate_table.GetRate(key)
|
||
if rate is not None:
|
||
return rate
|
||
return self._rule.get('Rate', Decimal('0'))
|
||
|
||
|
||
class PercentageStrategy(BaseSplitStrategy):
|
||
"""比例分账策略
|
||
|
||
配置示例:
|
||
{ role: 'worker', type: 'percentage', value: 0.80 }
|
||
{ role: 'worker', type: 'percentage', rate_table_key: '1' }
|
||
"""
|
||
|
||
def Calculate(self, total_amount: Decimal, tier_index: int = 0, **context) -> Decimal:
|
||
rate_key = self._rule.get('RateTableKey', '')
|
||
if rate_key:
|
||
rate = self._get_rate_from_table(rate_key)
|
||
else:
|
||
rate = Decimal(str(self._rule.get('Value', 0)))
|
||
amount = total_amount * rate
|
||
return self._apply_bounds(amount, total_amount)
|
||
|
||
def _apply_bounds(self, amount, total_amount):
|
||
minimum = self._rule.get('MinAmount')
|
||
maximum = self._rule.get('MaxAmount')
|
||
if minimum is not None:
|
||
amount = max(amount, Decimal(str(minimum)))
|
||
if maximum is not None:
|
||
amount = min(amount, Decimal(str(maximum)))
|
||
return amount
|
||
|
||
|
||
class FixedStrategy(BaseSplitStrategy):
|
||
"""固定金额策略
|
||
|
||
配置示例:
|
||
{ role: 'agent', type: 'fixed', value: 10 }
|
||
{ role: 'leader', type: 'fixed', rate_table_key: 'guanshi_fc' }
|
||
"""
|
||
|
||
def Calculate(self, total_amount: Decimal, tier_index: int = 0, **context) -> Decimal:
|
||
rate_key = self._rule.get('RateTableKey', '')
|
||
if rate_key:
|
||
amount = self._get_rate_from_table(rate_key)
|
||
else:
|
||
amount = Decimal(str(self._rule.get('Value', 0)))
|
||
return amount
|
||
|
||
|
||
class TieredStrategy(BaseSplitStrategy):
|
||
"""阶梯分账策略——按次数/档位查表
|
||
|
||
配置示例:
|
||
{ role: 'agent', type: 'tiered',
|
||
tiers: { 1: 5, 2: 8, 3: 10 } }
|
||
或从费率表中按 key_N 形式查找。
|
||
"""
|
||
|
||
def Calculate(self, total_amount: Decimal, tier_index: int = 0, **context) -> Decimal:
|
||
tiers = self._rule.get('Tiers', {})
|
||
if tiers:
|
||
tier_key = str(tier_index)
|
||
raw_value = tiers.get(tier_key, tiers.get('default', 0))
|
||
if isinstance(raw_value, str) and raw_value.endswith('%'):
|
||
return total_amount * Decimal(raw_value.rstrip('%')) / Decimal('100')
|
||
return Decimal(str(raw_value))
|
||
|
||
tier_key = self._rule.get('TierKeyPrefix', '')
|
||
if tier_key and self._rate_table:
|
||
rate = self._rate_table.GetRate(f'{tier_key}_{tier_index}')
|
||
if rate is not None:
|
||
return rate
|
||
|
||
return Decimal('0')
|
||
|
||
|
||
class RemainderStrategy(BaseSplitStrategy):
|
||
"""剩余归平台策略——扣除所有其他参与者后的余额
|
||
|
||
配置示例:
|
||
{ role: 'platform', type: 'remainder' }
|
||
"""
|
||
|
||
def Calculate(self, total_amount: Decimal, tier_index: int = 0, **context) -> Decimal:
|
||
allocated = context.get('AllocatedAmount', Decimal('0'))
|
||
remainder = total_amount - allocated
|
||
return remainder if remainder > 0 else Decimal('0')
|
||
|
||
|
||
class WithdrawalFeeStrategy(BaseSplitStrategy):
|
||
"""提现手续费策略
|
||
|
||
配置示例:
|
||
{ role: 'platform', type: 'withdrawal_fee', value: 0.01 }
|
||
"""
|
||
|
||
def Calculate(self, total_amount: Decimal, tier_index: int = 0, **context) -> Decimal:
|
||
rate_key = self._rule.get('RateTableKey', '')
|
||
if rate_key:
|
||
rate = self._get_rate_from_table(rate_key)
|
||
else:
|
||
rate = Decimal(str(self._rule.get('Value', 0)))
|
||
return total_amount * rate
|
||
|
||
|
||
STRATEGY_MAP: Dict[str, type] = {
|
||
'percentage': PercentageStrategy,
|
||
'fixed': FixedStrategy,
|
||
'tiered': TieredStrategy,
|
||
'remainder': RemainderStrategy,
|
||
'withdrawal_fee': WithdrawalFeeStrategy,
|
||
}
|
||
|
||
|
||
def CreateStrategy(participant_rule: dict, rate_table: Optional[RateTable] = None) -> BaseSplitStrategy:
|
||
split_type = participant_rule.get('Type', 'percentage')
|
||
strategy_class = STRATEGY_MAP.get(split_type, PercentageStrategy)
|
||
return strategy_class(participant_rule, rate_table)
|