feat: 俱乐部超级管理可加本店余额,不再强制集团高管

持有000001且处于当前俱乐部视图时可加余额/押金/可提现;强制目标用户同俱乐部,并在修改记录中写入俱乐部ID。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-27 07:53:33 +08:00
parent 69e1ca0e1a
commit 96f1a80395
5 changed files with 107 additions and 23 deletions

View File

@@ -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):
"""
提现审核访问: