From dc71c4f1cbd00cee32a9b62cf3ea1c1150b96abc Mon Sep 17 00:00:00 2001 From: XingQue Date: Mon, 29 Jun 2026 01:30:56 +0800 Subject: [PATCH] feat: gate withdraw audit and add-balance to group executives only Co-authored-by: Cursor --- backend/view.py | 16 +++++++++ jituan/services/group_finance_gate.py | 50 +++++++++++++++++++++++++++ users/views.py | 24 ++++++++++--- 3 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 jituan/services/group_finance_gate.py diff --git a/backend/view.py b/backend/view.py index 1590d50..941e739 100644 --- a/backend/view.py +++ b/backend/view.py @@ -1117,6 +1117,10 @@ class KefuUpdateDashouView(APIView): elif caozuo == 'jia_yue': if '001bb' not in permissions: return Response({'code': 403, 'msg': '无权限加打手余额'}) + from jituan.services.group_finance_gate import deny_group_finance_exec + deny = deny_group_finance_exec(request.user, request) + if deny: + return deny amount = Decimal(str(value)) dashou_profile.yue += amount dashou_profile.save() @@ -1637,6 +1641,10 @@ class KefuUpdateShangjiaView(APIView): # 增加余额 if action == 'add_balance': + from jituan.services.group_finance_gate import deny_group_finance_exec + deny = deny_group_finance_exec(request.user, request) + if deny: + return deny amount_str = request.data.get('amount') try: amount = Decimal(str(amount_str)) @@ -3527,6 +3535,10 @@ class UpdateGuanliView(APIView): if new_yue > old_yue: if '4400c' not in permissions: return Response({'code': 403, 'msg': '无权限增加余额(需要4400c)'}) + from jituan.services.group_finance_gate import deny_group_finance_exec + deny = deny_group_finance_exec(request.user, request) + if deny: + return deny action_desc = f'增加{new_yue - old_yue}元' elif new_yue < old_yue: if '4400b' not in permissions: @@ -4147,6 +4159,10 @@ class UpdateZuzhangView(APIView): if new_val > old_ketixian_jine: if '6600a' not in permissions: return Response({'code': 403, 'msg': '无权限增加可提现金额(需要6600a)'}) + from jituan.services.group_finance_gate import deny_group_finance_exec + deny = deny_group_finance_exec(request.user, request) + if deny: + return deny action_desc = f'增加{new_val - old_ketixian_jine}元' elif new_val < old_ketixian_jine: if '6600b' not in permissions: diff --git a/jituan/services/group_finance_gate.py b/jituan/services/group_finance_gate.py new file mode 100644 index 0000000..1e22d14 --- /dev/null +++ b/jituan/services/group_finance_gate.py @@ -0,0 +1,50 @@ +"""集团级资金处置权限:提现审核、加余额等。俱乐部 000001 超级管理不能绕过。""" +from django.db.models import Q + +from jituan.constants import DATA_SCOPE_ALL +from jituan.models import AdminAssignment +from jituan.services.admin_context import GROUP_MANAGE_ROLES, build_admin_club_context, is_system_super_admin + +MSG_GROUP_FINANCE_REQUIRED = '该操作仅集团高管可执行(需在数据范围配置中为集团级任职)' + + +def has_group_finance_exec(user, request=None): + """系统超管 / 集团任职(GROUP_OWNER、GROUP_SUPER_ADMIN)。""" + if is_system_super_admin(user): + return True + ctx = build_admin_club_context(user) + if ctx.get('is_group_admin') and ctx.get('role_code') in GROUP_MANAGE_ROLES: + return True + uid = getattr(user, 'UserUID', None) + if not uid: + return False + return AdminAssignment.query.filter( + yonghuid=uid, + status=1, + role_code__in=GROUP_MANAGE_ROLES, + ).filter( + Q(club_id__isnull=True) | Q(data_scope=DATA_SCOPE_ALL), + ).exists() + + +def deny_group_finance_exec(user, request=None): + from rest_framework.response import Response + if has_group_finance_exec(user, request): + return None + return Response({'code': 403, 'msg': MSG_GROUP_FINANCE_REQUIRED}) + + +def require_withdraw_audit_access(request, phone): + """提现审核:须 005bb 且集团资金处置权。""" + from backend.utils import verify_kefu_permission + from rest_framework.response import Response + + kefu, permissions = verify_kefu_permission(request, phone) + if kefu is None: + return None, permissions + if '005bb' not in permissions: + return None, Response({'code': 403, 'msg': '您无权操作提现审核'}) + deny = deny_group_finance_exec(request.user, request) + if deny: + return None, deny + return kefu, None diff --git a/users/views.py b/users/views.py index 64c6942..df9a465 100644 --- a/users/views.py +++ b/users/views.py @@ -8793,6 +8793,10 @@ class KefuUpdateDashouView(APIView): elif caozuo == 'jia_yue': if '001bb' not in permissions: return Response({'code': 403, 'msg': '无权限加打手余额'}) + from jituan.services.group_finance_gate import deny_group_finance_exec + deny = deny_group_finance_exec(request.user, request) + if deny: + return deny amount = Decimal(str(value)) dashou_profile.yue += amount dashou_profile.save() @@ -9776,6 +9780,8 @@ class KefuWithdrawListView(APIView): return 1 def post(self, request): + from jituan.services.group_finance_gate import require_withdraw_audit_access + dakuan_mode_filter = request.data.get('dakuan_mode') req_params = self._safe_request_params(request) kefu_txsh_list_logger.info('kefu_txsh_list 请求开始 params=%s', req_params) @@ -9804,11 +9810,9 @@ class KefuWithdrawListView(APIView): if not phone: return Response({'code': 401, 'msg': '缺少客服手机号'}, status=status.HTTP_401_UNAUTHORIZED) - kefu, permissions = verify_kefu_permission(request, phone) - if kefu is None: - return permissions - if '005bb' not in permissions: - return Response({'code': 403, 'msg': '您无权查看提现审核列表'}) + kefu, err_resp = require_withdraw_audit_access(request, phone) + if err_resp: + return err_resp q = Q() @@ -10029,6 +10033,11 @@ class KefuWithdrawDetailView(APIView): logger.warning(f"客服账号已禁用: {current_user.UserUID}") return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED) + from jituan.services.group_finance_gate import require_withdraw_audit_access + _, err_resp = require_withdraw_audit_access(request, phone) + if err_resp: + return err_resp + # 查询提现记录 try: record = Tixianjilu.query.get(id=tixian_id) @@ -10327,6 +10336,11 @@ class KefuWithdrawActionView(APIView): if err_resp: return err_resp + from jituan.services.group_finance_gate import require_withdraw_audit_access + _, audit_err = require_withdraw_audit_access(request, phone) + if audit_err: + return audit_err + items, err_resp = self._parse_items(request) if err_resp: return err_resp