修复了管理员无法进后台的问题

This commit is contained in:
2026-06-19 01:58:34 +08:00
parent 5b9436b994
commit d71a81f279
22 changed files with 4396 additions and 4295 deletions

View File

@@ -1,478 +1,481 @@
# houtai/utils.py
import logging
import json
from functools import lru_cache
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from rest_framework.response import Response
from .models import AbnormalUserLog, PlayerDailyStats, MerchantDailyStats, \
ManagerRenewalDailyStats, ManagerDailyStats, LeaderDailyStats, WithdrawalDailyStats
from gvsdsdk.models import UserRole as SdkUserRole, RolePermission as SdkRolePermission, Permission as SdkPermission
from django.db import transaction
from django.db.models import F
from datetime import date
from decimal import Decimal
from gvsdsdk.fluent import db, func, FQ
logger = logging.getLogger(__name__)
def _get_real_ip(request):
"""获取真实IP考虑代理"""
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0].strip()
return ip, x_forwarded_for
else:
ip = request.META.get('REMOTE_ADDR')
return ip, ''
def _record_abnormal(account_id, request, attempt_type, detail=''):
"""记录异常操作(仅用于越权等攻击行为)"""
try:
real_ip, xff = _get_real_ip(request)
headers = {k: v for k, v in request.META.items() if k.startswith('HTTP_')}
headers_json = json.dumps(headers, ensure_ascii=False)[:2000]
request_params = json.dumps(request.query_params.dict(), ensure_ascii=False) if request.query_params else ''
request_body = request.body.decode('utf-8', errors='ignore')[:5000]
AbnormalUserLog.query.create(
account_id=account_id,
ip_address=real_ip,
real_ip=real_ip,
x_forwarded_for=xff,
user_agent=request.META.get('HTTP_USER_AGENT', ''),
request_method=request.method,
request_path=request.path,
request_params=request_params,
request_body=request_body,
request_headers=headers_json,
attempt_type=attempt_type,
detail=detail,
)
logger.warning(f"记录异常用户: {account_id} IP:{real_ip} 类型:{attempt_type}")
except Exception as e:
logger.error(f"记录异常日志失败: {e}")
def verify_kefu_permission(request, username_from_frontend=None):
"""
验证客服身份并返回该用户的所有权限列表基于新RBAC表
只有前端传递的username与当前登录用户不匹配时才记录异常并警告前端。
参数:
request: DRF request 对象
username_from_frontend: 前端传递的账号(用于防越权)
返回:
(kefu_obj, permissions_list) # 验证通过
(None, Response) # 验证失败直接返回Response
"""
current_user = request.user
# ---------- 1. 必须是客服 ----------
if current_user.user_type != 'kefu':
return None, Response({'code': 403, 'msg': '身份错误,非客服账号'}, status=403)
# ---------- 2. 客服扩展表存在且状态正常 ----------
try:
kefu = current_user.kefu_profile
if kefu.zhuangtai != 1:
return None, Response({'code': 403, 'msg': '客服账号已被禁用'}, status=403)
except ObjectDoesNotExist:
return None, Response({'code': 403, 'msg': '客服账号不存在'}, status=403)
# ---------- 3. 越权检测只记录前端篡改username的情况 ----------
if username_from_frontend and current_user.phone != username_from_frontend:
detail = f'前端username: {username_from_frontend},实际登录账号: {current_user.phone}'
_record_abnormal(current_user.phone, request, '越权尝试篡改username', detail=detail)
return None, Response({
'code': 403,
'msg': '身份错误,无权操作其他账号,相关操作已记录'
}, status=403)
# ---------- 4. 获取用户的所有权限 ----------
# 使用 gvsdsdk RBAC 表UserRole → RolePermission → Permission
try:
user_uuid = current_user.UserUUID
role_uuids = list(SdkUserRole.objects.filter(
UserUUID=user_uuid
).values_list('RoleUUID', flat=True))
if role_uuids:
perm_uuids = list(SdkRolePermission.objects.filter(
RoleUUID__in=role_uuids
).values_list('PermUUID', flat=True))
permissions = list(SdkPermission.objects.filter(
PermUUID__in=perm_uuids,
PermStatus=1
).values_list('PermCode', flat=True).distinct())
else:
permissions = []
except Exception:
permissions = []
# 超级用户自动拥有所有权限
if current_user.IsSuperuser and '000001' not in permissions:
permissions.insert(0, '000001')
if not permissions:
logger.warning(f"用户 {current_user.phone} 没有任何权限,请检查角色分配")
return kefu, permissions
def update_dashou_daily_by_action(yonghuid, amount, action):
"""
根据行为类型更新打手每日统计(接单、成交、退款)
参数:
yonghuid: 打手用户ID
amount: 本单涉及的金额Decimal
action: 1 = 接单, 2 = 成交(结算), 3 = 退款
"""
amount = Decimal(str(amount)) if amount else Decimal('0.00')
if amount <= 0:
return # 金额非正不统计
today = date.today()
with transaction.atomic():
stat, created = PlayerDailyStats.objects.select_for_update().get_or_create(
PlayerID=yonghuid,
Date=today,
defaults={
'PlayerID': yonghuid,
'Date': today,
'AcceptedOrderTotal': 0,
'AcceptedAmount': Decimal('0.00'),
'CompletedOrderTotal': 0,
'CompletedAmount': Decimal('0.00'),
'RefundCount': 0,
'RefundAmount': Decimal('0.00'),
}
)
update_fields = {}
if action == 1: # 接单
update_fields['AcceptedOrderTotal'] = F('AcceptedOrderTotal') + 1
update_fields['AcceptedAmount'] = F('AcceptedAmount') + amount
elif action == 2: # 成交
update_fields['CompletedOrderTotal'] = F('CompletedOrderTotal') + 1
update_fields['CompletedAmount'] = F('CompletedAmount') + amount
elif action == 3: # 退款
update_fields['RefundCount'] = F('RefundCount') + 1
update_fields['RefundAmount'] = F('RefundAmount') + amount
else:
return # 无效行为
if update_fields:
PlayerDailyStats.query.filter(id=stat.id).update(**update_fields)
def update_shangjia_daily(yonghuid, amount, action):
"""
根据操作行为更新商家每日统计(派发、结算、退款)
参数:
yonghuid: 商家用户ID
amount: 当前订单涉及的金额Decimal
action: 操作类型1 = 派发2 = 结算成交3 = 退款
说明:
- 金额必须大于0否则不执行任何操作
- 使用 select_for_update + F() 表达式保证原子性和并发安全
- 每天每个商家只会有一条统计记录,不存在则自动创建
"""
amount = Decimal(str(amount)) if amount else Decimal('0.00')
if amount <= 0:
return # 金额非正,不统计
today = date.today()
with transaction.atomic():
# 获取或创建当天统计记录,同时加锁防止并发
stat, created = MerchantDailyStats.objects.select_for_update().get_or_create(
MerchantID=yonghuid,
Date=today,
defaults={
'MerchantID': yonghuid,
'Date': today,
'AssignedOrderCount': 0,
'AssignedAmount': Decimal('0.00'),
'SettledOrderCount': 0,
'SettledAmount': Decimal('0.00'),
'RefundOrderCount': 0,
'RefundAmount': Decimal('0.00'),
}
)
# 构建本次更新的字段,使用 F 表达式进行原子累加
update_fields = {}
if action == 1: # 派发
update_fields['AssignedOrderCount'] = F('AssignedOrderCount') + 1
update_fields['AssignedAmount'] = F('AssignedAmount') + amount
elif action == 2: # 结算(成交)
update_fields['SettledOrderCount'] = F('SettledOrderCount') + 1
update_fields['SettledAmount'] = F('SettledAmount') + amount
elif action == 3: # 退款
update_fields['RefundOrderCount'] = F('RefundOrderCount') + 1
update_fields['RefundAmount'] = F('RefundAmount') + amount
else:
# 无效的行为类型,直接返回
return
# 执行原子更新
MerchantDailyStats.query.filter(id=stat.id).update(**update_fields)
def update_guanshi_daily_by_action(yonghuid, action, amount=Decimal('0.00')):
"""
根据操作行为更新管事每日统计(邀请打手、充值会员、订单/押金分红)
action: 1 = 邀请打手, 2 = 充值会员, 3 = 商家订单分红, 4 = 押金分红
"""
today = date.today()
with transaction.atomic():
stat, created = ManagerDailyStats.objects.select_for_update().get_or_create(
ManagerID=yonghuid,
Date=today,
defaults={
'ManagerID': yonghuid,
'Date': today,
'InvitedPlayerCount': 0,
'RechargedPlayerCount': 0,
'TotalIncome': Decimal('0.00'),
}
)
if action == 1:
# 邀请打手:无论新创建还是已存在,直接 +1
if created:
stat.InvitedPlayerCount = 1
stat.save()
else:
ManagerDailyStats.query.filter(id=stat.id).update(
InvitedPlayerCount=F('InvitedPlayerCount') + 1
)
elif action == 2:
amount = Decimal(str(amount)) if amount else Decimal('0.00')
if amount <= 0:
return
if created:
stat.RechargedPlayerCount = 1
stat.TotalIncome = amount
stat.save()
else:
ManagerDailyStats.query.filter(id=stat.id).update(
RechargedPlayerCount=F('RechargedPlayerCount') + 1,
TotalIncome=F('TotalIncome') + amount
)
elif action in (3, 4):
amount = Decimal(str(amount)) if amount else Decimal('0.00')
if amount <= 0:
return
if created:
stat.TotalIncome = amount
stat.save()
else:
ManagerDailyStats.query.filter(id=stat.id).update(
TotalIncome=F('TotalIncome') + amount
)
def update_guanshi_xufei_daily(yonghuid, xufei_jine=Decimal('0.00')):
"""
管事续费统计(单独表)
用法: update_guanshi_xufei_daily('300001', Decimal('30'))
"""
today = date.today()
with transaction.atomic():
stat, created = ManagerRenewalDailyStats.objects.select_for_update().get_or_create(
ManagerID=yonghuid,
Date=today,
defaults={
'ManagerID': yonghuid,
'Date': today,
'RenewalTotal': 1,
'RenewalRevenue': xufei_jine,
}
)
if not created:
ManagerRenewalDailyStats.query.filter(id=stat.id).update(
RenewalTotal=F('RenewalTotal') + 1,
RenewalRevenue=F('RenewalRevenue') + xufei_jine
)
def update_zuzhang_daily_by_action(yonghuid, action, amount=Decimal('0.00')):
"""
根据操作行为更新组长每日统计(邀请管事、分佣收入)
参数:
yonghuid: 组长用户ID
action: 操作类型
1 = 邀请管事(次数+1无金额
2 = 分佣收入(累加金额)
amount: 分佣金额Decimal仅在 action=2 时有效
"""
today = date.today()
with transaction.atomic():
stat, created = LeaderDailyStats.objects.select_for_update().get_or_create(
LeaderID=yonghuid,
Date=today,
defaults={
'LeaderID': yonghuid,
'Date': today,
'InvitedManagerCount': 0,
'TotalIncome': Decimal('0.00'),
'CommissionAmount': Decimal('0.00'),
}
)
if created:
# 新记录直接设置初始值
if action == 1:
stat.InvitedManagerCount = 1
elif action == 2:
amount = Decimal(str(amount)) if amount else Decimal('0.00')
stat.TotalIncome = amount
stat.CommissionAmount = amount
stat.save()
else:
# 已存在记录,使用 F 表达式原子累加
if action == 1:
LeaderDailyStats.query.filter(id=stat.id).update(
InvitedManagerCount=F('InvitedManagerCount') + 1
)
elif action == 2:
amount = Decimal(str(amount)) if amount else Decimal('0.00')
if amount > 0:
LeaderDailyStats.query.filter(id=stat.id).update(
TotalIncome=F('TotalIncome') + amount,
CommissionAmount=F('CommissionAmount') + amount
)
def update_tixian_daily_stat(leixing, amount):
"""
更新每日提现统计(平台维度)
leixing: 1=打手, 2=管事, 3=组长
amount: 本次提现金额(申请金额,含手续费)
"""
today = date.today()
with transaction.atomic():
stat, created = WithdrawalDailyStats.objects.select_for_update().get_or_create(
Date=today,
WithdrawalType=leixing,
defaults={'total_amount': amount}
)
if not created:
WithdrawalDailyStats.query.filter(id=stat.id).update(
total_amount=F('total_amount') + amount
)
# 罚款权限(与 permission 表一致)
# 66693a/b/c + 66694c按身份管理罚款99933abs罚款申请通用可查看列表并操作各身份
_DEFAULT_FADAN_PERM_TO_SHENFEN = {
'66693a': 1, # 罚款管理-打手
'66693c': 2, # 罚款管理-管事
'66693b': 3, # 罚款管理-商家
'66694c': 4, # 罚款管理-组长
}
_FADAN_GENERIC_PERMS = (
'99933abs', # 罚款申请权限
)
_SHENFEN_KEYWORDS = {
'打手': 1,
'管事': 2,
'商家': 3,
'组长': 4,
}
SHENFEN_PROFILE_MAP = {
1: 'dashou_profile',
2: 'guanshi_profile',
3: 'shop_profile',
4: 'zuzhang_profile',
}
@lru_cache(maxsize=1)
def _get_fadan_permission_config():
"""
加载罚款相关权限码(硬编码 + permission 表按 perm_name 补充)。
仅匹配 perm_name 含「罚款/罚单」,避免 qtczcaiwu 等描述里带「罚款」的财务权限误入。
"""
perm_map = dict(_DEFAULT_FADAN_PERM_TO_SHENFEN)
generic_codes = list(_FADAN_GENERIC_PERMS)
try:
rows = Permission.query.filter(
Q(perm_name__icontains='罚款') | Q(perm_name__icontains='罚单')
).values('perm_code', 'perm_name')
for row in rows:
code = row['perm_code']
name = row['perm_name'] or ''
matched_sf = None
for kw, sf in _SHENFEN_KEYWORDS.items():
if kw in name:
matched_sf = sf
break
if matched_sf is not None:
perm_map[code] = matched_sf
elif code not in generic_codes:
generic_codes.append(code)
except Exception as e:
logger.warning(f"从 permission 表加载罚款权限失败,使用硬编码映射: {e}")
return perm_map, tuple(generic_codes)
# 兼容旧引用
PERMISSION_TO_SHENFEN = _DEFAULT_FADAN_PERM_TO_SHENFEN
def has_fadan_view_permission(permissions, account_id=''):
"""列表/统计:拥有任一罚款权限或超级管理员权限即可查看。"""
if '000001' in permissions:
return True
perm_map, generic_codes = _get_fadan_permission_config()
all_codes = set(perm_map.keys()) | set(generic_codes)
if any(p in permissions for p in all_codes):
return True
logger.warning(
f"罚款查看权限不足 account={account_id} "
f"user_perms={permissions} need_any={sorted(all_codes)}"
)
return False
def check_fadan_permission(permissions, shenfen):
"""创建/处理:需拥有对应身份的罚款权限,或通用罚款权限,或超级管理员权限。"""
if '000001' in permissions:
return True
if any(p in permissions for p in _get_fadan_permission_config()[1]):
return True
shenfen = int(shenfen)
perm_map, _ = _get_fadan_permission_config()
for perm, sf in perm_map.items():
if sf == shenfen and perm in permissions:
return True
# houtai/utils.py
import logging
import json
from functools import lru_cache
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from rest_framework.response import Response
from .models import AbnormalUserLog, PlayerDailyStats, MerchantDailyStats, \
ManagerRenewalDailyStats, ManagerDailyStats, LeaderDailyStats, WithdrawalDailyStats
from gvsdsdk.models import UserRole as SdkUserRole, RolePermission as SdkRolePermission, Permission as SdkPermission
from django.db import transaction
from django.db.models import F
from datetime import date
from decimal import Decimal
from gvsdsdk.fluent import db, func, FQ
logger = logging.getLogger(__name__)
def _get_real_ip(request):
"""获取真实IP考虑代理"""
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0].strip()
return ip, x_forwarded_for
else:
ip = request.META.get('REMOTE_ADDR')
return ip, ''
def _record_abnormal(account_id, request, attempt_type, detail=''):
"""记录异常操作(仅用于越权等攻击行为)"""
try:
real_ip, xff = _get_real_ip(request)
headers = {k: v for k, v in request.META.items() if k.startswith('HTTP_')}
headers_json = json.dumps(headers, ensure_ascii=False)[:2000]
request_params = json.dumps(request.query_params.dict(), ensure_ascii=False) if request.query_params else ''
request_body = request.body.decode('utf-8', errors='ignore')[:5000]
AbnormalUserLog.query.create(
account_id=account_id,
ip_address=real_ip,
real_ip=real_ip,
x_forwarded_for=xff,
user_agent=request.META.get('HTTP_USER_AGENT', ''),
request_method=request.method,
request_path=request.path,
request_params=request_params,
request_body=request_body,
request_headers=headers_json,
attempt_type=attempt_type,
detail=detail,
)
logger.warning(f"记录异常用户: {account_id} IP:{real_ip} 类型:{attempt_type}")
except Exception as e:
logger.error(f"记录异常日志失败: {e}")
def verify_kefu_permission(request, username_from_frontend=None):
"""
验证客服/管理员身份并返回该用户的所有权限列表基于新RBAC表
只有前端传递的username与当前登录用户不匹配时才记录异常并警告前端。
参数:
request: DRF request 对象
username_from_frontend: 前端传递的账号(用于防越权)
返回:
(kefu_obj, permissions_list) # 验证通过
(None, Response) # 验证失败直接返回Response
"""
current_user = request.user
# ---------- 1. 必须是客服或管理员 ----------
is_admin = current_user.UserType == 'admin' or current_user.IsSuperuser
if current_user.UserType != 'kefu' and not is_admin:
return None, Response({'code': 403, 'msg': '身份错误,非客服账号'}, status=403)
# ---------- 2. 客服扩展表存在且状态正常(管理员跳过) ----------
kefu = None
if not is_admin:
try:
kefu = current_user.KefuProfile
if kefu.zhuangtai != 1:
return None, Response({'code': 403, 'msg': '客服账号已被禁用'}, status=403)
except ObjectDoesNotExist:
return None, Response({'code': 403, 'msg': '客服账号不存在'}, status=403)
# ---------- 3. 越权检测只记录前端篡改username的情况 ----------
if username_from_frontend and current_user.Phone != username_from_frontend:
detail = f'前端username: {username_from_frontend},实际登录账号: {current_user.Phone}'
_record_abnormal(current_user.Phone, request, '越权尝试篡改username', detail=detail)
return None, Response({
'code': 403,
'msg': '身份错误,无权操作其他账号,相关操作已记录'
}, status=403)
# ---------- 4. 获取用户的所有权限 ----------
# 使用 gvsdsdk RBAC 表UserRole → RolePermission → Permission
try:
user_uuid = current_user.UserUUID
role_uuids = list(SdkUserRole.objects.filter(
UserUUID=user_uuid
).values_list('RoleUUID', flat=True))
if role_uuids:
perm_uuids = list(SdkRolePermission.objects.filter(
RoleUUID__in=role_uuids
).values_list('PermUUID', flat=True))
permissions = list(SdkPermission.objects.filter(
PermUUID__in=perm_uuids,
PermStatus=1
).values_list('PermCode', flat=True).distinct())
else:
permissions = []
except Exception:
permissions = []
# 超级用户自动拥有所有权限
if current_user.IsSuperuser and '000001' not in permissions:
permissions.insert(0, '000001')
if not permissions:
logger.warning(f"用户 {current_user.Phone} 没有任何权限,请检查角色分配")
return kefu, permissions
def update_dashou_daily_by_action(yonghuid, amount, action):
"""
根据行为类型更新打手每日统计(接单、成交、退款)
参数:
yonghuid: 打手用户ID
amount: 本单涉及的金额Decimal
action: 1 = 接单, 2 = 成交(结算), 3 = 退款
"""
amount = Decimal(str(amount)) if amount else Decimal('0.00')
if amount <= 0:
return # 金额非正不统计
today = date.today()
with transaction.atomic():
stat, created = PlayerDailyStats.objects.select_for_update().get_or_create(
PlayerID=yonghuid,
Date=today,
defaults={
'PlayerID': yonghuid,
'Date': today,
'AcceptedOrderTotal': 0,
'AcceptedAmount': Decimal('0.00'),
'CompletedOrderTotal': 0,
'CompletedAmount': Decimal('0.00'),
'RefundCount': 0,
'RefundAmount': Decimal('0.00'),
}
)
update_fields = {}
if action == 1: # 接单
update_fields['AcceptedOrderTotal'] = F('AcceptedOrderTotal') + 1
update_fields['AcceptedAmount'] = F('AcceptedAmount') + amount
elif action == 2: # 成交
update_fields['CompletedOrderTotal'] = F('CompletedOrderTotal') + 1
update_fields['CompletedAmount'] = F('CompletedAmount') + amount
elif action == 3: # 退款
update_fields['RefundCount'] = F('RefundCount') + 1
update_fields['RefundAmount'] = F('RefundAmount') + amount
else:
return # 无效行为
if update_fields:
PlayerDailyStats.query.filter(id=stat.id).update(**update_fields)
def update_shangjia_daily(yonghuid, amount, action):
"""
根据操作行为更新商家每日统计(派发、结算、退款)
参数:
yonghuid: 商家用户ID
amount: 当前订单涉及的金额Decimal
action: 操作类型1 = 派发2 = 结算成交3 = 退款
说明:
- 金额必须大于0否则不执行任何操作
- 使用 select_for_update + F() 表达式保证原子性和并发安全
- 每天每个商家只会有一条统计记录,不存在则自动创建
"""
amount = Decimal(str(amount)) if amount else Decimal('0.00')
if amount <= 0:
return # 金额非正,不统计
today = date.today()
with transaction.atomic():
# 获取或创建当天统计记录,同时加锁防止并发
stat, created = MerchantDailyStats.objects.select_for_update().get_or_create(
MerchantID=yonghuid,
Date=today,
defaults={
'MerchantID': yonghuid,
'Date': today,
'AssignedOrderCount': 0,
'AssignedAmount': Decimal('0.00'),
'SettledOrderCount': 0,
'SettledAmount': Decimal('0.00'),
'RefundOrderCount': 0,
'RefundAmount': Decimal('0.00'),
}
)
# 构建本次更新的字段,使用 F 表达式进行原子累加
update_fields = {}
if action == 1: # 派发
update_fields['AssignedOrderCount'] = F('AssignedOrderCount') + 1
update_fields['AssignedAmount'] = F('AssignedAmount') + amount
elif action == 2: # 结算(成交)
update_fields['SettledOrderCount'] = F('SettledOrderCount') + 1
update_fields['SettledAmount'] = F('SettledAmount') + amount
elif action == 3: # 退款
update_fields['RefundOrderCount'] = F('RefundOrderCount') + 1
update_fields['RefundAmount'] = F('RefundAmount') + amount
else:
# 无效的行为类型,直接返回
return
# 执行原子更新
MerchantDailyStats.query.filter(id=stat.id).update(**update_fields)
def update_guanshi_daily_by_action(yonghuid, action, amount=Decimal('0.00')):
"""
根据操作行为更新管事每日统计(邀请打手、充值会员、订单/押金分红)
action: 1 = 邀请打手, 2 = 充值会员, 3 = 商家订单分红, 4 = 押金分红
"""
today = date.today()
with transaction.atomic():
stat, created = ManagerDailyStats.objects.select_for_update().get_or_create(
ManagerID=yonghuid,
Date=today,
defaults={
'ManagerID': yonghuid,
'Date': today,
'InvitedPlayerCount': 0,
'RechargedPlayerCount': 0,
'TotalIncome': Decimal('0.00'),
}
)
if action == 1:
# 邀请打手:无论新创建还是已存在,直接 +1
if created:
stat.InvitedPlayerCount = 1
stat.save()
else:
ManagerDailyStats.query.filter(id=stat.id).update(
InvitedPlayerCount=F('InvitedPlayerCount') + 1
)
elif action == 2:
amount = Decimal(str(amount)) if amount else Decimal('0.00')
if amount <= 0:
return
if created:
stat.RechargedPlayerCount = 1
stat.TotalIncome = amount
stat.save()
else:
ManagerDailyStats.query.filter(id=stat.id).update(
RechargedPlayerCount=F('RechargedPlayerCount') + 1,
TotalIncome=F('TotalIncome') + amount
)
elif action in (3, 4):
amount = Decimal(str(amount)) if amount else Decimal('0.00')
if amount <= 0:
return
if created:
stat.TotalIncome = amount
stat.save()
else:
ManagerDailyStats.query.filter(id=stat.id).update(
TotalIncome=F('TotalIncome') + amount
)
def update_guanshi_xufei_daily(yonghuid, xufei_jine=Decimal('0.00')):
"""
管事续费统计(单独表)
用法: update_guanshi_xufei_daily('300001', Decimal('30'))
"""
today = date.today()
with transaction.atomic():
stat, created = ManagerRenewalDailyStats.objects.select_for_update().get_or_create(
ManagerID=yonghuid,
Date=today,
defaults={
'ManagerID': yonghuid,
'Date': today,
'RenewalTotal': 1,
'RenewalRevenue': xufei_jine,
}
)
if not created:
ManagerRenewalDailyStats.query.filter(id=stat.id).update(
RenewalTotal=F('RenewalTotal') + 1,
RenewalRevenue=F('RenewalRevenue') + xufei_jine
)
def update_zuzhang_daily_by_action(yonghuid, action, amount=Decimal('0.00')):
"""
根据操作行为更新组长每日统计(邀请管事、分佣收入)
参数:
yonghuid: 组长用户ID
action: 操作类型
1 = 邀请管事(次数+1无金额
2 = 分佣收入(累加金额)
amount: 分佣金额Decimal仅在 action=2 时有效
"""
today = date.today()
with transaction.atomic():
stat, created = LeaderDailyStats.objects.select_for_update().get_or_create(
LeaderID=yonghuid,
Date=today,
defaults={
'LeaderID': yonghuid,
'Date': today,
'InvitedManagerCount': 0,
'TotalIncome': Decimal('0.00'),
'CommissionAmount': Decimal('0.00'),
}
)
if created:
# 新记录直接设置初始值
if action == 1:
stat.InvitedManagerCount = 1
elif action == 2:
amount = Decimal(str(amount)) if amount else Decimal('0.00')
stat.TotalIncome = amount
stat.CommissionAmount = amount
stat.save()
else:
# 已存在记录,使用 F 表达式原子累加
if action == 1:
LeaderDailyStats.query.filter(id=stat.id).update(
InvitedManagerCount=F('InvitedManagerCount') + 1
)
elif action == 2:
amount = Decimal(str(amount)) if amount else Decimal('0.00')
if amount > 0:
LeaderDailyStats.query.filter(id=stat.id).update(
TotalIncome=F('TotalIncome') + amount,
CommissionAmount=F('CommissionAmount') + amount
)
def update_tixian_daily_stat(leixing, amount):
"""
更新每日提现统计(平台维度)
leixing: 1=打手, 2=管事, 3=组长
amount: 本次提现金额(申请金额,含手续费)
"""
today = date.today()
with transaction.atomic():
stat, created = WithdrawalDailyStats.objects.select_for_update().get_or_create(
Date=today,
WithdrawalType=leixing,
defaults={'total_amount': amount}
)
if not created:
WithdrawalDailyStats.query.filter(id=stat.id).update(
total_amount=F('total_amount') + amount
)
# 罚款权限(与 permission 表一致)
# 66693a/b/c + 66694c按身份管理罚款99933abs罚款申请通用可查看列表并操作各身份
_DEFAULT_FADAN_PERM_TO_SHENFEN = {
'66693a': 1, # 罚款管理-打手
'66693c': 2, # 罚款管理-管事
'66693b': 3, # 罚款管理-商家
'66694c': 4, # 罚款管理-组长
}
_FADAN_GENERIC_PERMS = (
'99933abs', # 罚款申请权限
)
_SHENFEN_KEYWORDS = {
'打手': 1,
'管事': 2,
'商家': 3,
'组长': 4,
}
SHENFEN_PROFILE_MAP = {
1: 'DashouProfile',
2: 'GuanshiProfile',
3: 'ShopProfile',
4: 'ZuzhangProfile',
}
@lru_cache(maxsize=1)
def _get_fadan_permission_config():
"""
加载罚款相关权限码(硬编码 + permission 表按 perm_name 补充)。
仅匹配 perm_name 含「罚款/罚单」,避免 qtczcaiwu 等描述里带「罚款」的财务权限误入。
"""
perm_map = dict(_DEFAULT_FADAN_PERM_TO_SHENFEN)
generic_codes = list(_FADAN_GENERIC_PERMS)
try:
rows = Permission.query.filter(
Q(perm_name__icontains='罚款') | Q(perm_name__icontains='罚单')
).values('perm_code', 'perm_name')
for row in rows:
code = row['perm_code']
name = row['perm_name'] or ''
matched_sf = None
for kw, sf in _SHENFEN_KEYWORDS.items():
if kw in name:
matched_sf = sf
break
if matched_sf is not None:
perm_map[code] = matched_sf
elif code not in generic_codes:
generic_codes.append(code)
except Exception as e:
logger.warning(f"从 permission 表加载罚款权限失败,使用硬编码映射: {e}")
return perm_map, tuple(generic_codes)
# 兼容旧引用
PERMISSION_TO_SHENFEN = _DEFAULT_FADAN_PERM_TO_SHENFEN
def has_fadan_view_permission(permissions, account_id=''):
"""列表/统计:拥有任一罚款权限或超级管理员权限即可查看。"""
if '000001' in permissions:
return True
perm_map, generic_codes = _get_fadan_permission_config()
all_codes = set(perm_map.keys()) | set(generic_codes)
if any(p in permissions for p in all_codes):
return True
logger.warning(
f"罚款查看权限不足 account={account_id} "
f"user_perms={permissions} need_any={sorted(all_codes)}"
)
return False
def check_fadan_permission(permissions, shenfen):
"""创建/处理:需拥有对应身份的罚款权限,或通用罚款权限,或超级管理员权限。"""
if '000001' in permissions:
return True
if any(p in permissions for p in _get_fadan_permission_config()[1]):
return True
shenfen = int(shenfen)
perm_map, _ = _get_fadan_permission_config()
for perm, sf in perm_map.items():
if sf == shenfen and perm in permissions:
return True
return False

View File

@@ -322,13 +322,13 @@ class GetAdminUserListView(APIView):
users = User.query.filter(
user_type='kefu',
kefu_profile__isnull=False
).select_related('kefu_profile')
KefuProfile__isnull=False
).select_related('KefuProfile')
if phone:
users = users.filter(phone__icontains=phone)
if nicheng:
users = users.filter(kefu_profile__nicheng__icontains=nicheng)
users = users.filter(KefuProfile__nicheng__icontains=nicheng)
# 关键修正:先求值为列表,再传入 phone__in
if role_code:
@@ -365,11 +365,11 @@ class GetAdminUserListView(APIView):
for r in user_roles
]
data_list.append({
'phone': user.phone,
'nicheng': user.kefu_profile.nicheng,
'phone': user.Phone,
'nicheng': user.KefuProfile.nicheng,
'roles': roles_data,
'status': user.kefu_profile.zhuangtai,
'create_time': user.create_time.strftime('%Y-%m-%d %H:%M:%S') if hasattr(user, 'create_time') else '',
'status': user.KefuProfile.zhuangtai,
'create_time': user.UserCreateTime.strftime('%Y-%m-%d %H:%M:%S') if hasattr(user, 'UserCreateTime') else '',
'update_time': user.update_time.strftime('%Y-%m-%d %H:%M:%S') if hasattr(user, 'update_time') else '',
})
@@ -440,11 +440,11 @@ class GetAdminRolesView(APIView):
page_size = int(request.data.get('pageSize', 20))
# 基础查询:存在客服扩展表
users = User.query.filter(kefu_profile__isnull=False).select_related('kefu_profile')
users = User.query.filter(KefuProfile__isnull=False).select_related('KefuProfile')
if phone:
users = users.filter(Phone__icontains=phone)
if nicheng:
users = users.filter(kefu_profile__nicheng__icontains=nicheng)
users = users.filter(KefuProfile__nicheng__icontains=nicheng)
if role_code:
# 筛选拥有该角色的用户
user_ids_with_role = UserRole.query.filter(role__role_code=role_code).values_list('account_id', flat=True)
@@ -461,14 +461,14 @@ class GetAdminRolesView(APIView):
data_list = []
for user in page_obj:
# 获取用户的所有角色
user_roles = UserRole.query.filter(account_id=user.phone).select_related('role')
user_roles = UserRole.query.filter(account_id=user.Phone).select_related('role')
roles_data = [{'role_code': ur.role.role_code, 'role_name': ur.role.role_name} for ur in user_roles]
data_list.append({
'phone': user.phone,
'nicheng': user.kefu_profile.nicheng,
'phone': user.Phone,
'nicheng': user.KefuProfile.nicheng,
'roles': roles_data,
'status': user.kefu_profile.zhuangtai,
'create_time': user.create_time.strftime('%Y-%m-%d %H:%M:%S'),
'status': user.KefuProfile.zhuangtai,
'create_time': user.UserCreateTime.strftime('%Y-%m-%d %H:%M:%S'),
'update_time': user.update_time.strftime('%Y-%m-%d %H:%M:%S'),
})
@@ -508,7 +508,7 @@ class ModifyAdminUserView(APIView):
try:
target_user = User.query.get(Phone=target_phone)
target_kefu = target_user.kefu_profile
target_kefu = target_user.KefuProfile
except User.DoesNotExist:
return Response({'code': 404, 'msg': '用户不存在'})
except UserKefu.DoesNotExist:
@@ -600,7 +600,7 @@ class AddAdminUserView(APIView):
return Response({'code': 400, 'msg': '密码和二级密码至少6位'})
# 校验手机号是否已被客服占用
if User.query.filter(Phone=phone, kefu_profile__isnull=False).exists():
if User.query.filter(Phone=phone, KefuProfile__isnull=False).exists():
return Response({'code': 400, 'msg': '该账号已存在'})
# 生成唯一的 yonghuid7位数字
@@ -743,8 +743,8 @@ class KefuGetDashouListView(APIView):
for dashou in dashou_list:
user = dashou.user
result.append({
'yonghuid': user.yonghuid,
'avatar': user.avatar or '',
'yonghuid': user.UserUID,
'avatar': user.Avatar or '',
'zaixianzhuangtai': dashou.zaixianzhuangtai,
'zhanghaozhuangtai': dashou.zhanghaozhuangtai,
'nicheng': dashou.nicheng or '',
@@ -799,19 +799,19 @@ class KefuGetDashouDetailView(APIView):
# 4. 查询打手主表和扩展表
try:
user_main = User.query.select_related('dashou_profile').get(
user_main = User.query.select_related('DashouProfile').get(
UserUID=uid
)
except User.DoesNotExist:
return Response({'code': 404, 'msg': '打手不存在'})
try:
dashou = user_main.dashou_profile
dashou = user_main.DashouProfile
except ObjectDoesNotExist:
# 用户存在但未注册打手身份,返回基本信息
user_info = {
'yonghuid': user_main.yonghuid,
'avatar': user_main.avatar or '',
'yonghuid': user_main.UserUID,
'avatar': user_main.Avatar or '',
'zaixianzhuangtai': 0,
'zhanghaozhuangtai': 0,
'zhuangtai': 0,
@@ -819,9 +819,9 @@ class KefuGetDashouDetailView(APIView):
'chenghao': '',
'dianhua': '',
'wechat': '',
'phone': user_main.phone or '',
'create_time': user_main.create_time,
'ip': user_main.ip or '',
'phone': user_main.Phone or '',
'create_time': user_main.UserCreateTime,
'ip': user_main.IP or '',
'jiedanzongliang': 0,
'chengjiaozongliang': 0,
'tuikuanliang': 0,
@@ -850,8 +850,8 @@ class KefuGetDashouDetailView(APIView):
# 5. 构建打手信息
user_info = {
'yonghuid': user_main.yonghuid,
'avatar': user_main.avatar or '',
'yonghuid': user_main.UserUID,
'avatar': user_main.Avatar or '',
'zaixianzhuangtai': dashou.zaixianzhuangtai,
'zhanghaozhuangtai': dashou.zhanghaozhuangtai,
'zhuangtai': dashou.zhuangtai,
@@ -859,9 +859,9 @@ class KefuGetDashouDetailView(APIView):
'chenghao': dashou.chenghao or '',
'dianhua': dashou.dianhua or '',
'wechat': dashou.wechat or '',
'phone': user_main.phone or '',
'create_time': user_main.create_time,
'ip': user_main.ip or '',
'phone': user_main.Phone or '',
'create_time': user_main.UserCreateTime,
'ip': user_main.IP or '',
'jiedanzongliang': dashou.jiedanzongliang,
'chengjiaozongliang': dashou.chengjiaozongliang,
'tuikuanliang': dashou.tuikuanliang,
@@ -956,7 +956,7 @@ class KefuGetDashouDetailView(APIView):
# 3. 查询打手主表和扩展表
try:
# 使用 select_related 预加载 dashou_profile减少数据库查询
user_main = User.query.select_related('dashou_profile').get(
user_main = User.query.select_related('DashouProfile').get(
UserUID=uid
)
except User.DoesNotExist:
@@ -965,12 +965,12 @@ class KefuGetDashouDetailView(APIView):
# 验证用户类型是否为打手(可选,但建议验证)
# 如果该用户没有打手扩展表,则返回空信息(但前端会显示未注册)
try:
dashou = user_main.dashou_profile
dashou = user_main.DashouProfile
except ObjectDoesNotExist:
# 用户存在但未注册打手身份,返回基本信息
user_info = {
'yonghuid': user_main.yonghuid,
'avatar': user_main.avatar or '',
'yonghuid': user_main.UserUID,
'avatar': user_main.Avatar or '',
'zaixianzhuangtai': 0,
'zhanghaozhuangtai': 0,
'zhuangtai': 0,
@@ -978,9 +978,9 @@ class KefuGetDashouDetailView(APIView):
'chenghao': '',
'dianhua': '',
'wechat': '',
'phone': user_main.phone or '',
'create_time': user_main.create_time,
'ip': user_main.ip or '',
'phone': user_main.Phone or '',
'create_time': user_main.UserCreateTime,
'ip': user_main.IP or '',
'jiedanzongliang': 0,
'chengjiaozongliang': 0,
'tuikuanliang': 0,
@@ -1005,8 +1005,8 @@ class KefuGetDashouDetailView(APIView):
# 4. 构建打手信息
user_info = {
'yonghuid': user_main.yonghuid,
'avatar': user_main.avatar or '',
'yonghuid': user_main.UserUID,
'avatar': user_main.Avatar or '',
'zaixianzhuangtai': dashou.zaixianzhuangtai,
'zhanghaozhuangtai': dashou.zhanghaozhuangtai,
'zhuangtai': dashou.zhuangtai,
@@ -1014,9 +1014,9 @@ class KefuGetDashouDetailView(APIView):
'chenghao': dashou.chenghao or '',
'dianhua': dashou.dianhua or '',
'wechat': dashou.wechat or '',
'phone': user_main.phone or '',
'create_time': user_main.create_time,
'ip': user_main.ip or '',
'phone': user_main.Phone or '',
'create_time': user_main.UserCreateTime,
'ip': user_main.IP or '',
'jiedanzongliang': dashou.jiedanzongliang,
'chengjiaozongliang': dashou.chengjiaozongliang,
'tuikuanliang': dashou.tuikuanliang,
@@ -1107,7 +1107,7 @@ class KefuUpdateDashouView(APIView):
# 3. 查询打手
try:
dashou_user = User.query.get(UserUID=dashou_id)
dashou_profile = dashou_user.dashou_profile
dashou_profile = dashou_user.DashouProfile
except User.DoesNotExist:
return Response({'code': 404, 'msg': '打手不存在'})
except UserDashou.DoesNotExist:
@@ -1137,7 +1137,7 @@ class KefuUpdateDashouView(APIView):
after = dashou_profile.yue
Xiugaijilu.query.create(
yonghuid=dashou_id,
xiugaiid=kefu.user.phone,
xiugaiid=kefu.user.Phone,
leixing=2,
xiugaitijiao=after,
xiugaitijiaoq=before['yue'],
@@ -1153,7 +1153,7 @@ class KefuUpdateDashouView(APIView):
after = dashou_profile.yue
Xiugaijilu.query.create(
yonghuid=dashou_id,
xiugaiid=kefu.user.phone,
xiugaiid=kefu.user.Phone,
leixing=2,
xiugaitijiao=after,
xiugaitijiaoq=before['yue'],
@@ -1172,7 +1172,7 @@ class KefuUpdateDashouView(APIView):
after = dashou_profile.yajin
Xiugaijilu.query.create(
yonghuid=dashou_id,
xiugaiid=kefu.user.phone,
xiugaiid=kefu.user.Phone,
leixing=2,
yajin=after,
yyajin=before['yajin'],
@@ -1188,7 +1188,7 @@ class KefuUpdateDashouView(APIView):
after = dashou_profile.yajin
Xiugaijilu.query.create(
yonghuid=dashou_id,
xiugaiid=kefu.user.phone,
xiugaiid=kefu.user.Phone,
leixing=2,
yajin=after,
yyajin=before['yajin'],
@@ -1207,7 +1207,7 @@ class KefuUpdateDashouView(APIView):
after = dashou_profile.jifen
Xiugaijilu.query.create(
yonghuid=dashou_id,
xiugaiid=kefu.user.phone,
xiugaiid=kefu.user.Phone,
leixing=2,
jifen=after,
yjifen=before['jifen'],
@@ -1223,7 +1223,7 @@ class KefuUpdateDashouView(APIView):
after = dashou_profile.jifen
Xiugaijilu.query.create(
yonghuid=dashou_id,
xiugaiid=kefu.user.phone,
xiugaiid=kefu.user.Phone,
leixing=2,
jifen=after,
yjifen=before['jifen'],
@@ -1241,7 +1241,7 @@ class KefuUpdateDashouView(APIView):
dashou_profile.save()
Xiugaijilu.query.create(
yonghuid=dashou_id,
xiugaiid=kefu.user.phone,
xiugaiid=kefu.user.Phone,
leixing=2,
qitashuoming=f'修改账号状态为{new_status}',
)
@@ -1257,7 +1257,7 @@ class KefuUpdateDashouView(APIView):
dashou_profile.save()
Xiugaijilu.query.create(
yonghuid=dashou_id,
xiugaiid=kefu.user.phone,
xiugaiid=kefu.user.Phone,
leixing=2,
qitashuoming=f'修改在线状态为{new_status}',
)
@@ -1273,7 +1273,7 @@ class KefuUpdateDashouView(APIView):
dashou_profile.save()
Xiugaijilu.query.create(
yonghuid=dashou_id,
xiugaiid=kefu.user.phone,
xiugaiid=kefu.user.Phone,
leixing=2,
qitashuoming=f'修改接单状态为{new_status}',
)
@@ -1306,7 +1306,7 @@ class KefuUpdateDashouView(APIView):
record.save()
Xiugaijilu.query.create(
yonghuid=dashou_id,
xiugaiid=kefu.user.phone,
xiugaiid=kefu.user.Phone,
leixing=2,
huiyuants=days,
huiyuan_id=huiyuan_id,
@@ -1326,7 +1326,7 @@ class KefuUpdateDashouView(APIView):
record.delete()
Xiugaijilu.query.create(
yonghuid=dashou_id,
xiugaiid=kefu.user.phone,
xiugaiid=kefu.user.Phone,
leixing=2,
huiyuan_id=huiyuan_id,
qitashuoming=f'移除会员 {huiyuan_id}',
@@ -1345,7 +1345,7 @@ class KefuUpdateDashouView(APIView):
dashou_profile.save()
Xiugaijilu.query.create(
yonghuid=dashou_id,
xiugaiid=kefu.user.phone,
xiugaiid=kefu.user.Phone,
leixing=2,
qitashuoming=f'修改称号:{before["chenghao"] or ""}{new_chenghao}',
)
@@ -1479,8 +1479,8 @@ class KefuGetShangjiaListView(APIView):
for shangjia in shangjia_list:
user = shangjia.user
result.append({
'yonghuid': user.yonghuid,
'avatar': user.avatar or '',
'yonghuid': user.UserUID,
'avatar': user.Avatar or '',
'nicheng': shangjia.nicheng or '',
'yue': str(shangjia.yue), # 转换为字符串,避免前端精度问题
'fabu': shangjia.fabu,
@@ -1545,8 +1545,8 @@ class KefuGetShangjiaDetailView(APIView):
user = shangjia.user
data = {
'yonghuid': user.yonghuid,
'avatar': user.avatar or '',
'yonghuid': user.UserUID,
'avatar': user.Avatar or '',
'nicheng': shangjia.nicheng,
'zhuangtai': shangjia.zhuangtai, # 1正常2禁用
'dianhua': shangjia.dianhua,
@@ -2882,14 +2882,14 @@ class GetGuanliListView(APIView):
# 构建查询:从 User 开始,关联 UserGuanshi 和 UserBoss左连接获取昵称
qs = User.query.filter(
guanshi_profile__isnull=False # 确保是管事
).select_related('guanshi_profile').select_related('boss_profile')
GuanshiProfile__isnull=False # 确保是管事
).select_related('GuanshiProfile').select_related('BossProfile')
# 关键词搜索用户ID 或 昵称来自 boss_profile.nickname
if keyword:
qs = qs.filter(
Q(UserUID__icontains=keyword) |
Q(boss_profile__nickname__icontains=keyword)
Q(BossProfile__nickname__icontains=keyword)
)
# 余额筛选(使用 guanshi_profile.yue
@@ -2897,9 +2897,9 @@ class GetGuanliListView(APIView):
try:
balance_val = Decimal(str(balance_value))
if balance_op == 'gte':
qs = qs.filter(guanshi_profile__yue__gte=balance_val)
qs = qs.filter(GuanshiProfile__yue__gte=balance_val)
elif balance_op == 'lte':
qs = qs.filter(guanshi_profile__yue__lte=balance_val)
qs = qs.filter(GuanshiProfile__yue__lte=balance_val)
else:
return Response({'code': 400, 'msg': '余额比较符无效'})
except:
@@ -2910,9 +2910,9 @@ class GetGuanliListView(APIView):
try:
commission_val = Decimal(str(commission_value))
if commission_op == 'gte':
qs = qs.filter(guanshi_profile__chongzhifenrun__gte=commission_val)
qs = qs.filter(GuanshiProfile__chongzhifenrun__gte=commission_val)
elif commission_op == 'lte':
qs = qs.filter(guanshi_profile__chongzhifenrun__lte=commission_val)
qs = qs.filter(GuanshiProfile__chongzhifenrun__lte=commission_val)
else:
return Response({'code': 400, 'msg': '分佣比较符无效'})
except:
@@ -2923,9 +2923,9 @@ class GetGuanliListView(APIView):
try:
invite_val = int(invite_count_value)
if invite_count_op == 'gte':
qs = qs.filter(guanshi_profile__yaogingshuliang__gte=invite_val)
qs = qs.filter(GuanshiProfile__yaogingshuliang__gte=invite_val)
elif invite_count_op == 'lte':
qs = qs.filter(guanshi_profile__yaogingshuliang__lte=invite_val)
qs = qs.filter(GuanshiProfile__yaogingshuliang__lte=invite_val)
else:
return Response({'code': 400, 'msg': '邀请数量比较符无效'})
except:
@@ -2937,7 +2937,7 @@ class GetGuanliListView(APIView):
status_val = int(status)
if status_val not in (0, 1):
raise ValueError
qs = qs.filter(guanshi_profile__zhuangtai=status_val)
qs = qs.filter(GuanshiProfile__zhuangtai=status_val)
except:
return Response({'code': 400, 'msg': '状态值无效'})
@@ -2951,11 +2951,11 @@ class GetGuanliListView(APIView):
# 构建返回数据
result = []
for user in users:
guanshi = user.guanshi_profile
boss = user.boss_profile if hasattr(user, 'boss_profile') else None
guanshi = user.GuanshiProfile
boss = user.BossProfile if hasattr(user, 'BossProfile') else None
result.append({
'yonghuid': user.yonghuid,
'avatar': user.avatar or '',
'yonghuid': user.UserUID,
'avatar': user.Avatar or '',
'nickname': boss.nickname if boss else '',
'zhuangtai': guanshi.zhuangtai,
'yaogingshuliang': guanshi.yaogingshuliang,
@@ -3010,9 +3010,9 @@ class GetGuanliDetailView(APIView):
# 查询用户主表和管事扩展表
try:
user = User.query.select_related('guanshi_profile', 'boss_profile').get(UserUID=yonghuid)
guanshi = user.guanshi_profile
boss = user.boss_profile if hasattr(user, 'boss_profile') else None
user = User.query.select_related('GuanshiProfile', 'BossProfile').get(UserUID=yonghuid)
guanshi = user.GuanshiProfile
boss = user.BossProfile if hasattr(user, 'BossProfile') else None
except User.DoesNotExist:
return Response({'code': 404, 'msg': '管事不存在'})
except UserGuanshi.DoesNotExist:
@@ -3020,9 +3020,9 @@ class GetGuanliDetailView(APIView):
# 基础信息
base_data = {
'yonghuid': user.yonghuid,
'yonghuid': user.UserUID,
'nickname': boss.nickname if boss else '',
'avatar': user.avatar or '',
'avatar': user.Avatar or '',
'dianhua': guanshi.dianhua or '',
'wechat': guanshi.wechat or '',
'zhuangtai': guanshi.zhuangtai,
@@ -3132,8 +3132,8 @@ class UpdateGuanliView(APIView):
# 查询管事对象(不在事务外加锁,事务内加锁)
try:
user = User.query.get(UserUID=yonghuid)
guanshi = user.guanshi_profile
boss = user.boss_profile if hasattr(user, 'boss_profile') else None
guanshi = user.GuanshiProfile
boss = user.BossProfile if hasattr(user, 'BossProfile') else None
except User.DoesNotExist:
return Response({'code': 404, 'msg': '用户不存在'})
except UserGuanshi.DoesNotExist:
@@ -3143,8 +3143,8 @@ class UpdateGuanliView(APIView):
with transaction.atomic():
# 重新获取加锁对象
user = User.objects.select_for_update().get(UserUID=yonghuid)
guanshi = user.guanshi_profile
boss = user.boss_profile if hasattr(user, 'boss_profile') else None
guanshi = user.GuanshiProfile
boss = user.BossProfile if hasattr(user, 'BossProfile') else None
# ---------- 1. 基础信息修改需要4400a----------
nickname = request.data.get('nickname')
@@ -3176,7 +3176,7 @@ class UpdateGuanliView(APIView):
except:
pass
if avatar is not None:
user.avatar = avatar
user.Avatar = avatar
user.save()
guanshi.save()
@@ -3559,7 +3559,7 @@ class GetZuzhangListView(APIView):
# 3. 检查是否拥有组长管理所需权限6600a ~ 6600e 任一)
required_perms = ['6600a', '6600b', '6600c', '6600d', '6600e']
if not any(perm in permissions for perm in required_perms):
logger.warning(f"用户 {request.user.phone} 尝试访问组长列表但缺少必要权限,已有权限: {permissions}")
logger.warning(f"用户 {request.user.Phone} 尝试访问组长列表但缺少必要权限,已有权限: {permissions}")
return Response({'code': 403, 'msg': '您无权访问组长管理页面'})
# 4. 提取请求参数(分页 + 筛选)
@@ -3579,7 +3579,7 @@ class GetZuzhangListView(APIView):
commission_value = request.data.get('commission_value')
# 5. 构建基础 QuerySet预加载关联表
queryset = UserZuzhang.query.select_related('user', 'user__boss_profile').all()
queryset = UserZuzhang.query.select_related('user', 'user__BossProfile').all()
# 6. 应用筛选条件(使用 ORM 安全过滤)
if keyword:
@@ -3633,12 +3633,12 @@ class GetZuzhangListView(APIView):
for zu in paged_queryset:
user = zu.user
nickname = ''
if hasattr(user, 'boss_profile') and user.boss_profile:
nickname = user.boss_profile.nickname or ''
if hasattr(user, 'BossProfile') and user.BossProfile:
nickname = user.BossProfile.nickname or ''
data_list.append({
'yonghuid': user.yonghuid,
'avatar': user.avatar or '',
'yonghuid': user.UserUID,
'avatar': user.Avatar or '',
'nickname': nickname,
'fenyong_zonge': float(zu.fenyong_zonge),
'ketixian_jine': float(zu.ketixian_jine),
@@ -3702,9 +3702,9 @@ class GetZuzhangDetailView(APIView):
# 查询组长主表、扩展表、老板扩展表(昵称)
try:
user = User.query.select_related('zuzhang_profile', 'boss_profile').get(UserUID=yonghuid)
zuzhang = user.zuzhang_profile
boss = user.boss_profile if hasattr(user, 'boss_profile') else None
user = User.query.select_related('ZuzhangProfile', 'BossProfile').get(UserUID=yonghuid)
zuzhang = user.ZuzhangProfile
boss = user.BossProfile if hasattr(user, 'BossProfile') else None
except User.DoesNotExist:
return Response({'code': 404, 'msg': '组长不存在'})
except UserZuzhang.DoesNotExist:
@@ -3712,9 +3712,9 @@ class GetZuzhangDetailView(APIView):
# 基础信息
base_data = {
'yonghuid': user.yonghuid,
'yonghuid': user.UserUID,
'nickname': boss.nickname if boss else '',
'avatar': user.avatar or '',
'avatar': user.Avatar or '',
'dianhua': '', # 组长表无电话字段?组长扩展表没有电话/微信但前端可能需要可从User关联实际上组长表无电话字段这里留空或从其他表获取
'wechat': '',
'zhuangtai': zuzhang.zhuangtai,
@@ -3826,8 +3826,8 @@ class UpdateZuzhangView(APIView):
# 查询组长对象
try:
user = User.query.get(UserUID=yonghuid)
zuzhang = user.zuzhang_profile
boss = user.boss_profile if hasattr(user, 'boss_profile') else None
zuzhang = user.ZuzhangProfile
boss = user.BossProfile if hasattr(user, 'BossProfile') else None
except User.DoesNotExist:
return Response({'code': 404, 'msg': '用户不存在'})
except UserZuzhang.DoesNotExist:
@@ -3861,7 +3861,7 @@ class UpdateZuzhangView(APIView):
boss.nickname = nickname
boss.save()
if avatar:
user.avatar = avatar
user.Avatar = avatar
user.save()
# 组长表没有电话/微信字段如需存储可暂存到User或忽略
# 这里不处理
@@ -5944,7 +5944,7 @@ class FaKuanChuLiView(APIView):
return Response({'code': 400, 'msg': '无效的操作类型'})
# 记录处理者(使用 request.user 的 phone
penalty.ProcessorID = request.user.phone
penalty.ProcessorID = request.user.Phone
penalty.ProcessorIdentity = 2 # 这里假设处理者是售后身份,可根据实际业务获取
penalty.save()
@@ -6033,7 +6033,7 @@ class FaKuanChuangJianView(APIView):
penalty = Penalty.query.create(
PenalizedUserID=PenalizedUserID,
Identity=shenfen,
ApplicantID=request.user.phone, # 申请人(客服)手机号
ApplicantID=request.user.Phone, # 申请人(客服)手机号
ApplicantIdentity=2, # 售后身份
Reason=Reason,
FineAmount=FineAmount,
@@ -6134,7 +6134,7 @@ class FineApplyView(APIView):
with transaction.atomic():
penalty = Penalty.query.create(
PenalizedUserID=dashou_id,
ApplicantID=request.user.phone,
ApplicantID=request.user.Phone,
Identity=1, # 被处罚人身份1 打手
Reason=Reason,
FineAmount=FineAmount,
@@ -6204,7 +6204,7 @@ class PunishDashouView(APIView):
# 查询打手
try:
dashou_user = User.query.get(UserUID=dashou_id)
dashou = dashou_user.dashou_profile
dashou = dashou_user.DashouProfile
except User.DoesNotExist:
return Response({'code': 404, 'msg': '打手用户不存在'})
except ObjectDoesNotExist:
@@ -7984,7 +7984,7 @@ class KhgglView(APIView):
filter_bankuai_name = request.data.get('bankuai_name', '').strip()
# 基础查询:所有审核官,预加载用户及打手扩展表
queryset = UserShenheguan.query.select_related('user__dashou_profile')
queryset = UserShenheguan.query.select_related('user__DashouProfile')
if filter_yonghuid:
queryset = queryset.filter(user__UserUID=filter_yonghuid)
@@ -8015,7 +8015,7 @@ class KhgglView(APIView):
tag_ids = Chenghao.query.filter(bankuai=bk, leixing='dashou').values_list('id', flat=True)
count = YonghuChenghao.query.filter(
chenghao_id__in=tag_ids,
yonghu__dashou_profile__isnull=False
yonghu__DashouProfile__isnull=False
).values('yonghu').distinct().count()
bankuai_dashou_count_map[bk.bankuai_id] = count
@@ -8042,7 +8042,7 @@ class KhgglView(APIView):
for sg in shenheguan_list:
user = sg.user
# 获取该审核官关联的板块
bk_qs = KaoheguanBankuai.query.filter(kaoheguan_id=user.yonghuid).select_related('bankuai')
bk_qs = KaoheguanBankuai.query.filter(kaoheguan_id=user.UserUID).select_related('bankuai')
bankuai_info = []
for bk in bk_qs:
bid = bk.bankuai.bankuai_id
@@ -8052,12 +8052,12 @@ class KhgglView(APIView):
'dashou_count': bankuai_dashou_count_map.get(bid, 0)
})
dashou_nick = user.dashou_profile.nicheng if hasattr(user, 'dashou_profile') and user.dashou_profile else ''
dashou_nick = user.DashouProfile.nicheng if hasattr(user, 'DashouProfile') and user.DashouProfile else ''
data_list.append({
'yonghuid': user.yonghuid,
'avatar': user.avatar or '',
'phone': user.phone or '',
'yonghuid': user.UserUID,
'avatar': user.Avatar or '',
'phone': user.Phone or '',
'zhuangtai': sg.zhuangtai,
'shenhe_zhuangtai': sg.shenhe_zhuangtai,
'shenhe_zongshu': sg.shenhe_zongshu,
@@ -8231,7 +8231,7 @@ class ZxkfghdsView(APIView):
shangjia_id = shangjia_ext.MerchantID
# 获取商家昵称用于新订单扩展表
shangjia_user = User.query.get(UserUID=shangjia_id)
shangjia_nicheng = shangjia_user.shop_profile.nicheng or f"商家{shangjia_id}"
shangjia_nicheng = shangjia_user.ShopProfile.nicheng or f"商家{shangjia_id}"
except (ObjectDoesNotExist, UserShangjia.DoesNotExist):
logger.error(f"商家信息缺失,订单: {OrderID}")
return Response({'code': 500, 'msg': '商家数据异常'})
@@ -8240,7 +8240,7 @@ class ZxkfghdsView(APIView):
with transaction.atomic():
# 5.1 订单状态改为已退款
old_order.Status = 5
old_order.AssignedCS = current_user.phone
old_order.AssignedCS = current_user.Phone
old_order.save()
# 5.2 处理接单打手(若有)
@@ -8248,7 +8248,7 @@ class ZxkfghdsView(APIView):
if dashou_id:
try:
dashou_user = User.query.get(UserUID=dashou_id)
dashou_profile = dashou_user.dashou_profile
dashou_profile = dashou_user.DashouProfile
dashou_profile.tuikuanliang += 1 # 退款次数+1
dashou_profile.zhuangtai = 1 # 设为空闲
dashou_profile.save()
@@ -8261,7 +8261,7 @@ class ZxkfghdsView(APIView):
defaults={
'PlayerID': dashou_id or '',
'ApplicantID': '',
'ProcessorID': current_user.phone,
'ProcessorID': current_user.Phone,
'Reason': '客服更换打手退款',
'ApplyStatus': 1,
'Amount': old_order.Amount or 0,