268 lines
11 KiB
Python
268 lines
11 KiB
Python
"""提现设置与运行时:按俱乐部读 club_lilubiao / club_tixian_quota / club_withdraw_config。"""
|
||
from datetime import date
|
||
from decimal import Decimal
|
||
|
||
from django.db import transaction
|
||
from rest_framework.response import Response
|
||
|
||
from backend.models import WithdrawalDailyStats
|
||
from config.models import TixianQuotaDefault
|
||
from jituan.constants import CLUB_ID_DEFAULT, DATA_SCOPE_ALL
|
||
from jituan.models import ClubLilubiao, ClubTixianQuota, ClubWithdrawConfig
|
||
from jituan.services.club_config import get_commission_rate
|
||
from jituan.services.club_context import resolve_club_id_from_request, resolve_club_scope
|
||
|
||
# 提现手续费率 → lilubiao config_type / CommissionRate Platform
|
||
LEIXING_RATE_KEY = {
|
||
1: '5', # 打手佣金
|
||
2: '6', # 管事分红
|
||
3: '8', # 组长分红
|
||
4: '9', # 考核官
|
||
5: '11', # 打手押金
|
||
6: '10', # 商家余额
|
||
}
|
||
|
||
# 提现身份 leixing → 平台日总限额配置 leixing(club_tixian_quota / TixianQuotaDefault UserType)
|
||
WITHDRAW_TOTAL_QUOTA_LEIXING = {1: 4, 2: 5, 3: 6, 4: 7, 5: 8, 6: 9}
|
||
|
||
|
||
def _normalize_club_id(club_id):
|
||
return (club_id or '').strip() or CLUB_ID_DEFAULT
|
||
|
||
|
||
def _club_id(request):
|
||
return resolve_club_id_from_request(request)
|
||
|
||
|
||
def _get_quota(club_id, leixing, default=20.0):
|
||
club_id = _normalize_club_id(club_id)
|
||
row = ClubTixianQuota.query.filter(club_id=club_id, leixing=leixing).first()
|
||
if row is not None:
|
||
return float(row.daily_limit)
|
||
obj = TixianQuotaDefault.query.filter(UserType=leixing).first()
|
||
return float(obj.default_quota) if obj else default
|
||
|
||
|
||
def get_quota_decimal(club_id, leixing, default=20.0):
|
||
return Decimal(str(_get_quota(club_id, leixing, default)))
|
||
|
||
|
||
def _set_quota(club_id, leixing, quota):
|
||
row, created = ClubTixianQuota.query.get_or_create(
|
||
club_id=_normalize_club_id(club_id),
|
||
leixing=leixing,
|
||
defaults={'daily_limit': quota},
|
||
)
|
||
if not created:
|
||
row.daily_limit = quota
|
||
row.save(update_fields=['daily_limit', 'UpdateTime'])
|
||
|
||
|
||
def _set_lilv(club_id, config_type, rate):
|
||
ct = int(config_type)
|
||
row, created = ClubLilubiao.query.get_or_create(
|
||
club_id=_normalize_club_id(club_id),
|
||
config_type=ct,
|
||
defaults={'lilv': rate, 'remark': ''},
|
||
)
|
||
if not created:
|
||
row.lilv = rate
|
||
row.save(update_fields=['lilv', 'UpdateTime'])
|
||
|
||
|
||
def get_tixian_feilv(club_id, leixing):
|
||
"""读取提现手续费率(各俱乐部可不同)。"""
|
||
rate_key = LEIXING_RATE_KEY.get(leixing)
|
||
if not rate_key:
|
||
raise ValueError('无效的提现类型')
|
||
return get_commission_rate(_normalize_club_id(club_id), rate_key)
|
||
|
||
|
||
def get_withdraw_mode(club_id):
|
||
club_id = _normalize_club_id(club_id)
|
||
try:
|
||
return ClubWithdrawConfig.query.get(club_id=club_id).mode
|
||
except ClubWithdrawConfig.DoesNotExist:
|
||
return 2
|
||
|
||
|
||
def get_personal_daily_quota(club_id, leixing, profile):
|
||
"""打手/管事/组长个人每日限额:用户额外限额优先,否则读俱乐部配置。"""
|
||
if leixing == 1:
|
||
if profile.kaioi_ewai_xiane and profile.ewai_xiane > 0:
|
||
return Decimal(str(profile.ewai_xiane))
|
||
return get_quota_decimal(club_id, 1)
|
||
if leixing == 2:
|
||
if profile.kaioi_ewai_xiane and profile.ewai_xiane > 0:
|
||
return Decimal(str(profile.ewai_xiane))
|
||
return get_quota_decimal(club_id, 2)
|
||
if leixing == 3:
|
||
default = get_quota_decimal(club_id, 3, default=0.0)
|
||
if profile.kaioi_ewai_tixian and profile.ewai_tixian_xiane > 0:
|
||
return Decimal(str(profile.ewai_tixian_xiane))
|
||
return default
|
||
raise ValueError('无效的个人限额身份')
|
||
|
||
|
||
def get_platform_daily_limit(club_id, leixing):
|
||
quota_leixing = WITHDRAW_TOTAL_QUOTA_LEIXING.get(leixing)
|
||
if not quota_leixing:
|
||
return Decimal('0.00')
|
||
return get_quota_decimal(club_id, quota_leixing, default=0.0)
|
||
|
||
|
||
def get_platform_daily_total(club_id, leixing, today=None):
|
||
"""平台今日已提现总额(到账金额口径,按俱乐部)。"""
|
||
club_id = _normalize_club_id(club_id)
|
||
today = today or date.today()
|
||
rec = WithdrawalDailyStats.query.filter(
|
||
club_id=club_id, Date=today, WithdrawalType=leixing,
|
||
).first()
|
||
if rec:
|
||
return Decimal(str(rec.total_amount))
|
||
return Decimal('0.00')
|
||
|
||
|
||
def get_or_create_platform_daily_stat(club_id, leixing, today=None):
|
||
"""在 transaction.atomic 内获取并锁定平台日统计行。"""
|
||
club_id = _normalize_club_id(club_id)
|
||
today = today or date.today()
|
||
stat, _ = WithdrawalDailyStats.objects.select_for_update().get_or_create(
|
||
club_id=club_id,
|
||
Date=today,
|
||
WithdrawalType=leixing,
|
||
defaults={'total_amount': Decimal('0.00')},
|
||
)
|
||
return stat
|
||
|
||
|
||
def build_tixian_asset_rates(club_id):
|
||
club_id = _normalize_club_id(club_id)
|
||
return {
|
||
'dashou_rate': float(get_commission_rate(club_id, '5')),
|
||
'dashou_yajin_rate': float(get_commission_rate(club_id, '11')),
|
||
'shangjia_rate': float(get_commission_rate(club_id, '10')),
|
||
'guanshi_rate': float(get_commission_rate(club_id, '6')),
|
||
'zuzhang_rate': float(get_commission_rate(club_id, '8')),
|
||
'kaoheguan_rate': float(get_commission_rate(club_id, '9')),
|
||
}
|
||
|
||
|
||
def build_withdraw_settings_payload(request):
|
||
club_id = _club_id(request)
|
||
rate_map = {}
|
||
for leixing, code in [(1, '5'), (2, '6'), (3, '8'), (4, '9'), (5, '11'), (6, '10')]:
|
||
rate_map[leixing] = float(get_commission_rate(club_id, code))
|
||
|
||
order_rate_map = {}
|
||
for leixing, code in [(1, '1'), (3, '3'), (12, '12'), (13, '13')]:
|
||
order_rate_map[leixing] = float(get_commission_rate(club_id, code))
|
||
|
||
quota_map = {leixing: _get_quota(club_id, leixing) for leixing in [1, 2, 3]}
|
||
|
||
total_limit_map = {}
|
||
for withdraw_leixing, quota_leixing in [(1, 4), (2, 5), (3, 6), (4, 7), (5, 8), (6, 9)]:
|
||
total_limit_map[withdraw_leixing] = _get_quota(club_id, quota_leixing, default=0.0)
|
||
|
||
today = date.today()
|
||
today_totals = {i: 0.0 for i in range(1, 7)}
|
||
for rec in WithdrawalDailyStats.query.filter(club_id=club_id, Date=today):
|
||
if rec.WithdrawalType in today_totals:
|
||
today_totals[rec.WithdrawalType] = float(rec.total_amount)
|
||
|
||
return {
|
||
'club_id': club_id,
|
||
'scope': resolve_club_scope(request),
|
||
'withdraw_mode': get_withdraw_mode(club_id),
|
||
'rates': rate_map,
|
||
'order_rates': order_rate_map,
|
||
'quotas': quota_map,
|
||
'total_limits': total_limit_map,
|
||
'today_totals': today_totals,
|
||
}
|
||
|
||
|
||
def apply_withdraw_settings_update(request, permissions):
|
||
if resolve_club_scope(request) == DATA_SCOPE_ALL:
|
||
return Response({'code': 403, 'msg': '请在子公司视图下修改提现设置'})
|
||
|
||
club_id = _club_id(request)
|
||
rates = request.data.get('rates', {})
|
||
order_rates = request.data.get('order_rates', request.data.get('commission_rates', {}))
|
||
quotas = request.data.get('quotas', {})
|
||
total_limits = request.data.get('total_limits', {})
|
||
|
||
role_perms = {1: '5500a', 2: '5500b', 3: '5500c'}
|
||
rate_code_map = {1: '5', 2: '6', 3: '8', 4: '9', 5: '11', 6: '10'}
|
||
order_rate_code_map = {1: '1', 3: '3', 12: '12', 13: '13'}
|
||
total_code_map = {1: 4, 2: 5, 3: 6, 4: 7, 5: 8, 6: 9}
|
||
|
||
def _rate_key_present(data, role):
|
||
return (str(role) in data and data[str(role)] is not None) or \
|
||
(role in data and data[role] is not None)
|
||
|
||
with transaction.atomic():
|
||
for role in [1, 2, 3]:
|
||
perm_needed = role_perms[role]
|
||
if perm_needed not in permissions:
|
||
if _rate_key_present(rates, role) or \
|
||
(str(role) in quotas and quotas[str(role)] is not None) or \
|
||
(role in quotas and quotas.get(role) is not None) or \
|
||
(str(role) in total_limits and total_limits[str(role)] is not None) or \
|
||
(role in total_limits and total_limits.get(role) is not None):
|
||
return Response({'code': 403, 'msg': f'无权限修改{["","打手","管事","组长"][role]}相关设置(需要{perm_needed})'})
|
||
|
||
extra_role_names = {4: '考核官', 5: '打手押金', 6: '商家余额'}
|
||
for role in [4, 5, 6]:
|
||
if _rate_key_present(rates, role) and not any(p in permissions for p in ('5500a', '5500b', '5500c')):
|
||
return Response({'code': 403, 'msg': f'无权限修改{extra_role_names[role]}提现手续费率'})
|
||
if (str(role) in total_limits and total_limits[str(role)] is not None) or \
|
||
(role in total_limits and total_limits.get(role) is not None):
|
||
if not any(p in permissions for p in ('5500a', '5500b', '5500c')):
|
||
return Response({'code': 403, 'msg': f'无权限修改{extra_role_names[role]}总限额'})
|
||
|
||
for role in [1, 3, 12, 13]:
|
||
if _rate_key_present(order_rates, role) and not any(p in permissions for p in ('5500a', '5500b', '5500c')):
|
||
order_names = {
|
||
1: '平台订单打手分红', 3: '商家订单打手分红',
|
||
12: '押金管事分红', 13: '管事订单分红',
|
||
}
|
||
return Response({'code': 403, 'msg': f'无权限修改{order_names[role]}费率'})
|
||
|
||
for role in [1, 2, 3, 4, 5, 6]:
|
||
val = rates.get(str(role), rates.get(role))
|
||
if val is not None:
|
||
_set_lilv(club_id, rate_code_map[role], Decimal(str(val)))
|
||
|
||
for role in [1, 3, 12, 13]:
|
||
val = order_rates.get(str(role), order_rates.get(role))
|
||
if val is not None:
|
||
_set_lilv(club_id, order_rate_code_map[role], Decimal(str(val)))
|
||
|
||
for role in [1, 2, 3]:
|
||
if str(role) in quotas:
|
||
val = quotas[str(role)]
|
||
if val is not None:
|
||
_set_quota(club_id, role, Decimal(str(val)))
|
||
elif role in quotas and quotas.get(role) is not None:
|
||
_set_quota(club_id, role, Decimal(str(quotas[role])))
|
||
|
||
for role in [1, 2, 3, 4, 5, 6]:
|
||
val = total_limits.get(str(role), total_limits.get(role))
|
||
if val is not None:
|
||
_set_quota(club_id, total_code_map[role], Decimal(str(val)))
|
||
|
||
withdraw_mode = request.data.get('withdraw_mode')
|
||
if withdraw_mode is not None:
|
||
if not any(p in permissions for p in ('5500a', '5500b', '5500c')):
|
||
return Response({'code': 403, 'msg': '无权限修改提现模式'})
|
||
row, created = ClubWithdrawConfig.query.get_or_create(
|
||
club_id=club_id,
|
||
defaults={'mode': int(withdraw_mode)},
|
||
)
|
||
if not created:
|
||
row.mode = int(withdraw_mode)
|
||
row.save(update_fields=['mode', 'UpdateTime'])
|
||
|
||
return Response({'code': 0, 'msg': '设置修改成功'})
|