Files
Django/rank/views/kaohe_jilu.py
TermiNexus f1c8633345 perf: P1 性能优化批量修复 + kefu 视图拆分
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
2026-07-05 23:17:38 +08:00

265 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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)