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
265 lines
12 KiB
Python
265 lines
12 KiB
Python
"""rank.views.kaohe_jilu - auto-generated by split script."""
|
||
import json
|
||
import logging
|
||
from decimal import Decimal
|
||
from django.db import models, transaction
|
||
from django.db.models import F
|
||
from rest_framework.views import APIView
|
||
from rest_framework.response import Response
|
||
from rest_framework.permissions import IsAuthenticated
|
||
from ..utils import get_tag_fee, create_shenhe_jilu, validate_shenheguan
|
||
from ..models import (
|
||
YonghuChenghao, Chenghao, KaoheCishuFeiyong, KaoheJujueJilu,
|
||
KaoheguanBankuai, Bankuai, ShenheJilu, KaoheJiluFeiyong
|
||
)
|
||
from users.models import UserShenheguan, UserDashou
|
||
from users.business_models import User
|
||
from products.models import ShangpinLeixing
|
||
|
||
|
||
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class KaoheJiluView(APIView):
|
||
"""获取打手本人的考核记录(支持筛选状态)"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
try:
|
||
user = request.user
|
||
status = request.data.get('status', 'pending') # pending / passed
|
||
page = int(request.data.get('page', 1))
|
||
page_size = int(request.data.get('page_size', 5))
|
||
|
||
if status == 'pending':
|
||
# 进行中/未通过:审核中(0)、待审核(3)、未通过(2)
|
||
query = ShenheJilu.objects.filter(
|
||
shenqingren_id=user.UserUID,
|
||
zhuangtai__in=[0, 2, 3]
|
||
)
|
||
else:
|
||
query = ShenheJilu.objects.filter(
|
||
shenqingren_id=user.UserUID,
|
||
zhuangtai=1 # 已通过
|
||
)
|
||
|
||
total = query.count()
|
||
records = query.order_by('-CreateTime')[(page-1)*page_size : page*page_size]
|
||
|
||
# 批量预取关联数据,避免循环内 N+1
|
||
records = records.select_related('chenghao', 'bankuai')
|
||
records_list = list(records)
|
||
jilu_ids = [jl.jilu_id for jl in records_list]
|
||
|
||
# 批量汇总缴纳总额(按 jilu_id 分组聚合)
|
||
_fee_map = {}
|
||
if jilu_ids:
|
||
_fee_agg = KaoheJiluFeiyong.objects.filter(
|
||
jilu_id__in=jilu_ids
|
||
).values('jilu_id').annotate(total=models.Sum('jine'))
|
||
for item in _fee_agg:
|
||
_fee_map[item['jilu_id']] = float(item['total'] or 0.00)
|
||
|
||
# 批量获取上次拒绝原因(每个 jilu_id 取最近一条)
|
||
_fail_map = {}
|
||
if jilu_ids:
|
||
_reject_qs = KaoheJujueJilu.objects.filter(
|
||
jilu_id__in=jilu_ids
|
||
).order_by('jilu_id', '-CreateTime')
|
||
for rej in _reject_qs:
|
||
if rej.jilu_id not in _fail_map: # 只取首条(最近)
|
||
_fail_map[rej.jilu_id] = rej.yuanyin or ''
|
||
|
||
# 批量获取考核官头像
|
||
_avatar_map = {}
|
||
_kg_ids = {jl.shenheguan_id for jl in records_list if jl.shenheguan_id}
|
||
if _kg_ids:
|
||
for u in User.objects.filter(UserUID__in=_kg_ids):
|
||
_avatar_map[u.UserUID] = u.Avatar or ''
|
||
|
||
data = []
|
||
for jl in records_list:
|
||
total_fee = _fee_map.get(jl.jilu_id, 0.00)
|
||
fail_reason = _fail_map.get(jl.jilu_id, '')
|
||
kg_avatar = _avatar_map.get(jl.shenheguan_id, '')
|
||
|
||
data.append({
|
||
'jilu_id': jl.jilu_id,
|
||
'chenghao_name': jl.chenghao.mingcheng if jl.chenghao else '',
|
||
'bankuai_name': jl.bankuai.mingcheng if jl.bankuai else '',
|
||
'zhuangtai': jl.zhuangtai,
|
||
'statusText': dict(ShenheJilu._meta.get_field('zhuangtai').choices).get(jl.zhuangtai, '未知'),
|
||
'statusColor': {0: '#FFA500', 1: '#27ae60', 2: '#e74c3c', 3: '#3498db'}.get(jl.zhuangtai, '#888'),
|
||
'shenheguan_id': jl.shenheguan_id,
|
||
'shenheguan_avatar': kg_avatar,
|
||
'dashou_youxi_id': jl.dashou_youxi_id,
|
||
'dashou_beizhu': jl.dashou_beizhu,
|
||
'cishu': jl.cishu,
|
||
'total_fee': float(total_fee),
|
||
'last_fail_reason': fail_reason,
|
||
'CreateTime': jl.CreateTime.strftime('%Y-%m-%d %H:%M:%S'),
|
||
# 为再次申请预留标签对象数据(前端可能用到)
|
||
'tag_obj': {
|
||
'id': jl.chenghao_id,
|
||
'name': jl.chenghao.mingcheng if jl.chenghao else '',
|
||
# 其他字段按需,但申请时会重新拉取
|
||
} if jl.chenghao else None,
|
||
})
|
||
|
||
has_more = (page * page_size) < total
|
||
return Response({
|
||
'code': 0,
|
||
'data': {'list': data, 'has_more': has_more, 'total': total}
|
||
})
|
||
except Exception as e:
|
||
return Response({'code': 500, 'msg': str(e)}, status=500)
|
||
|
||
|
||
class KaoheJiluListView(APIView):
|
||
"""
|
||
获取当前考核官的历史审核记录
|
||
路径:POST /dengji/khghqkhjl
|
||
权限:JWT Token (考核官)
|
||
参数:
|
||
bankuai_id : int (可选,0或空表示全部)
|
||
status : int (可选,0-全部 1-审核中 2-已通过 3-未通过)
|
||
page : int (默认1)
|
||
page_size : int (默认5)
|
||
返回:
|
||
list, has_more, total
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
try:
|
||
yonghu = request.user
|
||
# 1. 验证考核官身份及账号状态
|
||
kg = UserShenheguan.objects.filter(user=yonghu, zhuangtai=1).first()
|
||
if not kg:
|
||
return Response({'code': 403, 'msg': '您不是考核官或账号异常'})
|
||
|
||
# 2. 获取参数
|
||
bankuai_id = request.data.get('bankuai_id')
|
||
status = int(request.data.get('status', 0)) # 0全部 1审核中 2已通过 3未通过
|
||
page = int(request.data.get('page', 1))
|
||
page_size = int(request.data.get('page_size', 5))
|
||
|
||
# 3. 构建查询条件:只查当前考核官审核的记录
|
||
base_query = ShenheJilu.objects.filter(shenheguan_id=yonghu.UserUID)
|
||
|
||
# 板块筛选
|
||
if bankuai_id and int(bankuai_id) > 0:
|
||
base_query = base_query.filter(bankuai_id=int(bankuai_id))
|
||
|
||
# 状态筛选(前端传的状态映射到后端zhuangtai字段)
|
||
status_map = {
|
||
0: None, # 全部不筛选
|
||
1: 0, # 审核中
|
||
2: 1, # 已通过
|
||
3: 2, # 未通过
|
||
}
|
||
if status_map.get(status) is not None:
|
||
base_query = base_query.filter(zhuangtai=status_map[status])
|
||
|
||
# 4. 计数与分页
|
||
total = base_query.count()
|
||
offset = (page - 1) * page_size
|
||
jilu_list = base_query.order_by('-CreateTime')[offset:offset + page_size]
|
||
|
||
# 5. 预取关联数据,减少查询次数
|
||
jilu_list = jilu_list.select_related('bankuai', 'chenghao')
|
||
# 为每条记录预取费用明细和拒绝记录(通过 related_name 或手动过滤)
|
||
# 由于 KaoheJiluFeiyong 和 KaoheJujueJilu 通过外键指向 ShenheJilu (to_field='jilu_id'),
|
||
# 可以使用 prefetch_related
|
||
jilu_ids = [j.jilu_id for j in jilu_list]
|
||
fee_dict = {}
|
||
reject_dict = {}
|
||
if jilu_ids:
|
||
# 批量获取所有费用明细
|
||
fee_qs = KaoheJiluFeiyong.objects.filter(jilu_id__in=jilu_ids).order_by('cishu')
|
||
for fee in fee_qs:
|
||
if fee.jilu_id not in fee_dict:
|
||
fee_dict[fee.jilu_id] = []
|
||
fee_dict[fee.jilu_id].append({'cishu': fee.cishu, 'jine': float(fee.jine)})
|
||
|
||
# 批量获取所有拒绝记录
|
||
reject_qs = KaoheJujueJilu.objects.filter(jilu_id__in=jilu_ids).order_by('cishu')
|
||
for rej in reject_qs:
|
||
if rej.jilu_id not in reject_dict:
|
||
reject_dict[rej.jilu_id] = []
|
||
reject_dict[rej.jilu_id].append({
|
||
'cishu': rej.cishu,
|
||
'yuanyin': rej.yuanyin,
|
||
'CreateTime': rej.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if rej.CreateTime else ''
|
||
})
|
||
|
||
# 6. 批量获取申请人信息(昵称和头像)
|
||
applicant_ids = {j.shenqingren_id for j in jilu_list}
|
||
user_map = {}
|
||
if applicant_ids:
|
||
users = User.objects.filter(UserUID__in=applicant_ids).select_related('DashouProfile')
|
||
for u in users:
|
||
avatar = u.Avatar or ''
|
||
nicheng = ''
|
||
try:
|
||
# 打手扩展表可能有 nicheng
|
||
nicheng = u.DashouProfile.nicheng if getattr(u, 'DashouProfile', None) else ''
|
||
except UserDashou.DoesNotExist:
|
||
pass
|
||
user_map[u.UserUID] = {
|
||
'avatar': avatar,
|
||
'nicheng': nicheng or f'用户{u.UserUID}'
|
||
}
|
||
|
||
# 7. 组装数据
|
||
records = []
|
||
for jl in jilu_list:
|
||
user_info = user_map.get(jl.shenqingren_id, {'avatar': '', 'nicheng': '未知'})
|
||
# 标签特效JSON
|
||
texiao_json = {}
|
||
if jl.chenghao and jl.chenghao.texiao_miaoshu:
|
||
try:
|
||
texiao_json = json.loads(jl.chenghao.texiao_miaoshu)
|
||
except:
|
||
pass
|
||
|
||
# 汇总总缴纳费用
|
||
fee_items = fee_dict.get(jl.jilu_id, [])
|
||
total_fee = sum(item['jine'] for item in fee_items)
|
||
|
||
records.append({
|
||
'jilu_id': jl.jilu_id,
|
||
'shenqingren_id': jl.shenqingren_id,
|
||
'shenqingren_avatar': user_info['avatar'],
|
||
'shenqingren_nicheng': user_info['nicheng'],
|
||
'bankuai_mingcheng': jl.bankuai.mingcheng if jl.bankuai else '',
|
||
'chenghao_mingcheng': jl.chenghao.mingcheng if jl.chenghao else '',
|
||
'chenghao_texiao_json': texiao_json,
|
||
'zhuangtai': jl.zhuangtai, # 0审核中 1通过 2未通过 3待审核
|
||
'cishu': jl.cishu,
|
||
'total_fee': total_fee,
|
||
'dashou_youxi_id': jl.dashou_youxi_id or '',
|
||
'dashou_beizhu': jl.dashou_beizhu or '',
|
||
'shenheguan_youxi_id': jl.shenheguan_youxi_id or '',
|
||
'guize_neirong': jl.guize_neirong or '',
|
||
'CreateTime': jl.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if jl.CreateTime else '',
|
||
'UpdateTime': jl.UpdateTime.strftime('%Y-%m-%d %H:%M:%S') if jl.UpdateTime else '',
|
||
'fee_list': fee_items,
|
||
'reject_list': reject_dict.get(jl.jilu_id, []),
|
||
})
|
||
|
||
has_more = (page * page_size) < total
|
||
return Response({
|
||
'code': 0,
|
||
'data': {
|
||
'list': records,
|
||
'has_more': has_more,
|
||
'total': total
|
||
}
|
||
})
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取考核记录异常: {str(e)}", exc_info=True)
|
||
return Response({'code': 500, 'msg': '服务器内部错误'}, status=500) |