"""提现设置与运行时:按俱乐部读 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': '设置修改成功'})