P1 性能优化(避免循环内 N+1 / N 次 DB 查询): - shop_order_views._batch_images: 移除 ThreadPoolExecutor 掩盖的 N 次查询,改单次批量 + Python 侧分组 - product_query.DashouHuiyuanList: 5N 查询 -> 3 次批量预取 + 本地闭包判断 - roles.GetRolePermissionView / 用户列表: 循环内 RolePermission/Permission/UserRole/Role -> 批量 __in 预取 - guanli / zuzhang 杜次分红 4.2/4.3: 循环内 update_or_create + 单条 delete -> bulk_create + bulk_update + 批量 delete - admin_config QQ 群配置: 循环内 exists/first/save/create -> 批量预取 + bulk_create + bulk_update - jituan 服务层多处合并统计查询、批量预取 map - rank 多处循环 N+1 改批量预取 - backend 多处循环内 count/create 改批量 - config/orders/merchant_ops 多处循环 N+1 改批量预取 其他改动: - users/views/kefu.py 拆分为 kefu_base/kefu_dashou/kefu_orders/kefu_punishment/kefu_withdraw 5 个文件 - 删除遗留脚本 check_prod_uid.py / create_rbac_tables.sql
565 lines
22 KiB
Python
565 lines
22 KiB
Python
"""users.views.kefu_punishment - auto-generated by split script."""
|
||
"""users.views.kefu - auto-generated by split script."""
|
||
import os
|
||
import sys
|
||
import json
|
||
import re
|
||
import time
|
||
import uuid
|
||
import random
|
||
import string
|
||
import requests
|
||
import hashlib
|
||
import traceback
|
||
import decimal
|
||
import jwt
|
||
import ipaddress
|
||
import xmltodict
|
||
from datetime import datetime, timedelta
|
||
from decimal import Decimal
|
||
import defusedxml.ElementTree as ET
|
||
|
||
from django.conf import settings
|
||
from django.db import models, transaction, IntegrityError, DatabaseError, connection
|
||
from django.db.models import Q, F, Sum, Count, Case, When, IntegerField, Prefetch
|
||
from django.core.cache import cache
|
||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||
from django.core.exceptions import ObjectDoesNotExist
|
||
from django.shortcuts import get_object_or_404
|
||
from django.utils import timezone
|
||
from django.http import HttpResponse
|
||
from django.views import View
|
||
from django.views.decorators.csrf import csrf_exempt
|
||
from django.contrib.auth.hashers import make_password
|
||
|
||
from rest_framework.views import APIView
|
||
from rest_framework.response import Response
|
||
from rest_framework import permissions, status
|
||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
|
||
from rest_framework.throttling import AnonRateThrottle
|
||
from rest_framework_simplejwt.tokens import RefreshToken
|
||
from rest_framework_simplejwt.authentication import JWTAuthentication
|
||
|
||
from gvsdsdk.fluent import db, func, FQ
|
||
|
||
from utils.oss_utils import validate_image, upload_to_oss, delete_from_oss
|
||
from utils.money import yuan_to_fen
|
||
from utils.fadan_utils import check_fadan_qiangdan_eligible
|
||
from utils.invitationcode_utils import CreateInvitationCode, VerifyInvitationCode
|
||
from users.fadan_fenhong_utils import process_fadan_fenhong
|
||
|
||
from orders.utils import (
|
||
update_daily_payout,
|
||
settle_shangjia_order_guanshi_fenhong
|
||
)
|
||
from backend.utils import (
|
||
update_dashou_daily_by_action, update_guanshi_daily_by_action,
|
||
update_shangjia_daily, update_zuzhang_daily_by_action,
|
||
verify_kefu_permission
|
||
)
|
||
from rank.utils import get_tag_fee, create_shenhe_jilu, validate_shenheguan
|
||
|
||
from ..models import (
|
||
UserBoss, UserDashou, UserShangjia, UserGuanshi,
|
||
UserZuzhang, UserKefu, AdminProfile, OfficialAccountUser,
|
||
TixianAutoRecord, TixianShenheJilu, Tixianjilu, Xiugaijilu,
|
||
RankingRecord
|
||
)
|
||
from users.business_models import User
|
||
from ..tixian_shenhe_services import (
|
||
load_audit_meta_map, refund_balance, sync_audit_and_jilu_status,
|
||
mark_transfer_success, release_collect_quota_for_record,
|
||
close_audit_wechat_failed, query_wechat_transfer_bill,
|
||
check_shijidaozhang_within_limit,
|
||
check_dashou_trial_blocks_commission_withdraw,
|
||
WX_STATE_FAIL, WX_STATE_SUCCESS, WX_STATE_WAIT, WX_STATE_CANCELING,
|
||
)
|
||
from ..tixian_shenhe_views import process_audit_collect
|
||
|
||
from orders.models import (
|
||
Order, PlatformOrderExt, MerchantOrderExt, CommissionRate,
|
||
PenaltyRecord, PenaltyEvidenceImage, RefundRecord, PlayerDeliveryImage,
|
||
Penalty, PenaltyAppealImage
|
||
)
|
||
from products.models import (
|
||
ShangpinLeixing, Huiyuan, Huiyuangoumai, Gsfenhong, Czjilu
|
||
)
|
||
from config.models import Qunpeizhi
|
||
from backend.models import MerchantDailyStats
|
||
from rank.models import (
|
||
KaohePayTemp, Chenghao, ShenheJilu,
|
||
KaoheCishuFeiyong, YonghuChenghao
|
||
)
|
||
from users.models import UserShenheguan
|
||
from products.utils import update_shangpin_daily_stat
|
||
from shop.utils import update_dianpu_daily_stat
|
||
from rank.utils import create_shenhe_jilu_from_temp
|
||
|
||
import logging
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class KefuPunishmentListView(APIView):
|
||
"""
|
||
客服获取处罚记录列表接口
|
||
请求:POST /yonghu/kefu_cfgl
|
||
参数:{
|
||
"phone": "客服手机号",
|
||
"status": [0,3] 或 [1,2], # 状态数组
|
||
"dingdan_id": "订单ID(可选)",
|
||
"dashouid": "打手ID(可选)",
|
||
"page": 1,
|
||
"page_size": 20
|
||
}
|
||
认证:JWT
|
||
返回:{
|
||
"code": 0,
|
||
"data": {
|
||
"list": [处罚记录],
|
||
"total": 总数,
|
||
"stats": {
|
||
"zongshu": 总记录数,
|
||
"daichuli": 待处理数,
|
||
"yichuli": 已处理数
|
||
}
|
||
}
|
||
}
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
parser_classes = [JSONParser]
|
||
|
||
@staticmethod
|
||
def _parse_status_list(raw):
|
||
"""兼容 status=[0,3]、\"0,3\"、单数字;空/all 表示不过滤。"""
|
||
if raw is None:
|
||
return None
|
||
if isinstance(raw, str):
|
||
s = raw.strip().lower()
|
||
if not s or s in ('all', 'null', 'none'):
|
||
return None
|
||
if s.startswith('['):
|
||
import json
|
||
try:
|
||
raw = json.loads(s)
|
||
except (TypeError, ValueError):
|
||
return None
|
||
else:
|
||
raw = [x.strip() for x in s.split(',') if x.strip()]
|
||
if isinstance(raw, (int, float)):
|
||
return [int(raw)]
|
||
if isinstance(raw, list):
|
||
out = []
|
||
for x in raw:
|
||
try:
|
||
out.append(int(x))
|
||
except (TypeError, ValueError):
|
||
continue
|
||
return out or None
|
||
return None
|
||
|
||
def post(self, request):
|
||
# 1. 获取参数
|
||
phone = request.data.get('phone', '').strip()
|
||
status_list = self._parse_status_list(request.data.get('status'))
|
||
dingdan_id = request.data.get('dingdan_id', '').strip()
|
||
dashouid = request.data.get('dashouid', '').strip()
|
||
try:
|
||
page = int(request.data.get('page', 1))
|
||
page_size = int(request.data.get('page_size', 20))
|
||
except ValueError:
|
||
return Response({'code': 400, 'msg': '分页参数错误'}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
if not phone:
|
||
return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
# 2. 客服身份验证
|
||
current_user = request.user
|
||
# 1. 获取前端参数
|
||
username = request.data.get('phone', '').strip()
|
||
if not username:
|
||
return Response({'code': 400, 'msg': '缺少username'})
|
||
|
||
# 2. 公共权限校验
|
||
kefu, permissions = verify_kefu_permission(request, username)
|
||
if kefu is None:
|
||
return permissions
|
||
|
||
# 3. 检查平台订单管理权限(002ab)
|
||
if '005aa' not in permissions:
|
||
return Response({'code': 403, 'msg': '您无权查看平台订单列表'})
|
||
|
||
# 3. 获取统计信息(不分页)
|
||
from jituan.services.club_penalty import filter_penalty_record_qs
|
||
from jituan.services.club_user_access import list_response_meta
|
||
|
||
base_qs = filter_penalty_record_qs(PenaltyRecord.query.all(), request)
|
||
agg = base_qs.aggregate(
|
||
zongshu=Count('id'),
|
||
daichuli=Count('id', filter=Q(ApplyStatus__in=[0, 3])),
|
||
yichuli=Count('id', filter=Q(ApplyStatus__in=[1, 2])),
|
||
)
|
||
stats = {
|
||
'zongshu': agg['zongshu'],
|
||
'daichuli': agg['daichuli'],
|
||
'yichuli': agg['yichuli'],
|
||
}
|
||
|
||
queryset = base_qs
|
||
|
||
if status_list and isinstance(status_list, list):
|
||
queryset = queryset.filter(ApplyStatus__in=status_list)
|
||
|
||
if dingdan_id:
|
||
queryset = queryset.filter(OrderID=dingdan_id)
|
||
|
||
if dashouid:
|
||
queryset = queryset.filter(PlayerID=dashouid)
|
||
|
||
# 5. 分页
|
||
page_obj = queryset.order_by('-CreateTime').paginate(page=page, per_page=page_size)
|
||
total = page_obj.total
|
||
records = page_obj.items
|
||
|
||
# 6. 构建返回列表
|
||
list_data = []
|
||
for item in records:
|
||
ct = item.CreateTime
|
||
ut = item.UpdateTime
|
||
list_data.append({
|
||
'dingdan_id': item.OrderID,
|
||
'dashouid': item.PlayerID,
|
||
'qingqiuid': item.ApplicantID,
|
||
'cfliyou': item.PenaltyReason,
|
||
'sqzhuangtai': item.ApplyStatus,
|
||
'jifen': item.DeductedPoints,
|
||
'CreateTime': ct,
|
||
'UpdateTime': ut,
|
||
'create_time': ct.isoformat() if ct else '',
|
||
'update_time': ut.isoformat() if ut else '',
|
||
})
|
||
|
||
return Response({
|
||
'code': 0,
|
||
'data': {
|
||
'list': list_data,
|
||
'total': total,
|
||
'stats': stats,
|
||
**list_response_meta(request),
|
||
}
|
||
})
|
||
|
||
|
||
class KefuPunishmentDetailView(APIView):
|
||
"""
|
||
客服获取处罚详情接口
|
||
请求:POST /yonghu/kefu_cf_detail
|
||
参数:{
|
||
"phone": "客服手机号",
|
||
"dingdan_id": "订单ID"
|
||
}
|
||
认证:JWT
|
||
返回:code=0 + 处罚记录详情(含图片)
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
parser_classes = [JSONParser]
|
||
|
||
def post(self, request):
|
||
phone = request.data.get('phone', '').strip()
|
||
dingdan_id = request.data.get('dingdan_id', '').strip()
|
||
|
||
if not phone or not dingdan_id:
|
||
return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
# 客服身份验证
|
||
current_user = request.user
|
||
if getattr(current_user, 'Phone', '') != phone:
|
||
logger.warning(f"手机号不匹配: {phone}")
|
||
return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
if current_user.UserType not in ('kefu', 'admin'):
|
||
logger.warning(f"用户类型非客服: {current_user.UserUID}")
|
||
return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
is_admin = current_user.UserType == 'admin' or current_user.IsSuperuser
|
||
kefu = None
|
||
if not is_admin:
|
||
try:
|
||
kefu = current_user.KefuProfile
|
||
except ObjectDoesNotExist:
|
||
logger.warning(f"客服扩展表不存在: {current_user.UserUID}")
|
||
return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
if kefu.zhuangtai != 1:
|
||
logger.warning(f"客服账号已禁用: {current_user.UserUID}")
|
||
return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
# 查询处罚记录(按创建时间倒序取最新一条)
|
||
try:
|
||
record = PenaltyRecord.query.filter(OrderID=dingdan_id).order_by('-CreateTime').first()
|
||
if not record:
|
||
return Response({'code': 404, 'msg': '处罚记录不存在'}, status=status.HTTP_404_NOT_FOUND)
|
||
except Exception as e:
|
||
logger.error(f"查询处罚记录失败: {e}")
|
||
return Response({'code': 500, 'msg': '服务器内部错误'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
# 查询商家证据图片
|
||
zhengju_images = []
|
||
shensu_images = []
|
||
try:
|
||
# 商家证据图片:上传者为请求人 (ApplicantID)
|
||
zhengju_qs = PenaltyEvidenceImage.query.filter(OrderID=dingdan_id, UserID=record.ApplicantID)
|
||
zhengju_images = [item.ImageURL for item in zhengju_qs if item.ImageURL]
|
||
|
||
# 打手申诉图片:上传者为打手 (PlayerID)
|
||
shensu_qs = PenaltyEvidenceImage.query.filter(OrderID=dingdan_id, UserID=record.PlayerID)
|
||
shensu_images = [item.ImageURL for item in shensu_qs if item.ImageURL]
|
||
except Exception as e:
|
||
logger.error(f"查询图片失败: {e}")
|
||
|
||
# 构建返回数据
|
||
data = {
|
||
'dingdan_id': record.OrderID,
|
||
'dashouid': record.PlayerID,
|
||
'qingqiuid': record.ApplicantID,
|
||
'chuliid': record.ProcessorID,
|
||
'cfliyou': record.PenaltyReason,
|
||
'sqzhuangtai': record.ApplyStatus,
|
||
'bhliyou': record.RejectReason,
|
||
'jifen': record.DeductedPoints,
|
||
'ssliyou': record.AppealReason or '',
|
||
'zhengju_tupian': zhengju_images,
|
||
'shensu_tupian': shensu_images,
|
||
'CreateTime': record.CreateTime,
|
||
'UpdateTime': record.UpdateTime,
|
||
}
|
||
|
||
return Response({'code': 0, 'data': data})
|
||
|
||
|
||
class KefuPunishmentActionView(APIView):
|
||
"""
|
||
客服同意/驳回处罚接口
|
||
请求:POST /yonghu/kefu_cf_action
|
||
参数:{
|
||
"phone": "客服手机号",
|
||
"dingdan_id": "订单ID",
|
||
"action": 1(同意) / 2(驳回),
|
||
"reject_reason": "驳回理由(驳回时必填)"
|
||
}
|
||
认证:JWT
|
||
返回:code=0 成功
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
parser_classes = [JSONParser]
|
||
|
||
def post(self, request):
|
||
phone = request.data.get('phone', '').strip()
|
||
dingdan_id = request.data.get('dingdan_id', '').strip()
|
||
action = request.data.get('action')
|
||
reject_reason = request.data.get('reject_reason', '').strip()
|
||
|
||
if not phone or not dingdan_id or action not in [1, 2]:
|
||
return Response({'code': 401, 'msg': '参数错误'}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
if action == 2 and not reject_reason:
|
||
return Response({'code': 400, 'msg': '驳回理由不能为空'}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 客服身份验证(完整验证,此处略)
|
||
current_user = request.user
|
||
if getattr(current_user, 'Phone', '') != phone:
|
||
return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED)
|
||
if current_user.UserType not in ('kefu', 'admin'):
|
||
return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED)
|
||
is_admin = current_user.UserType == 'admin' or current_user.IsSuperuser
|
||
kefu = None
|
||
if not is_admin:
|
||
try:
|
||
kefu = current_user.KefuProfile
|
||
if kefu.zhuangtai != 1:
|
||
return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED)
|
||
except ObjectDoesNotExist:
|
||
return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
# 查询待处理的处罚记录(状态0或3)
|
||
try:
|
||
record = PenaltyRecord.query.filter(
|
||
OrderID=dingdan_id,
|
||
ApplyStatus__in=[0, 3]
|
||
).order_by('-CreateTime').first()
|
||
if not record:
|
||
exists = PenaltyRecord.query.filter(OrderID=dingdan_id).exists()
|
||
if exists:
|
||
return Response({'code': 400, 'msg': '该处罚记录已处理'}, status=status.HTTP_400_BAD_REQUEST)
|
||
else:
|
||
return Response({'code': 404, 'msg': '处罚记录不存在'}, status=status.HTTP_404_NOT_FOUND)
|
||
except Exception as e:
|
||
logger.error(f"查询处罚记录失败: {e}")
|
||
return Response({'code': 500, 'msg': '服务器内部错误'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
with transaction.atomic():
|
||
# 处理重复记录:同一订单的其他待处理/申诉中记录标记为驳回
|
||
duplicate_records = PenaltyRecord.query.filter(
|
||
OrderID=dingdan_id,
|
||
ApplyStatus__in=[0, 3]
|
||
).exclude(id=record.id)
|
||
for dup in duplicate_records:
|
||
dup.ApplyStatus = 2
|
||
dup.ProcessorID = phone
|
||
dup.RejectReason = "系统自动驳回:存在重复处罚记录"
|
||
dup.save()
|
||
logger.info(f"自动驳回重复处罚记录 ID={dup.id}")
|
||
|
||
if action == 1: # 同意
|
||
# 可选验证打手存在
|
||
if record.PlayerID:
|
||
try:
|
||
dashou = UserDashou.query.get(user__UserUID=record.PlayerID)
|
||
except UserDashou.DoesNotExist:
|
||
return Response({'code': 400, 'msg': '被处罚打手不存在'}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
record.ApplyStatus = 1
|
||
record.ProcessorID = phone
|
||
record.save()
|
||
|
||
# 更新商家订单扩展表
|
||
try:
|
||
dingdan = Order.query.select_related('shangjia_kuozhan').get(OrderID=dingdan_id)
|
||
if hasattr(dingdan, 'shangjia_kuozhan'):
|
||
dingdan.shangjia_kuozhan.PenaltyApplyStatus = 1
|
||
dingdan.shangjia_kuozhan.save()
|
||
except Exception as e:
|
||
logger.warning(f"更新商家扩展表失败: {e}")
|
||
|
||
elif action == 2: # 驳回
|
||
# 返还积分
|
||
if record.DeductedPoints > 0 and record.PlayerID:
|
||
try:
|
||
dashou = UserDashou.query.get(user__UserUID=record.PlayerID)
|
||
if dashou.jifen <= 1000: # 安全限制
|
||
dashou.jifen += record.DeductedPoints
|
||
dashou.save()
|
||
logger.info(f"驳回处罚,打手{record.PlayerID}返还积分{record.DeductedPoints}")
|
||
else:
|
||
logger.warning(f"打手积分异常({dashou.jifen}),不再返还")
|
||
except UserDashou.DoesNotExist:
|
||
logger.warning(f"打手{record.PlayerID}不存在,无法返还积分")
|
||
|
||
record.ApplyStatus = 2
|
||
record.ProcessorID = phone
|
||
record.RejectReason = reject_reason
|
||
record.save()
|
||
|
||
try:
|
||
dingdan = Order.query.select_related('shangjia_kuozhan').get(OrderID=dingdan_id)
|
||
if hasattr(dingdan, 'shangjia_kuozhan'):
|
||
dingdan.shangjia_kuozhan.PenaltyApplyStatus = 2
|
||
dingdan.shangjia_kuozhan.RejectReason = reject_reason
|
||
dingdan.shangjia_kuozhan.save()
|
||
except Exception as e:
|
||
logger.warning(f"更新商家扩展表失败: {e}")
|
||
|
||
return Response({'code': 0, 'msg': '处理成功'})
|
||
|
||
|
||
class KefuPunishView(APIView):
|
||
"""
|
||
客服处罚打手接口
|
||
请求:POST /yonghu/kefuchufa
|
||
参数:{
|
||
"phone": "客服手机号",
|
||
"dingdan_id": "订单ID",
|
||
"reason": "处罚原因(可选)"
|
||
}
|
||
认证:JWT
|
||
返回:code=0 成功
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
parser_classes = [JSONParser]
|
||
|
||
def post(self, request):
|
||
# 1. 获取参数
|
||
phone = request.data.get('phone', '').strip()
|
||
dingdan_id = request.data.get('dingdan_id', '').strip()
|
||
reason = request.data.get('reason', '').strip()
|
||
|
||
if not phone or not dingdan_id:
|
||
return Response({'code': 401, 'msg': '参数不完整'}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 2. 客服身份验证
|
||
current_user = request.user
|
||
if getattr(current_user, 'Phone', '') != phone:
|
||
logger.warning(f"手机号不匹配: {phone}")
|
||
return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
if current_user.UserType not in ('kefu', 'admin'):
|
||
logger.warning(f"用户类型非客服: {current_user.UserUID}")
|
||
return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
is_admin = current_user.UserType == 'admin' or current_user.IsSuperuser
|
||
kefu = None
|
||
if not is_admin:
|
||
try:
|
||
kefu = current_user.KefuProfile
|
||
except ObjectDoesNotExist:
|
||
logger.warning(f"客服扩展表不存在: {current_user.UserUID}")
|
||
return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
if kefu.zhuangtai != 1:
|
||
logger.warning(f"客服账号已禁用: {current_user.UserUID}")
|
||
return Response({'code': 401, 'msg': '认证失败'}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
# 3. 查询订单
|
||
try:
|
||
order = Order.query.get(OrderID=dingdan_id)
|
||
except Order.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '订单不存在'}, status=status.HTTP_404_NOT_FOUND)
|
||
|
||
# 4. 获取打手ID
|
||
dashou_id = order.PlayerID
|
||
if not dashou_id:
|
||
return Response({'code': 400, 'msg': '订单未接单,无法处罚打手'}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 5. 查询打手
|
||
try:
|
||
dashou_user = User.query.select_related('DashouProfile').get(UserUID=dashou_id)
|
||
dashou = dashou_user.DashouProfile
|
||
except User.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '打手用户不存在'}, status=status.HTTP_404_NOT_FOUND)
|
||
except ObjectDoesNotExist:
|
||
return Response({'code': 400, 'msg': '该用户不是打手'}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 6. 使用事务
|
||
from jituan.services.club_penalty import resolve_penalty_record_club_id
|
||
with transaction.atomic():
|
||
# 创建处罚记录(积分扣除5分)
|
||
jifen = 5
|
||
chufa = PenaltyRecord.query.create(
|
||
PlayerID=dashou_id,
|
||
ApplicantID=phone, # 客服手机号作为请求人
|
||
PenaltyReason=reason or '',
|
||
ApplyStatus=0, # 待处理
|
||
DeductedPoints=jifen,
|
||
OrderID=dingdan_id,
|
||
ClubID=resolve_penalty_record_club_id(
|
||
order_id=dingdan_id,
|
||
player_id=dashou_id,
|
||
request=request,
|
||
user=request.user,
|
||
),
|
||
)
|
||
|
||
# 扣除打手积分
|
||
if dashou.jifen >= jifen:
|
||
dashou.jifen -= jifen
|
||
else:
|
||
dashou.jifen = 0
|
||
dashou.save()
|
||
|
||
# 可选:更新订单的 clkf 字段记录处理客服
|
||
order.AssignedCS = phone
|
||
order.save()
|
||
|
||
return Response({'code': 0, 'msg': '处罚成功'})
|