From 96f1a80395647c197e1b7b08f9a922a9a9e8a602 Mon Sep 17 00:00:00 2001 From: XingQue Date: Mon, 27 Jul 2026 07:53:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=B1=E4=B9=90=E9=83=A8=E8=B6=85?= =?UTF-8?q?=E7=BA=A7=E7=AE=A1=E7=90=86=E5=8F=AF=E5=8A=A0=E6=9C=AC=E5=BA=97?= =?UTF-8?q?=E4=BD=99=E9=A2=9D=EF=BC=8C=E4=B8=8D=E5=86=8D=E5=BC=BA=E5=88=B6?= =?UTF-8?q?=E9=9B=86=E5=9B=A2=E9=AB=98=E7=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 持有000001且处于当前俱乐部视图时可加余额/押金/可提现;强制目标用户同俱乐部,并在修改记录中写入俱乐部ID。 Co-authored-by: Cursor --- backend/views/guanli.py | 10 +++-- backend/views/kefu.py | 33 ++++++++++---- backend/views/zuzhang.py | 10 +++-- jituan/services/group_finance_gate.py | 65 +++++++++++++++++++++++++-- users/views/kefu_dashou.py | 12 +++-- 5 files changed, 107 insertions(+), 23 deletions(-) diff --git a/backend/views/guanli.py b/backend/views/guanli.py index 4cfa3b7..caae3f7 100644 --- a/backend/views/guanli.py +++ b/backend/views/guanli.py @@ -636,8 +636,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) + from jituan.services.group_finance_gate import deny_balance_add_exec + deny = deny_balance_add_exec( + request, permissions=permissions, target_user=user, + ) if deny: return deny action_desc = f'增加{new_yue - old_yue}元' @@ -650,6 +652,8 @@ class UpdateGuanliView(APIView): if new_yue != old_yue: guanshi.yue = new_yue guanshi.save() + from jituan.services.club_context import resolve_club_id_from_request + club_id = resolve_club_id_from_request(request) or '' write_xiugai_log( yonghuid=yonghuid, xiugaiid=operator, @@ -658,7 +662,7 @@ class UpdateGuanliView(APIView): guanshiyueq=old_yue, qitashuoming=( f'【后台】修改管事可提现余额:{old_yue}元 → {new_yue}元({action_desc}),' - f'管事ID={yonghuid},操作人={operator}' + f'管事ID={yonghuid},俱乐部={club_id},操作人={operator}' ), ) diff --git a/backend/views/kefu.py b/backend/views/kefu.py index 873ce79..a4e1383 100644 --- a/backend/views/kefu.py +++ b/backend/views/kefu.py @@ -468,14 +468,18 @@ 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) + from jituan.services.group_finance_gate import deny_balance_add_exec + from jituan.services.club_context import resolve_club_id_from_request + deny = deny_balance_add_exec( + request, permissions=permissions, target_user=dashou_user, + ) if deny: return deny amount = Decimal(str(value)) dashou_profile.yue += amount dashou_profile.save() after = dashou_profile.yue + club_id = resolve_club_id_from_request(request) or '' write_xiugai_log( yonghuid=dashou_id, xiugaiid=kefu.user.Phone, @@ -484,7 +488,8 @@ class KefuUpdateDashouView(APIView): xiugaitijiaoq=before['yue'], qitashuoming=( f'【后台】增加打手可提现余额:{before["yue"]}元 → {after}元,' - f'增加{amount}元,打手ID={dashou_id},操作人={kefu.user.Phone}' + f'增加{amount}元,打手ID={dashou_id},俱乐部={club_id},' + f'操作人={kefu.user.Phone}' ), ) return Response({'code': 0, 'msg': '加余额成功', 'data': {'new_yue': str(after)}}) @@ -515,14 +520,18 @@ class KefuUpdateDashouView(APIView): elif caozuo == 'jia_yajin': 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) + from jituan.services.group_finance_gate import deny_balance_add_exec + from jituan.services.club_context import resolve_club_id_from_request + deny = deny_balance_add_exec( + request, permissions=permissions, target_user=dashou_user, + ) if deny: return deny amount = Decimal(str(value)) dashou_profile.yajin += amount dashou_profile.save() after = dashou_profile.yajin + club_id = resolve_club_id_from_request(request) or '' write_xiugai_log( yonghuid=dashou_id, xiugaiid=kefu.user.Phone, @@ -531,7 +540,8 @@ class KefuUpdateDashouView(APIView): yyajin=before['yajin'], qitashuoming=( f'【后台】增加打手押金:{before["yajin"]}元 → {after}元,' - f'增加{amount}元,打手ID={dashou_id},操作人={kefu.user.Phone}' + f'增加{amount}元,打手ID={dashou_id},俱乐部={club_id},' + f'操作人={kefu.user.Phone}' ), ) return Response({'code': 0, 'msg': '加押金成功', 'data': {'new_yajin': str(after)}}) @@ -1012,8 +1022,11 @@ 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) + from jituan.services.group_finance_gate import deny_balance_add_exec + from jituan.services.club_context import resolve_club_id_from_request + deny = deny_balance_add_exec( + request, permissions=permissions, target_user=shangjia.user, + ) if deny: return deny amount_str = request.data.get('amount') @@ -1029,6 +1042,7 @@ class KefuUpdateShangjiaView(APIView): return Response({'code': 400, 'msg': '增加后余额超出上限'}) shangjia.yue = new_yue shangjia.save(update_fields=['yue']) + club_id = resolve_club_id_from_request(request) or '' write_xiugai_log( yonghuid=yonghuid, xiugaiid=kefu.user.Phone, @@ -1037,7 +1051,8 @@ class KefuUpdateShangjiaView(APIView): shangjiayueq=old_yue, qitashuoming=( f'【后台】增加商家余额:{old_yue}元 → {new_yue}元,增加{amount}元,' - f'商家ID={yonghuid},昵称={shangjia.nicheng},操作人={kefu.user.Phone}' + f'商家ID={yonghuid},昵称={shangjia.nicheng},俱乐部={club_id},' + f'操作人={kefu.user.Phone}' ), ) return Response({'code': 0, 'msg': '余额增加成功', 'data': {'new_balance': str(shangjia.yue)}}) diff --git a/backend/views/zuzhang.py b/backend/views/zuzhang.py index 03b9e22..acafcf0 100644 --- a/backend/views/zuzhang.py +++ b/backend/views/zuzhang.py @@ -505,8 +505,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) + from jituan.services.group_finance_gate import deny_balance_add_exec + deny = deny_balance_add_exec( + request, permissions=permissions, target_user=user, + ) if deny: return deny action_desc = f'增加{new_val - old_ketixian_jine}元' @@ -519,6 +521,8 @@ class UpdateZuzhangView(APIView): if new_val != old_ketixian_jine: zuzhang.ketixian_jine = new_val zuzhang.save(update_fields=['ketixian_jine']) + from jituan.services.club_context import resolve_club_id_from_request + club_id = resolve_club_id_from_request(request) or '' write_xiugai_log( yonghuid=yonghuid, xiugaiid=operator, @@ -527,7 +531,7 @@ class UpdateZuzhangView(APIView): xiugaitijiaoq=old_ketixian_jine, qitashuoming=( f'【后台】修改组长可提现金额:{old_ketixian_jine}元 → {new_val}元({action_desc}),' - f'组长ID={yonghuid},操作人={operator}' + f'组长ID={yonghuid},俱乐部={club_id},操作人={operator}' ), ) diff --git a/jituan/services/group_finance_gate.py b/jituan/services/group_finance_gate.py index d038dd9..150d7a5 100644 --- a/jituan/services/group_finance_gate.py +++ b/jituan/services/group_finance_gate.py @@ -1,9 +1,13 @@ -"""集团级资金处置权限:加余额等仍仅集团高管。 +"""集团/俱乐部资金处置权限。 + +加余额(及同类增加资金): +- 系统超管 / 集团高管(GROUP_OWNER、GROUP_SUPER_ADMIN):可按数据范围操作 +- 当前俱乐部超级管理 000001:仅限 SINGLE_CLUB 本店,且目标用户必须属于当前俱乐部 +不再强制「仅集团高管才能加余额」。 提现审核:权限码 005bb + 按俱乐部隔离—— -- 子公司视图:持有 005bb 且任职含当前俱乐部 → 可审本俱乐部(同意/拒绝/自动打款) -- 集团汇总视图 / 跨俱乐部:须集团资金处置权(GROUP_OWNER / GROUP_SUPER_ADMIN) -俱乐部本地 000001 不能绕过「跨店」闸门,但可审本店提现。 +- 子公司视图:持有 005bb 且任职含当前俱乐部 → 可审本俱乐部 +- 集团汇总视图 / 跨俱乐部:须集团资金处置权 """ from django.db.models import Q @@ -17,6 +21,12 @@ from jituan.services.admin_context import ( ) MSG_GROUP_FINANCE_REQUIRED = '该操作仅集团高管可执行(需在数据范围配置中为集团级任职)' +MSG_BALANCE_ADD_DENIED = ( + '无权加余额:需当前俱乐部超级管理权限(000001),或集团高管任职;' + '且仅能操作当前俱乐部用户' +) +MSG_BALANCE_ADD_CROSS_CLUB = '跨俱乐部加余额仅集团高管可执行,请切换到具体俱乐部后再操作' +MSG_BALANCE_ADD_CLUB_DENIED = '无权在该俱乐部加余额' MSG_WITHDRAW_CROSS_CLUB = '跨俱乐部提现审核仅集团高管可执行,请切换到具体俱乐部后再操作' MSG_WITHDRAW_CLUB_DENIED = '无权审核该俱乐部提现' @@ -41,12 +51,59 @@ def has_group_finance_exec(user, request=None): 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 deny_balance_add_exec(request, *, permissions=None, target_user=None): + """ + 加余额 / 加押金 / 增加可提现等「资金增加」闸门。 + + 放行条件(满足其一): + 1) 系统超管或集团高管 + 2) 当前请求权限含俱乐部超级管理 000001,且为 SINGLE_CLUB, + 当前俱乐部在任职范围内 + + 无论哪种身份:若传入 target_user,必须属于当前请求俱乐部数据范围 + (集团 ALL_CLUBS 视图下目标校验放行,见 forbid_if_user_out_of_scope)。 + """ + from rest_framework.response import Response + from jituan.services.club_context import resolve_club_id_from_request, resolve_club_scope + from jituan.services.club_user_access import forbid_if_user_out_of_scope + + user = getattr(request, 'user', None) + perms = permissions or [] + + if has_group_finance_exec(user, request): + if target_user is not None: + denied = forbid_if_user_out_of_scope(request, target_user) + if denied: + return denied + return None + + if '000001' not in perms: + return Response({'code': 403, 'msg': MSG_BALANCE_ADD_DENIED}) + + scope = resolve_club_scope(request) + if scope == DATA_SCOPE_ALL: + return Response({'code': 403, 'msg': MSG_BALANCE_ADD_CROSS_CLUB}) + + club_id = resolve_club_id_from_request(request) + allowed = get_allowed_club_ids(user) + if not club_id or club_id not in allowed: + return Response({'code': 403, 'msg': MSG_BALANCE_ADD_CLUB_DENIED}) + + if target_user is not None: + denied = forbid_if_user_out_of_scope(request, target_user) + if denied: + return denied + + return None + + def require_withdraw_audit_access(request, phone): """ 提现审核访问: diff --git a/users/views/kefu_dashou.py b/users/views/kefu_dashou.py index 4b0766e..f3c586c 100644 --- a/users/views/kefu_dashou.py +++ b/users/views/kefu_dashou.py @@ -438,8 +438,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) + from jituan.services.group_finance_gate import deny_balance_add_exec + deny = deny_balance_add_exec( + request, permissions=permissions, target_user=dashou_user, + ) if deny: return deny amount = Decimal(str(value)) @@ -477,8 +479,10 @@ class KefuUpdateDashouView(APIView): elif caozuo == 'jia_yajin': 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) + from jituan.services.group_finance_gate import deny_balance_add_exec + deny = deny_balance_add_exec( + request, permissions=permissions, target_user=dashou_user, + ) if deny: return deny amount = Decimal(str(value))