644 lines
23 KiB
Python
644 lines
23 KiB
Python
# 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 _club_id_for_yonghuid(yonghuid, club_id=None):
|
||
from jituan.constants import CLUB_ID_DEFAULT
|
||
from jituan.services.club_user import get_user_club_id
|
||
from users.business_models import User
|
||
if club_id:
|
||
return club_id
|
||
u = User.query.filter(UserUID=yonghuid).first()
|
||
return get_user_club_id(u) if u else CLUB_ID_DEFAULT
|
||
|
||
|
||
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}")
|
||
|
||
|
||
class _AllPermissions(list):
|
||
"""拥有 000001 超级管理权限时使用,使任何权限码 in 检查都返回 True"""
|
||
def __contains__(self, item):
|
||
return True
|
||
|
||
|
||
def has_merchant_order_permission(permissions):
|
||
"""商家/链接订单处理权限:002ac 商家订单、002ab 平台订单、003aa 跨平台。"""
|
||
if isinstance(permissions, _AllPermissions):
|
||
return True
|
||
return any(p in permissions for p in ('002ac', '002ab', '003aa'))
|
||
|
||
|
||
def _legacy_backend_role_permissions(phone):
|
||
"""
|
||
读取旧权限表(backend.models):
|
||
user_role.account_id = 手机号 → role → role_permission → permission.perm_code
|
||
与 gvsdsdk 表并行,未改 SDK 结构。
|
||
"""
|
||
phone = (phone or '').strip()
|
||
if not phone:
|
||
return [], []
|
||
try:
|
||
from backend.models import (
|
||
UserRole as LegacyUserRole,
|
||
Role as BackendRole,
|
||
Permission as BackendPermission,
|
||
)
|
||
role_ids = list(
|
||
LegacyUserRole.objects.filter(account_id=phone).values_list('role_id', flat=True)
|
||
)
|
||
if not role_ids:
|
||
return [], []
|
||
perm_codes = list(
|
||
BackendPermission.objects.filter(
|
||
rolepermission__role_id__in=role_ids
|
||
).values_list('perm_code', flat=True).distinct()
|
||
)
|
||
role_names = list(
|
||
BackendRole.objects.filter(role_id__in=role_ids).values_list('role_name', flat=True)
|
||
)
|
||
return perm_codes, role_names
|
||
except Exception as e:
|
||
logger.warning('legacy_backend_role_permissions phone=%s err=%s', phone, e)
|
||
return [], []
|
||
|
||
|
||
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. 必须是客服或管理员 ----------
|
||
from jituan.constants import SUPER_ADMIN_PHONES
|
||
from jituan.services.admin_context import is_kefu_backend_account
|
||
is_super_phone = getattr(current_user, 'Phone', '') in SUPER_ADMIN_PHONES
|
||
is_admin = current_user.UserType == 'admin' or current_user.IsSuperuser or is_super_phone
|
||
if not is_kefu_backend_account(current_user):
|
||
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)
|
||
else:
|
||
# 管理员无需客服扩展表,用占位对象以通过 kefu_obj is None 检查
|
||
from types import SimpleNamespace
|
||
kefu = SimpleNamespace(user=current_user)
|
||
|
||
# ---------- 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 as e:
|
||
logger.warning(
|
||
'verify_kefu_permission failed: UserUUID=%s Phone=%s err=%s',
|
||
getattr(current_user, 'UserUUID', None),
|
||
getattr(current_user, 'Phone', None),
|
||
e,
|
||
exc_info=True,
|
||
)
|
||
permissions = []
|
||
|
||
# 旧权限表(user_role 按手机号),与 gvsdsdk 合并
|
||
legacy_codes, _ = _legacy_backend_role_permissions(current_user.Phone)
|
||
if legacy_codes:
|
||
if isinstance(permissions, _AllPermissions):
|
||
pass
|
||
else:
|
||
permissions = list(set(list(permissions) + legacy_codes))
|
||
|
||
# 管理员/超级用户自动拥有所有权限
|
||
if is_admin and '000001' not in permissions:
|
||
permissions.insert(0, '000001')
|
||
|
||
# 拥有 000001(超级管理权限)的用户自动获得所有功能权限
|
||
# 使用 _AllPermissions 包装,使任何权限码检查都自动通过
|
||
if '000001' in permissions:
|
||
permissions = _AllPermissions(permissions)
|
||
|
||
if not permissions:
|
||
logger.warning(f"用户 {current_user.Phone} 没有任何权限,请检查角色分配")
|
||
|
||
return kefu, permissions
|
||
|
||
|
||
|
||
|
||
|
||
def update_dashou_daily_by_action(yonghuid, amount, action, club_id=None):
|
||
"""
|
||
根据行为类型更新打手每日统计(接单、成交、退款)
|
||
"""
|
||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||
if amount <= 0:
|
||
return
|
||
|
||
cid = _club_id_for_yonghuid(yonghuid, club_id)
|
||
today = date.today()
|
||
|
||
with transaction.atomic():
|
||
stat, created = PlayerDailyStats.objects.select_for_update().get_or_create(
|
||
club_id=cid,
|
||
PlayerID=yonghuid,
|
||
Date=today,
|
||
defaults={
|
||
'club_id': cid,
|
||
'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, order_id=None, club_id=None):
|
||
"""
|
||
根据操作行为更新商家每日统计(派发、结算、退款)
|
||
"""
|
||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||
if amount <= 0:
|
||
return
|
||
|
||
cid = _club_id_for_yonghuid(yonghuid, club_id)
|
||
today = date.today()
|
||
|
||
with transaction.atomic():
|
||
stat, created = MerchantDailyStats.objects.select_for_update().get_or_create(
|
||
club_id=cid,
|
||
MerchantID=yonghuid,
|
||
Date=today,
|
||
defaults={
|
||
'club_id': cid,
|
||
'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)
|
||
|
||
if order_id:
|
||
try:
|
||
from merchant_ops.services.stats import sync_staff_stats_by_order_id
|
||
sync_staff_stats_by_order_id(order_id, amount, action)
|
||
except Exception:
|
||
logger.warning('同步客服日统计失败 order_id=%s', order_id, exc_info=True)
|
||
|
||
|
||
def update_guanshi_daily_by_action(yonghuid, action, amount=Decimal('0.00'), club_id=None):
|
||
"""
|
||
根据操作行为更新管事每日统计(邀请打手、充值会员、订单/押金分红)
|
||
"""
|
||
cid = _club_id_for_yonghuid(yonghuid, club_id)
|
||
today = date.today()
|
||
|
||
with transaction.atomic():
|
||
stat, created = ManagerDailyStats.objects.select_for_update().get_or_create(
|
||
club_id=cid,
|
||
ManagerID=yonghuid,
|
||
Date=today,
|
||
defaults={
|
||
'club_id': cid,
|
||
'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'), club_id=None):
|
||
"""管事续费统计(单独表)"""
|
||
cid = _club_id_for_yonghuid(yonghuid, club_id)
|
||
today = date.today()
|
||
with transaction.atomic():
|
||
stat, created = ManagerRenewalDailyStats.objects.select_for_update().get_or_create(
|
||
club_id=cid,
|
||
ManagerID=yonghuid,
|
||
Date=today,
|
||
defaults={
|
||
'club_id': cid,
|
||
'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'), club_id=None):
|
||
"""根据操作行为更新组长每日统计"""
|
||
cid = _club_id_for_yonghuid(yonghuid, club_id)
|
||
today = date.today()
|
||
|
||
with transaction.atomic():
|
||
stat, created = LeaderDailyStats.objects.select_for_update().get_or_create(
|
||
club_id=cid,
|
||
LeaderID=yonghuid,
|
||
Date=today,
|
||
defaults={
|
||
'club_id': cid,
|
||
'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, club_id=None):
|
||
"""
|
||
更新每日提现统计(平台维度,按俱乐部)
|
||
leixing: 1=打手, 2=管事, 3=组长
|
||
amount: 本次提现金额(申请金额,含手续费)
|
||
"""
|
||
from jituan.constants import CLUB_ID_DEFAULT
|
||
from jituan.services.withdraw_config import get_or_create_platform_daily_stat
|
||
|
||
club_id = (club_id or '').strip() or CLUB_ID_DEFAULT
|
||
today = date.today()
|
||
with transaction.atomic():
|
||
stat = get_or_create_platform_daily_stat(club_id, leixing, today)
|
||
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
|
||
|
||
|
||
def fmt_datetime(dt):
|
||
"""格式化时间为前端通用字符串。"""
|
||
if not dt:
|
||
return ''
|
||
return dt.strftime('%Y-%m-%d %H:%M:%S')
|
||
|
||
|
||
def datetime_aliases(dt):
|
||
"""同时返回 CreateTime / create_time,避免前后端字段名不一致。"""
|
||
s = fmt_datetime(dt)
|
||
return {'CreateTime': s, 'create_time': s}
|
||
|
||
|
||
def pick_order_id(data):
|
||
"""兼容前端 dingdan_id 与 SDK 字段 OrderID。"""
|
||
return str(data.get('OrderID') or data.get('dingdan_id') or '').strip()
|
||
|
||
|
||
def pick_leixing_id(data):
|
||
"""兼容前端 leixing_id 与 SDK 字段 ProductTypeID。"""
|
||
val = data.get('leixing_id')
|
||
if val is None or val == '':
|
||
val = data.get('ProductTypeID')
|
||
if val is None or val == '':
|
||
return None
|
||
try:
|
||
return int(val)
|
||
except (ValueError, TypeError):
|
||
return None
|
||
|
||
|
||
def pick_penalty_create_params(data):
|
||
"""兼容罚单创建:SDK 字段名与旧版拼音字段名。"""
|
||
penalized_id = str(data.get('PenalizedUserID') or data.get('beichufa_id') or '').strip()
|
||
reason = str(data.get('Reason') or data.get('chufaliyou') or '').strip()
|
||
fine_amount = data.get('FineAmount')
|
||
if fine_amount is None:
|
||
fine_amount = data.get('fakuanjine')
|
||
affects = data.get('AffectsGrabbing')
|
||
if affects is None:
|
||
affects = data.get('yingxiang_qiangdan', 1)
|
||
return penalized_id, reason, fine_amount, affects
|
||
|
||
|
||
# ---------- 修改记录(Xiugaijilu) ----------
|
||
XIUGAI_LEIXING_DASHOU = 2
|
||
XIUGAI_LEIXING_GUANSHI = 3
|
||
XIUGAI_LEIXING_SHANGJIA = 4
|
||
XIUGAI_LEIXING_ZUZHANG = 5
|
||
|
||
XIUGAI_LEIXING_LABEL = {
|
||
XIUGAI_LEIXING_DASHOU: '打手',
|
||
XIUGAI_LEIXING_GUANSHI: '管事',
|
||
XIUGAI_LEIXING_SHANGJIA: '商家',
|
||
XIUGAI_LEIXING_ZUZHANG: '组长',
|
||
}
|
||
|
||
|
||
def write_xiugai_log(*, yonghuid, xiugaiid, leixing, qitashuoming, club_id=None, request_ip='', **extra_fields):
|
||
"""
|
||
统一写入 xiugaijilu 修改记录。
|
||
qitashuoming 应详细描述本次操作(尤其是金额、身份、上下级变更等)。
|
||
extra_fields 可传入 xiugaitijiao、shangjiayue、guanshiyue 等模型字段。
|
||
"""
|
||
from users.models import Xiugaijilu
|
||
from jituan.services.admin_audit import log_admin_audit_from_xiugai
|
||
|
||
if not qitashuoming:
|
||
label = XIUGAI_LEIXING_LABEL.get(leixing, f'类型{leixing}')
|
||
qitashuoming = f'【后台】修改{label}信息,操作人{xiugaiid}'
|
||
|
||
Xiugaijilu.query.create(
|
||
yonghuid=str(yonghuid),
|
||
xiugaiid=str(xiugaiid),
|
||
leixing=leixing,
|
||
qitashuoming=qitashuoming,
|
||
**extra_fields,
|
||
)
|
||
log_admin_audit_from_xiugai(
|
||
yonghuid=yonghuid,
|
||
xiugaiid=xiugaiid,
|
||
leixing=leixing,
|
||
qitashuoming=qitashuoming,
|
||
club_id=club_id,
|
||
request_ip=request_ip,
|
||
) |