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
831 lines
32 KiB
Python
831 lines
32 KiB
Python
"""users.views.chufa - 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 DashouPermission:
|
||
"""
|
||
打手权限验证类
|
||
确保用户是打手且账号状态正常
|
||
"""
|
||
|
||
def has_permission(self, request, view):
|
||
user = request.user
|
||
|
||
# 检查用户是否已认证
|
||
if not user.is_authenticated:
|
||
return False
|
||
|
||
try:
|
||
# 通过反向关系获取打手扩展信息
|
||
dashou_profile = user.DashouProfile
|
||
|
||
# 检查账号状态是否为1(正常)
|
||
if dashou_profile.zhanghaozhuangtai != 1:
|
||
return False
|
||
|
||
return True
|
||
except UserDashou.DoesNotExist:
|
||
return False
|
||
except Exception as e:
|
||
logger.error(f"打手权限验证异常: {e}")
|
||
return False
|
||
|
||
|
||
class ChufaJiluHuoquView(APIView):
|
||
"""
|
||
打手获取处罚记录接口
|
||
路径: /yonghu/dshqcfjl
|
||
方法: POST
|
||
权限: JWT Token + 打手权限验证
|
||
"""
|
||
permission_classes = [IsAuthenticated, DashouPermission]
|
||
|
||
def post(self, request):
|
||
try:
|
||
# 获取当前用户信息
|
||
user_main = request.user
|
||
yonghuid = user_main.UserUID
|
||
|
||
# 通过反向关系获取打手扩展信息
|
||
try:
|
||
dashou_profile = user_main.DashouProfile
|
||
# 再次确认账号状态
|
||
if dashou_profile.zhanghaozhuangtai != 1:
|
||
return Response({
|
||
'code': 3,
|
||
'msg': '账号已被封禁,无法查看处罚记录',
|
||
'data': None
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
except UserDashou.DoesNotExist:
|
||
return Response({
|
||
'code': 2,
|
||
'msg': '用户不是打手身份',
|
||
'data': None
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
# 判断请求类型:统计信息 or 记录列表
|
||
qingqiu_tongji = request.data.get('qingqiu_tongji', False)
|
||
|
||
if qingqiu_tongji:
|
||
# 返回统计信息
|
||
return self.get_tongji_info(yonghuid)
|
||
else:
|
||
# 返回处罚记录列表
|
||
return self.get_chufa_list(yonghuid, request.data)
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取处罚记录异常 - 用户ID: {yonghuid}, 错误: {str(e)}", exc_info=True)
|
||
return Response({
|
||
'code': 99,
|
||
'msg': '系统繁忙,请稍后重试',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
def get_tongji_info(self, yonghuid):
|
||
"""
|
||
获取统计信息(总记录、待处理、已处理)
|
||
"""
|
||
try:
|
||
# 使用单个查询获取所有统计信息
|
||
stats = PenaltyRecord.query.filter(PlayerID=yonghuid).aggregate(
|
||
# 总记录数
|
||
zongshu=Count('id'),
|
||
|
||
# 待处理数:状态为0(待处罚)和3(申诉中)
|
||
daichuli=Count(
|
||
Case(
|
||
When(Q(ApplyStatus=0) | Q(ApplyStatus=3), then=1),
|
||
output_field=IntegerField()
|
||
)
|
||
),
|
||
|
||
# 已处理数:状态为1(已处罚)和2(已驳回)
|
||
yichuli=Count(
|
||
Case(
|
||
When(Q(ApplyStatus=1) | Q(ApplyStatus=2), then=1),
|
||
output_field=IntegerField()
|
||
)
|
||
)
|
||
)
|
||
|
||
# 如果查询结果为None,则设置为0
|
||
tongji_data = {
|
||
'zongshu': stats.get('zongshu', 0) or 0,
|
||
'daichuli': stats.get('daichuli', 0) or 0,
|
||
'yichuli': stats.get('yichuli', 0) or 0
|
||
}
|
||
|
||
return Response({
|
||
'code': 0,
|
||
'msg': '获取统计信息成功',
|
||
'data': tongji_data
|
||
})
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取统计信息异常 - 用户ID: {yonghuid}, 错误: {str(e)}")
|
||
return Response({
|
||
'code': 1,
|
||
'msg': '获取统计信息失败',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
def get_chufa_list(self, yonghuid, request_data):
|
||
"""
|
||
获取处罚记录列表(高性能批量查询)
|
||
🔴 只修改图片查询逻辑,字段保持原样
|
||
"""
|
||
try:
|
||
# 获取分页参数
|
||
page = int(request_data.get('page', 1))
|
||
page_size = int(request_data.get('page_size', 10))
|
||
|
||
# 限制最大每页数量,防止恶意请求
|
||
if page_size > 50:
|
||
page_size = 50
|
||
if page_size < 1:
|
||
page_size = 10
|
||
|
||
# 获取状态筛选参数
|
||
zhuangtai = request_data.get('zhuangtai', 0)
|
||
|
||
# 🔴 构建查询条件 - 打手端根据PlayerID查询
|
||
queryset = PenaltyRecord.query.filter(PlayerID=yonghuid)
|
||
|
||
if zhuangtai == 0:
|
||
# 待处理:状态为0(待处罚)和3(申诉中)
|
||
queryset = queryset.filter(Q(ApplyStatus=0) | Q(ApplyStatus=3))
|
||
elif zhuangtai == 1:
|
||
# 已处理:状态为1(已处罚)和2(已驳回)
|
||
queryset = queryset.filter(Q(ApplyStatus=1) | Q(ApplyStatus=2))
|
||
else:
|
||
# 默认返回所有状态
|
||
pass
|
||
|
||
# 按创建时间倒序排列(最新在前)
|
||
queryset = queryset.order_by('-CreateTime')
|
||
|
||
# 使用游标分页,避免传统分页的性能问题
|
||
paginator = Paginator(queryset, page_size)
|
||
|
||
try:
|
||
page_obj = paginator.page(page)
|
||
except:
|
||
# 页码超出范围,返回空列表
|
||
return Response({
|
||
'code': 0,
|
||
'msg': '获取成功',
|
||
'data': {
|
||
'list': [],
|
||
'has_more': False,
|
||
'page': page,
|
||
'page_size': page_size,
|
||
'current_count': 0
|
||
}
|
||
})
|
||
|
||
# 获取当前页的记录
|
||
chufa_records = list(page_obj)
|
||
|
||
if not chufa_records:
|
||
# 没有数据直接返回
|
||
return Response({
|
||
'code': 0,
|
||
'msg': '获取成功',
|
||
'data': {
|
||
'list': [],
|
||
'has_more': False,
|
||
'page': page,
|
||
'page_size': page_size,
|
||
'current_count': 0
|
||
}
|
||
})
|
||
|
||
# 🔴【核心修复】收集所有需要查询图片的组合
|
||
dingdan_ids = []
|
||
shangjia_ids = []
|
||
|
||
for record in chufa_records:
|
||
if record.OrderID:
|
||
dingdan_ids.append(record.OrderID)
|
||
if record.ApplicantID: # 商家ID
|
||
shangjia_ids.append(record.ApplicantID)
|
||
|
||
# 去重
|
||
dingdan_ids = list(set(dingdan_ids))
|
||
shangjia_ids = list(set(shangjia_ids))
|
||
|
||
# 🔴【核心修复】分别查询两种图片(避免if-elif逻辑错误)
|
||
# 1. 查询证据图片(商家上传的)
|
||
zhengju_mapping = {}
|
||
if dingdan_ids and shangjia_ids:
|
||
# 构建Q对象:OrderID在列表中 AND UserID在商家ID列表中
|
||
zhengju_q = Q(OrderID__in=dingdan_ids, UserID__in=shangjia_ids)
|
||
zhengju_queryset = PenaltyEvidenceImage.query.filter(zhengju_q).values('OrderID', 'UserID', 'ImageURL')
|
||
|
||
for item in zhengju_queryset:
|
||
key = f"{item['OrderID']}_{item['UserID']}"
|
||
if key not in zhengju_mapping:
|
||
zhengju_mapping[key] = []
|
||
if item['ImageURL']:
|
||
zhengju_mapping[key].append(item['ImageURL'])
|
||
|
||
# 2. 查询申诉图片(打手上传的)
|
||
shensu_mapping = {}
|
||
if dingdan_ids:
|
||
# 构建Q对象:OrderID在列表中 AND UserID=当前打手ID
|
||
shensu_q = Q(OrderID__in=dingdan_ids, UserID=yonghuid)
|
||
shensu_queryset = PenaltyEvidenceImage.query.filter(shensu_q).values('OrderID', 'UserID', 'ImageURL')
|
||
|
||
for item in shensu_queryset:
|
||
key = f"{item['OrderID']}_{item['UserID']}"
|
||
if key not in shensu_mapping:
|
||
shensu_mapping[key] = []
|
||
if item['ImageURL']:
|
||
shensu_mapping[key].append(item['ImageURL'])
|
||
|
||
# 🔴【核心修复】组装返回数据 - 字段保持原样!
|
||
chufa_list = []
|
||
for record in chufa_records:
|
||
dingdan_id = record.OrderID
|
||
shangjia_id = record.ApplicantID
|
||
|
||
# 获取证据图片(商家上传的)
|
||
zhengju_tupian = []
|
||
if dingdan_id and shangjia_id:
|
||
key = f"{dingdan_id}_{shangjia_id}"
|
||
zhengju_tupian = zhengju_mapping.get(key, [])
|
||
|
||
# 获取申诉图片(打手上传的)
|
||
shensu_tupian = []
|
||
if dingdan_id:
|
||
key = f"{dingdan_id}_{yonghuid}"
|
||
shensu_tupian = shensu_mapping.get(key, [])
|
||
|
||
# 🔴【字段保持原样!】与前端完全对应!
|
||
chufa_data = {
|
||
'id': record.id,
|
||
'dingdan_id': record.OrderID or '',
|
||
'qingqiuid': record.ApplicantID or '', # 商家ID
|
||
'cfliyou': record.PenaltyReason or '',
|
||
'sqzhuangtai': record.ApplyStatus or 0,
|
||
'ssliyou': record.AppealReason or '',
|
||
'jifen': record.DeductedPoints or 0,
|
||
'CreateTime': record.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if record.CreateTime else '',
|
||
'UpdateTime': record.UpdateTime.strftime('%Y-%m-%d %H:%M:%S') if record.UpdateTime else '',
|
||
'zhengju_tupian': zhengju_tupian, # 商家上传的证据图片
|
||
'shensu_tupian': shensu_tupian, # 打手上传的申诉图片
|
||
}
|
||
chufa_list.append(chufa_data)
|
||
|
||
# 判断是否还有更多数据
|
||
has_more = page_obj.has_next()
|
||
|
||
# 构建响应数据
|
||
response_data = {
|
||
'list': chufa_list,
|
||
'has_more': has_more,
|
||
'page': page,
|
||
'page_size': page_size,
|
||
'current_count': len(chufa_list)
|
||
}
|
||
|
||
return Response({
|
||
'code': 0,
|
||
'msg': '获取成功',
|
||
'data': response_data
|
||
})
|
||
|
||
except ValueError as e:
|
||
logger.error(f"参数错误 - 用户ID: {yonghuid}, 错误: {str(e)}")
|
||
return Response({
|
||
'code': 4,
|
||
'msg': '参数格式错误',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
except Exception as e:
|
||
logger.error(f"获取处罚记录列表异常 - 用户ID: {yonghuid}, 错误: {str(e)}", exc_info=True)
|
||
return Response({
|
||
'code': 5,
|
||
'msg': '获取记录列表失败',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
|
||
class DashouShensuView(APIView):
|
||
"""
|
||
打手申诉接口
|
||
路径: /yonghu/dscfss
|
||
方法: POST
|
||
权限: JWT Token + 打手权限验证
|
||
功能: 打手对处罚进行申诉
|
||
"""
|
||
permission_classes = [IsAuthenticated, DashouPermission]
|
||
|
||
def post(self, request):
|
||
try:
|
||
# 获取当前用户信息
|
||
user_main = request.user
|
||
yonghuid = user_main.UserUID
|
||
|
||
# 🔴🆕【关键】验证打手账号状态
|
||
try:
|
||
dashou_profile = user_main.DashouProfile
|
||
if dashou_profile.zhanghaozhuangtai != 1:
|
||
return Response({
|
||
'code': 3,
|
||
'msg': '账号已被封禁,无法申诉',
|
||
'data': None
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
except UserDashou.DoesNotExist:
|
||
return Response({
|
||
'code': 2,
|
||
'msg': '用户不是打手身份',
|
||
'data': None
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
# 🔴🆕【关键】获取请求参数
|
||
chufa_id = request.data.get('chufa_id') # 处罚记录ID
|
||
shensu_liyou = request.data.get('shensu_liyou', '').strip() # 申诉理由
|
||
shensu_tupian_urls = request.data.get('shensu_tupian_urls', []) # 申诉图片URL数组
|
||
|
||
# 验证必填字段
|
||
if not chufa_id:
|
||
return Response({
|
||
'code': 6,
|
||
'msg': '处罚记录ID不能为空',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
if not shensu_liyou:
|
||
return Response({
|
||
'code': 7,
|
||
'msg': '申诉理由不能为空',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 验证申诉理由长度
|
||
if len(shensu_liyou) > 500:
|
||
return Response({
|
||
'code': 8,
|
||
'msg': '申诉理由不能超过500字',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 验证图片数量
|
||
if len(shensu_tupian_urls) > 9:
|
||
return Response({
|
||
'code': 9,
|
||
'msg': '最多只能上传9张图片',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 🔴🆕【关键】使用事务确保数据一致性
|
||
|
||
with transaction.atomic():
|
||
# 1. 查询处罚记录
|
||
try:
|
||
chufa_record = PenaltyRecord.objects.select_for_update().get(
|
||
id=chufa_id,
|
||
PlayerID=yonghuid # 确保只能申诉自己的处罚记录
|
||
)
|
||
except PenaltyRecord.DoesNotExist:
|
||
return Response({
|
||
'code': 10,
|
||
'msg': '处罚记录不存在或无权申诉',
|
||
'data': None
|
||
}, status=status.HTTP_404_NOT_FOUND)
|
||
|
||
# 2. 验证处罚记录状态(必须是待处理状态0)
|
||
if chufa_record.ApplyStatus != 0:
|
||
return Response({
|
||
'code': 11,
|
||
'msg': '该处罚记录已处理,无法申诉',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 3. 验证订单是否存在(可选,根据需求决定)
|
||
# 这里可以根据OrderID去查询订单表,但根据需求描述,我们已经有了处罚记录
|
||
# 处罚记录中包含了OrderID,如果OrderID不存在,说明订单可能被删除
|
||
# 但根据业务逻辑,有处罚记录就应该有订单,所以这里可以跳过这个检查
|
||
|
||
# 4. 检查是否已有申诉记录(防止重复申诉)
|
||
if chufa_record.AppealReason or chufa_record.ApplyStatus == 3:
|
||
# 检查是否已经有申诉图片
|
||
existing_shensu_tupian = PenaltyEvidenceImage.query.filter(
|
||
OrderID=chufa_record.OrderID,
|
||
UserID=yonghuid
|
||
).exists()
|
||
|
||
if existing_shensu_tupian:
|
||
return Response({
|
||
'code': 12,
|
||
'msg': '您已经提交过申诉,请等待处理结果',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 5. 更新处罚记录
|
||
chufa_record.AppealReason = shensu_liyou
|
||
chufa_record.ApplyStatus = 3 # 状态改为申诉中
|
||
chufa_record.save()
|
||
|
||
# 6. 保存申诉图片(如果有)
|
||
saved_tupian_urls = [u for u in shensu_tupian_urls if u] if shensu_tupian_urls else []
|
||
if saved_tupian_urls:
|
||
PenaltyEvidenceImage.objects.bulk_create([
|
||
PenaltyEvidenceImage(
|
||
OrderID=chufa_record.OrderID,
|
||
UserID=yonghuid,
|
||
ImageURL=tupian_url,
|
||
)
|
||
for tupian_url in saved_tupian_urls
|
||
])
|
||
|
||
# 7. 构建返回数据
|
||
response_data = {
|
||
'chufa_id': chufa_record.id,
|
||
'dingdan_id': chufa_record.OrderID or '',
|
||
'shensu_liyou': shensu_liyou,
|
||
'shensu_tupian_urls': saved_tupian_urls,
|
||
'sqzhuangtai': chufa_record.ApplyStatus,
|
||
'UpdateTime': chufa_record.UpdateTime.strftime(
|
||
'%Y-%m-%d %H:%M:%S') if chufa_record.UpdateTime else ''
|
||
}
|
||
|
||
# 记录操作日志
|
||
logger.info(f"打手申诉成功 - 用户ID: {yonghuid}, 处罚ID: {chufa_id}, 订单ID: {chufa_record.OrderID}")
|
||
|
||
return Response({
|
||
'code': 0,
|
||
'msg': '申诉提交成功',
|
||
'data': response_data
|
||
})
|
||
|
||
except Exception as e:
|
||
logger.error(f"打手申诉异常 - 用户ID: {yonghuid}, 错误: {str(e)}", exc_info=True)
|
||
return Response({
|
||
'code': 99,
|
||
'msg': '系统繁忙,请稍后重试',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
|
||
class ShangjiaChufaJiluHuoquView(APIView):
|
||
"""
|
||
商家获取处罚记录接口
|
||
路径: /yonghu/sjcfjlhq
|
||
方法: POST
|
||
权限: JWT Token + 商家权限验证
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
try:
|
||
# 获取当前用户信息
|
||
user_main = request.user
|
||
yonghuid = user_main.UserUID
|
||
|
||
# 通过反向关系获取商家扩展信息
|
||
try:
|
||
shangjia_profile = user_main.ShopProfile
|
||
# 再次确认账号状态
|
||
if shangjia_profile.zhuangtai != 1:
|
||
return Response({
|
||
'code': 3,
|
||
'msg': '商家账号状态异常,无法查看处罚记录',
|
||
'data': None
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
except UserShangjia.DoesNotExist:
|
||
return Response({
|
||
'code': 2,
|
||
'msg': '用户不是商家身份',
|
||
'data': None
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
# 判断请求类型
|
||
qingqiu_tongji = request.data.get('qingqiu_tongji', False)
|
||
|
||
if qingqiu_tongji:
|
||
return self.get_tongji_info(yonghuid)
|
||
else:
|
||
return self.get_chufa_list(yonghuid, request.data)
|
||
|
||
except Exception as e:
|
||
logger.error(f"商家获取处罚记录异常 - 用户ID: {yonghuid}, 错误: {str(e)}", exc_info=True)
|
||
return Response({
|
||
'code': 99,
|
||
'msg': '系统繁忙,请稍后重试',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
def get_tongji_info(self, yonghuid):
|
||
"""
|
||
获取统计信息
|
||
"""
|
||
try:
|
||
# 🔴 查询条件:ApplicantID = 当前商家ID
|
||
stats = PenaltyRecord.query.filter(ApplicantID=yonghuid).aggregate(
|
||
zongshu=Count('id'),
|
||
daichuli=Count(
|
||
Case(
|
||
When(Q(ApplyStatus=0) | Q(ApplyStatus=3), then=1),
|
||
output_field=IntegerField()
|
||
)
|
||
),
|
||
yichuli=Count(
|
||
Case(
|
||
When(Q(ApplyStatus=1) | Q(ApplyStatus=2), then=1),
|
||
output_field=IntegerField()
|
||
)
|
||
)
|
||
)
|
||
|
||
tongji_data = {
|
||
'zongshu': stats.get('zongshu', 0) or 0,
|
||
'daichuli': stats.get('daichuli', 0) or 0,
|
||
'yichuli': stats.get('yichuli', 0) or 0
|
||
}
|
||
|
||
return Response({
|
||
'code': 0,
|
||
'msg': '获取统计信息成功',
|
||
'data': tongji_data
|
||
})
|
||
|
||
except Exception as e:
|
||
logger.error(f"商家获取统计信息异常 - 用户ID: {yonghuid}, 错误: {str(e)}")
|
||
return Response({
|
||
'code': 1,
|
||
'msg': '获取统计信息失败',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
def get_chufa_list(self, yonghuid, request_data):
|
||
"""
|
||
获取处罚记录列表(商家端)
|
||
🔴 只修改图片查询逻辑,字段保持原样
|
||
"""
|
||
try:
|
||
# 获取分页参数
|
||
page = int(request_data.get('page', 1))
|
||
page_size = int(request_data.get('page_size', 10))
|
||
|
||
if page_size > 50:
|
||
page_size = 50
|
||
if page_size < 1:
|
||
page_size = 10
|
||
|
||
# 获取状态筛选参数
|
||
zhuangtai = request_data.get('zhuangtai', 0)
|
||
|
||
# 🔴 查询条件:ApplicantID = 当前商家ID
|
||
queryset = PenaltyRecord.query.filter(ApplicantID=yonghuid)
|
||
|
||
if zhuangtai == 0:
|
||
queryset = queryset.filter(Q(ApplyStatus=0) | Q(ApplyStatus=3))
|
||
elif zhuangtai == 1:
|
||
queryset = queryset.filter(Q(ApplyStatus=1) | Q(ApplyStatus=2))
|
||
else:
|
||
pass
|
||
|
||
# 按创建时间倒序排列
|
||
queryset = queryset.order_by('-CreateTime')
|
||
|
||
# 分页逻辑
|
||
paginator = Paginator(queryset, page_size)
|
||
|
||
try:
|
||
page_obj = paginator.page(page)
|
||
except:
|
||
return Response({
|
||
'code': 0,
|
||
'msg': '获取成功',
|
||
'data': {
|
||
'list': [],
|
||
'has_more': False,
|
||
'page': page,
|
||
'page_size': page_size,
|
||
'current_count': 0
|
||
}
|
||
})
|
||
|
||
chufa_records = list(page_obj)
|
||
|
||
if not chufa_records:
|
||
return Response({
|
||
'code': 0,
|
||
'msg': '获取成功',
|
||
'data': {
|
||
'list': [],
|
||
'has_more': False,
|
||
'page': page,
|
||
'page_size': page_size,
|
||
'current_count': 0
|
||
}
|
||
})
|
||
|
||
# 🔴【核心修复】收集所有需要查询图片的组合
|
||
dingdan_ids = []
|
||
dashou_ids = []
|
||
|
||
for record in chufa_records:
|
||
if record.OrderID:
|
||
dingdan_ids.append(record.OrderID)
|
||
if record.PlayerID: # 打手ID
|
||
dashou_ids.append(record.PlayerID)
|
||
|
||
# 去重
|
||
dingdan_ids = list(set(dingdan_ids))
|
||
dashou_ids = list(set(dashou_ids))
|
||
|
||
# 🔴【核心修复】分别查询两种图片(避免if-elif逻辑错误)
|
||
# 1. 查询证据图片(商家自己上传的)
|
||
zhengju_mapping = {}
|
||
if dingdan_ids:
|
||
# 构建Q对象:OrderID在列表中 AND UserID=当前商家ID
|
||
zhengju_q = Q(OrderID__in=dingdan_ids, UserID=yonghuid)
|
||
zhengju_queryset = PenaltyEvidenceImage.query.filter(zhengju_q).values('OrderID', 'UserID', 'ImageURL')
|
||
|
||
for item in zhengju_queryset:
|
||
key = f"{item['OrderID']}_{item['UserID']}"
|
||
if key not in zhengju_mapping:
|
||
zhengju_mapping[key] = []
|
||
if item['ImageURL']:
|
||
zhengju_mapping[key].append(item['ImageURL'])
|
||
|
||
# 2. 查询申诉图片(打手上传的)
|
||
shensu_mapping = {}
|
||
if dingdan_ids and dashou_ids:
|
||
# 构建Q对象:OrderID在列表中 AND UserID在打手ID列表中
|
||
shensu_q = Q(OrderID__in=dingdan_ids, UserID__in=dashou_ids)
|
||
shensu_queryset = PenaltyEvidenceImage.query.filter(shensu_q).values('OrderID', 'UserID', 'ImageURL')
|
||
|
||
for item in shensu_queryset:
|
||
key = f"{item['OrderID']}_{item['UserID']}"
|
||
if key not in shensu_mapping:
|
||
shensu_mapping[key] = []
|
||
if item['ImageURL']:
|
||
shensu_mapping[key].append(item['ImageURL'])
|
||
|
||
# 🔴【核心修复】组装返回数据 - 字段保持原样!
|
||
chufa_list = []
|
||
for record in chufa_records:
|
||
dingdan_id = record.OrderID
|
||
dashou_id = record.PlayerID
|
||
|
||
# 获取证据图片(商家自己上传的)
|
||
zhengju_tupian = []
|
||
if dingdan_id:
|
||
key = f"{dingdan_id}_{yonghuid}"
|
||
zhengju_tupian = zhengju_mapping.get(key, [])
|
||
|
||
# 获取申诉图片(打手上传的)
|
||
shensu_tupian = []
|
||
if dingdan_id and dashou_id:
|
||
key = f"{dingdan_id}_{dashou_id}"
|
||
shensu_tupian = shensu_mapping.get(key, [])
|
||
|
||
# 🔴【字段保持原样!】与前端完全对应!
|
||
chufa_data = {
|
||
'id': record.id,
|
||
'dingdan_id': record.OrderID or '',
|
||
'qingqiuid': record.PlayerID or '', # 打手ID
|
||
'cfliyou': record.PenaltyReason or '',
|
||
'sqzhuangtai': record.ApplyStatus or 0,
|
||
'ssliyou': record.AppealReason or '',
|
||
'jifen': record.DeductedPoints or 0,
|
||
'CreateTime': record.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if record.CreateTime else '',
|
||
'UpdateTime': record.UpdateTime.strftime('%Y-%m-%d %H:%M:%S') if record.UpdateTime else '',
|
||
'zhengju_tupian': zhengju_tupian, # 商家自己上传的证据图片
|
||
'shensu_tupian': shensu_tupian, # 打手上传的申诉图片
|
||
}
|
||
chufa_list.append(chufa_data)
|
||
|
||
# 判断是否还有更多数据
|
||
has_more = page_obj.has_next()
|
||
|
||
response_data = {
|
||
'list': chufa_list,
|
||
'has_more': has_more,
|
||
'page': page,
|
||
'page_size': page_size,
|
||
'current_count': len(chufa_list)
|
||
}
|
||
|
||
return Response({
|
||
'code': 0,
|
||
'msg': '获取成功',
|
||
'data': response_data
|
||
})
|
||
|
||
except ValueError as e:
|
||
logger.error(f"商家端参数错误 - 用户ID: {yonghuid}, 错误: {str(e)}")
|
||
return Response({
|
||
'code': 4,
|
||
'msg': '参数格式错误',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
except Exception as e:
|
||
logger.error(f"商家获取处罚记录列表异常 - 用户ID: {yonghuid}, 错误: {str(e)}", exc_info=True)
|
||
return Response({
|
||
'code': 5,
|
||
'msg': '获取记录列表失败',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|