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, UserMain, UserDashou from products.models import ShangpinLeixing logger = logging.getLogger(__name__) class DashouBiaoqianView(APIView): """获取当前打手所有称号标签""" permission_classes = [IsAuthenticated] def post(self, request): try: yonghu = request.user # 查询用户的所有称号关联,一次连表取出 relations = YonghuChenghao.objects.filter( yonghu=yonghu ).select_related('chenghao') chenghao_list = [] for rel in relations: ch = rel.chenghao # 解析特效描述 JSON(前端需要对象) texiao_json = {} if ch.texiao_miaoshu: try: texiao_json = json.loads(ch.texiao_miaoshu) except json.JSONDecodeError: texiao_json = {} chenghao_list.append({ 'mingcheng': ch.mingcheng, 'tubiao_url': ch.tubiao_url or '', 'texiao_json': texiao_json, }) return Response({ 'code': 0, 'msg': '成功', 'data': {'chenghao_list': chenghao_list} }) except Exception as e: logger.error(f"获取打手标签失败: {e}", exc_info=True) return Response({'code': 1, 'msg': '服务器错误', 'data': None}) class BankuaiBiaoqianView(APIView): """获取商品类型所属板块的所有标签""" permission_classes = [IsAuthenticated] def post(self, request): leixing_id = request.data.get('leixing_id') if not leixing_id: return Response({'code': 1, 'msg': '缺少leixing_id'}) try: leixing = ShangpinLeixing.objects.select_related('bankuai').get(id=leixing_id) except ShangpinLeixing.DoesNotExist: return Response({'code': 2, 'msg': '商品类型不存在'}) bankuai = leixing.bankuai if not bankuai: return Response({'code': 0, 'data': {'biaoqian_list': []}}) # 获取该板块下所有类型的标签(leixing可以为dashou/shangjia等,这里先返回所有) chenghao_list = Chenghao.objects.filter(bankuai=bankuai) data = [] for ch in chenghao_list: texiao_json = {} if ch.texiao_miaoshu: try: texiao_json = json.loads(ch.texiao_miaoshu) except: pass data.append({ 'id': ch.id, 'mingcheng': ch.mingcheng, 'texiao_json': texiao_json }) return Response({'code': 0, 'data': {'biaoqian_list': data}}) # 硬编码邀请码 KAOHEGUAN_INVITE_CODE = 'KHG2026VIP' class KaoheguanRegisterView(APIView): """ 考核官注册接口 POST /dengji/khgzc """ permission_classes = [IsAuthenticated] def post(self, request): try: yonghu = request.user invite_code = request.data.get('inviteCode', '').strip() if not invite_code: return Response({'code': 400, 'msg': '邀请码不能为空'}) if invite_code != KAOHEGUAN_INVITE_CODE: return Response({'code': 400, 'msg': '邀请码错误'}) kg = UserShenheguan.objects.filter(user=yonghu).first() if kg: if kg.zhuangtai == 1: return Response({'code': 200, 'msg': '您已是考核官', 'data': self._build_data(kg)}) else: return Response({'code': 400, 'msg': '账号已被封禁'}) with transaction.atomic(): kg = UserShenheguan.objects.create( user=yonghu, zhuangtai=1, shenhe_zhuangtai=0, shenhe_zongshu=0, tongguo_zongshu=0, yue=0.00, zonge=0.00 ) return Response({'code': 200, 'msg': '注册成功', 'data': self._build_data(kg)}) except Exception as e: logger.error(f"考核官注册失败: {e}", exc_info=True) return Response({'code': 500, 'msg': '服务器内部错误'}) def _build_data(self, kg): bankuai_qs = KaoheguanBankuai.objects.filter(kaoheguan_id=kg.user.yonghuid).select_related('bankuai') bankuai_list = [{'bankuai_id': b.bankuai.bankuai_id, 'mingcheng': b.bankuai.mingcheng} for b in bankuai_qs] return { 'zhuangtai': kg.zhuangtai, 'shenhe_zongshu': kg.shenhe_zongshu, 'tongguo_zongshu': kg.tongguo_zongshu, 'yue': str(kg.yue), 'zonge': str(kg.zonge), 'shenhe_zhuangtai': kg.shenhe_zhuangtai, 'bankuai_list': bankuai_list, 'kaoheguanstatus': 1 } class KaoheguanRefreshView(APIView): """ 考核官数据刷新接口 POST /dengji/khgsx """ permission_classes = [IsAuthenticated] def post(self, request): try: yonghu = request.user kg = UserShenheguan.objects.filter(user=yonghu).first() if not kg: return Response({'code': 400, 'msg': '您还未注册考核官'}) if kg.zhuangtai != 1: return Response({'code': 200, 'data': {'zhuangtai': kg.zhuangtai, 'kaoheguanstatus': 0}}) # 查询所属板块 bankuai_qs = KaoheguanBankuai.objects.filter(kaoheguan_id=kg.user.yonghuid).select_related('bankuai') bankuai_list = [{'bankuai_id': b.bankuai.bankuai_id, 'mingcheng': b.bankuai.mingcheng} for b in bankuai_qs] # 统计待审核 / 审核中的记录数量 pending_count = ShenheJilu.objects.filter( shenheguan_id=yonghu.yonghuid, zhuangtai=0 # 审核中 ).count() data = { 'zhuangtai': kg.zhuangtai, 'shenhe_zongshu': kg.shenhe_zongshu, 'tongguo_zongshu': kg.tongguo_zongshu, 'yue': str(kg.yue), 'zonge': str(kg.zonge), 'shenhe_zhuangtai': kg.shenhe_zhuangtai, 'bankuai_list': bankuai_list, 'kaoheguanstatus': 1, 'is_renzheng': kg.is_renzheng, # 🆕 是否认证 'pending_shenhe_count': pending_count # 🆕 审核中数量 } return Response({'code': 200, 'data': data}) except Exception as e: logger.error(f"考核官刷新失败: {e}", exc_info=True) return Response({'code': 500, 'msg': '服务器内部错误'}) class ShenheDatingModuleView(APIView): """审核大厅获取模块列表(板块 + 待审核数量 + 是否关联)""" permission_classes = [IsAuthenticated] def post(self, request): try: yonghu = request.user related_ids = set(KaoheguanBankuai.objects.filter(kaoheguan_id=yonghu.yonghuid) .values_list('bankuai_id', flat=True)) bankuai_list = [] for bk in Bankuai.objects.all(): count = ShenheJilu.objects.filter(bankuai=bk, zhuangtai=3, shenheguan_id__isnull=True).count() bankuai_list.append({ 'bankuai_id': bk.bankuai_id, 'mingcheng': bk.mingcheng, 'count': count, 'is_related': bk.bankuai_id in related_ids }) return Response({'code': 0, 'data': {'modules': bankuai_list}}) except Exception as e: return Response({'code': 500, 'msg': str(e)}, status=500) class ShenheDatingDataView(APIView): """获取审核大厅待审核记录列表""" permission_classes = [IsAuthenticated] def post(self, request): try: data = request.data bankuai_id = data.get('bankuai_id') page = int(data.get('page', 1)) page_size = int(data.get('page_size', 5)) query = ShenheJilu.objects.filter(zhuangtai=3, shenheguan_id__isnull=True) if bankuai_id: query = query.filter(bankuai_id=bankuai_id) total = query.count() offset = (page - 1) * page_size jilu_list = query.select_related('bankuai', 'chenghao').order_by('-create_time')[ offset:offset + page_size] records = [] for jl in jilu_list: user = UserMain.objects.filter(yonghuid=jl.shenqingren_id).first() avatar = user.avatar if user else '' texiao_json = {} if jl.chenghao and jl.chenghao.texiao_miaoshu: try: texiao_json = json.loads(jl.chenghao.texiao_miaoshu) except: pass # 新增字段:本次考核费用 kaohe_feiyong = float(jl.jiaofei_jine) if jl.jiaofei_jine is not None else 0.0 records.append({ 'jilu_id': jl.jilu_id, 'shenqingren_id': jl.shenqingren_id, 'shenqingren_avatar': avatar, 'dashou_youxi_id': jl.dashou_youxi_id or '', 'dashou_beizhu': jl.dashou_beizhu or '', 'bankuai_id': jl.bankuai.bankuai_id if jl.bankuai else None, 'bankuai_mingcheng': jl.bankuai.mingcheng if jl.bankuai else '', 'chenghao_id': jl.chenghao_id, 'chenghao_mingcheng': jl.chenghao.mingcheng if jl.chenghao else '', 'chenghao_texiao_json': texiao_json, 'guize_neirong': jl.guize_neirong or '', 'kaohe_feiyong': kaohe_feiyong, # 新增 'create_time': jl.create_time.strftime('%Y-%m-%d %H:%M:%S') if jl.create_time else '', }) has_more = (page * page_size) < total return Response({'code': 0, 'data': {'list': records, 'has_more': has_more, 'total': total}}) except Exception as e: return Response({'code': 500, 'msg': str(e)}, status=500) class ShenheQiangdanView(APIView): """ 审核官抢单(接审核任务) 校验:身份、账号封禁、已有进行中审核、记录状态、不能审核自己、板块权限 成功后更新考核官扩展表:审核总数+1,审核状态设为“审核中” """ permission_classes = [IsAuthenticated] def post(self, request): try: yonghu = request.user jilu_id = request.data.get('jilu_id') if not jilu_id: return Response({'code': 400, 'msg': '记录ID不能为空'}) # 1. 检查考核官身份及账号是否被禁用 kg = UserShenheguan.objects.filter(user=yonghu, zhuangtai=1).first() if not kg: return Response({'code': 403, 'msg': '您不是考核官或账号已被禁用'}) # 2. 检查是否有正在审核中的记录(zhuangtai=0),防止同时审核多个 ongoing = ShenheJilu.objects.filter( shenheguan_id=yonghu.yonghuid, zhuangtai=0 ).exists() if ongoing: return Response({'code': 400, 'msg': '您有正在审核中的任务,请完成后再接新任务'}) with transaction.atomic(): # 3. 行锁抢单:仅当状态为待审核(3)且无审核官时才能抢 jilu = ShenheJilu.objects.select_for_update().filter( jilu_id=jilu_id, zhuangtai=3, shenheguan_id__isnull=True ).first() if not jilu: return Response({'code': 400, 'msg': '该审核任务已被接走、状态变更或不存在'}) # 4. 不能审核自己 if jilu.shenqingren_id == yonghu.yonghuid: return Response({'code': 400, 'msg': '不能审核自己提交的申请'}) # 5. 板块权限:考核官必须属于该板块 is_related = KaoheguanBankuai.objects.filter( kaoheguan_id=yonghu.yonghuid, bankuai=jilu.bankuai ).exists() if not is_related: return Response({'code': 400, 'msg': '您不属于该板块,无法审核'}) # 新增:6. 费用>0时,要求考核官已认证 if jilu.jiaofei_jine and float(jilu.jiaofei_jine) > 0: if not kg.is_renzheng: return Response({'code': 400, 'msg': '本考核需要认证考核官接单,您尚未认证'}) # 7. 更新审核记录:分配给当前考核官,状态变为审核中 jilu.zhuangtai = 0 # 审核中 jilu.shenheguan_id = yonghu.yonghuid jilu.save() # 8. 更新考核官扩展表:审核总数 +1,审核状态置为审核中(1) UserShenheguan.objects.filter(user=yonghu).update( shenhe_zongshu=F('shenhe_zongshu') + 1, shenhe_zhuangtai=1 ) return Response({ 'code': 0, 'msg': '抢单成功,请前往游戏内审核,审核完成后请到考核打分页面进行评定。', 'data': {'bankuai_id': jilu.bankuai.bankuai_id} }) except Exception as e: logger.error(f"审核官抢单异常: {str(e)}", exc_info=True) return Response({'code': 500, 'msg': '服务器内部错误'}, status=500) class ShenheXiangqingView(APIView): """获取当前考核官审核中的记录详情(新增待收考核费字段)""" permission_classes = [IsAuthenticated] def post(self, request): try: yonghu = request.user kg = UserShenheguan.objects.filter(user=yonghu, zhuangtai=1).first() if not kg: return Response({'code': 403, 'msg': '您不是考核官或账号异常'}) page = int(request.data.get('page', 1)) page_size = int(request.data.get('page_size', 10)) # 查询当前考核官名下状态为“审核中”的记录 base_query = ShenheJilu.objects.filter( shenheguan_id=yonghu.yonghuid, zhuangtai=0 ).select_related('bankuai', 'chenghao') total = base_query.count() offset = (page - 1) * page_size jilu_list = base_query[offset:offset + page_size] records = [] for jl in jilu_list: user = UserMain.objects.filter(yonghuid=jl.shenqingren_id).first() avatar = user.avatar if user else '' texiao_json = {} if jl.chenghao and jl.chenghao.texiao_miaoshu: try: texiao_json = json.loads(jl.chenghao.texiao_miaoshu) except: pass # 是否开启金牌 is_jinpai = jl.chenghao.kaioi_jinpai if jl.chenghao else False # 本次待收考核费(基于记录中的当前次数,从费用明细表查询) daishou_fei = 0.00 fee_detail = KaoheJiluFeiyong.objects.filter( jilu=jl, cishu=jl.cishu ).first() if fee_detail: daishou_fei = float(fee_detail.jine) else: # 兜底:若明细表无记录,尝试用通用费用表计算 daishou_fei = 0.00 # 上次失败原因 last_fail_reason = '' last_fail = KaoheJujueJilu.objects.filter( jilu=jl, cishu=jl.cishu - 1 ).order_by('-create_time').first() if last_fail: last_fail_reason = last_fail.yuanyin records.append({ 'jilu_id': jl.jilu_id, 'shenqingren_id': jl.shenqingren_id, 'shenqingren_avatar': avatar, 'bankuai_mingcheng': jl.bankuai.mingcheng if jl.bankuai else '', 'chenghao_mingcheng': jl.chenghao.mingcheng if jl.chenghao else '', 'chenghao_texiao_json': texiao_json, 'guize_neirong': jl.guize_neirong or '', 'dashou_beizhu': jl.dashou_beizhu or '', 'dashou_youxi_id': jl.dashou_youxi_id or '', 'shenheguan_youxi_id': jl.shenheguan_youxi_id or '', 'cishu': jl.cishu, 'daishou_fei': daishou_fei, # 新增:待收考核费 'last_fail_reason': last_fail_reason, 'is_jinpai': is_jinpai, 'create_time': jl.create_time.strftime('%Y-%m-%d %H:%M:%S') if jl.create_time else '', }) has_more = (page * page_size) < total return Response({'code': 0, 'data': {'list': records, 'has_more': has_more, 'total': total}}) except Exception as e: return Response({'code': 500, 'msg': str(e)}, status=500) class ShenheCaozuoView(APIView): """ 考核官执行操作(同意/拒绝/转移/修改昵称),并更新考核官收益 - 只有“同意”或“拒绝”才增加余额和总额,金额严格从 KaoheJiluFeiyong 表获取 - 查不到费用则加 0 元,绝不影响流程 - 转移、修改昵称等操作不涉及收益 """ 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': '账号异常,无法操作'}) jilu_id = request.data.get('jilu_id') caozuo = request.data.get('caozuo') # agree / reject / transfer / modify_nick if not jilu_id or not caozuo: return Response({'code': 400, 'msg': '缺少必要参数'}) with transaction.atomic(): # 2. 锁定审核记录,确保属于当前考核官且状态为审核中(0) jilu = ShenheJilu.objects.select_for_update().filter( jilu_id=jilu_id, shenheguan_id=yonghu.yonghuid, zhuangtai=0 ).first() if not jilu: return Response({'code': 400, 'msg': '该记录不存在或不属于您'}) # 3. 本次操作产生的收入(仅通过/拒绝时有意义) daishou_fei = 0.00 # 4. 根据操作类型执行对应业务逻辑 if caozuo == 'agree': # --- 通过考核 --- # 查询本次考核应得的费用(基于记录ID和当前次数) fee_detail = KaoheJiluFeiyong.objects.filter( jilu=jilu, cishu=jilu.cishu ).first() if fee_detail: daishou_fei = float(fee_detail.jine) # 找不到就保持 0,绝不使用任何默认值 jilu.zhuangtai = 1 jilu.save() # 授予标签 if jilu.chenghao: yh = UserMain.objects.get(yonghuid=jilu.shenqingren_id) if not YonghuChenghao.objects.filter(yonghu=yh, chenghao=jilu.chenghao).exists(): YonghuChenghao.objects.create(yonghu=yh, chenghao=jilu.chenghao) # 如果标签开启了金牌,同步更新打手称号 if jilu.chenghao.kaioi_jinpai: dashou_prof = UserDashou.objects.filter(user=yh).first() if dashou_prof: dashou_prof.chenghao = '金牌选手' dashou_prof.save() elif caozuo == 'reject': # --- 拒绝考核 --- yuanyin = request.data.get('yuanyin', '') if not yuanyin.strip(): return Response({'code': 400, 'msg': '请填写拒绝原因'}) # 查询本次考核应得的费用(拒绝时同样获得收入) fee_detail = KaoheJiluFeiyong.objects.filter( jilu=jilu, cishu=jilu.cishu ).first() if fee_detail: daishou_fei = float(fee_detail.jine) # 找不到就保持 0 jilu.zhuangtai = 2 jilu.save() # 记录拒绝原因 KaoheJujueJilu.objects.create( jilu=jilu, cishu=jilu.cishu, shenheguan_id=yonghu.yonghuid, yuanyin=yuanyin ) # 新增:不合格时,如果考核官未认证,则清空考核官ID和游戏昵称 if not kg.is_renzheng: jilu.shenheguan_id = None jilu.shenheguan_youxi_id = '' jilu.save(update_fields=['shenheguan_id', 'shenheguan_youxi_id']) elif caozuo == 'transfer': # 转移考核官:清空考核官信息,状态回退为待审核 jilu.shenheguan_id = None jilu.shenheguan_youxi_id = None jilu.zhuangtai = 3 jilu.save() # 转移不更新收益,直接返回 elif caozuo == 'modify_nick': # 修改考核官游戏昵称 youxi_id = request.data.get('youxi_id', '').strip() if not youxi_id: return Response({'code': 400, 'msg': '请输入游戏昵称'}) jilu.shenheguan_youxi_id = youxi_id jilu.save() return Response({'code': 0, 'msg': '修改成功'}) else: return Response({'code': 400, 'msg': '无效的操作类型'}) # 5. 更新考核官收益(仅“通过”或“拒绝”时执行) if caozuo in ('transfer'): update_fields = { 'shenhe_zhuangtai': 0, # 设为空闲 } UserShenheguan.objects.filter(user=yonghu).update(**update_fields) if caozuo in ('agree', 'reject'): update_fields = { 'yue': F('yue') + Decimal(str(daishou_fei)), 'zonge': F('zonge') + Decimal(str(daishou_fei)), 'shenhe_zhuangtai': 0, # 设为空闲 } # 只有同意时才增加“通过总数” if caozuo == 'agree': update_fields['tongguo_zongshu'] = F('tongguo_zongshu') + 1 # 审核总数不再增加 UserShenheguan.objects.filter(user=yonghu).update(**update_fields) return Response({'code': 0, 'msg': '操作成功'}) except Exception as e: logger.error(f"考核操作异常: {str(e)}", exc_info=True) return Response({'code': 500, 'msg': '服务器内部错误'}, status=500) # dengji/views.py class KaoheJinpaiTagsView(APIView): """获取考核金牌页面的标签和板块信息""" permission_classes = [IsAuthenticated] def post(self, request): try: user = request.user # 获取用户已拥有的标签名称集合 owned_names = set( YonghuChenghao.objects.filter(yonghu=user, chenghao__leixing='dashou') .values_list('chenghao__mingcheng', flat=True) ) # 板块列表 plate_list = [] for bk in Bankuai.objects.all(): tags = Chenghao.objects.filter(bankuai=bk, leixing='dashou') tag_list = [] for tag in tags: # 费用列表 fee_objs = KaoheCishuFeiyong.objects.filter(chenghao=tag).order_by('cishu') fee_list = [{'cishu': f.cishu, 'feiyong': float(f.feiyong)} for f in fee_objs] # 若没有记录,补充默认费用 if not fee_list: fee_list = [{'cishu': 1, 'feiyong': 5.00}] tag_list.append({ 'id': tag.id, 'name': tag.mingcheng, 'texiao_json': json.loads(tag.texiao_miaoshu) if tag.texiao_miaoshu else {}, 'jinpai': tag.kaioi_jinpai, 'rule': tag.kaohe_guize or '', 'fee_list': fee_list, 'owned': tag.mingcheng in owned_names, }) if tag_list: plate_list.append({ 'bankuai_id': bk.bankuai_id, 'mingcheng': bk.mingcheng, 'tags': tag_list }) # 新增:返回所有在职考核官信息(用于指定考核官) kaoheguan_list = [] for kg in UserShenheguan.objects.filter(zhuangtai=1).select_related('user'): uid = kg.user.yonghuid avatar = kg.user.avatar or '' # 获取考核官的打手昵称(从打手扩展表) dashou_nick = '' dashou_prof = UserDashou.objects.filter(user=kg.user).first() if dashou_prof: dashou_nick = dashou_prof.nicheng or '' kaoheguan_list.append({ 'yonghuid': uid, 'avatar': avatar, 'dashou_nick': dashou_nick, 'is_renzheng': kg.is_renzheng, 'shenhe_zhuangtai': kg.shenhe_zhuangtai, }) return Response({ 'code': 0, 'data': { 'plate_list': plate_list, 'my_tags': list(owned_names), 'kaoheguan_list': kaoheguan_list, # 新增 } }) except Exception as e: return Response({'code': 500, 'msg': str(e)}, status=500) 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.yonghuid, zhuangtai__in=[0, 2, 3] ) else: query = ShenheJilu.objects.filter( shenqingren_id=user.yonghuid, zhuangtai=1 # 已通过 ) total = query.count() records = query.order_by('-create_time')[(page-1)*page_size : page*page_size] data = [] for jl in records: # 汇总缴纳总额 total_fee = KaoheJiluFeiyong.objects.filter(jilu=jl).aggregate( total=models.Sum('jine') )['total'] or 0.00 # 上次拒绝原因(最近一条) last_fail = KaoheJujueJilu.objects.filter(jilu=jl).order_by('-create_time').first() fail_reason = last_fail.yuanyin if last_fail else '' # 考核官头像 kg_user = UserMain.objects.filter(yonghuid=jl.shenheguan_id).first() kg_avatar = kg_user.avatar if kg_user else '' 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, 'create_time': jl.create_time.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 KaoheFreeApplyView(APIView): """免费申请考核(首次/再次,费用为0时调用)""" permission_classes = [IsAuthenticated] def post(self, request): try: user = request.user tag_id = request.data.get('tag_id') game_nick = request.data.get('game_nick', '').strip() remark = request.data.get('remark', '').strip() reapply = request.data.get('reapply', False) jilu_id = request.data.get('jilu_id') if reapply else None shenheguan_id = request.data.get('shenheguan_id', '').strip() # 新增 if not tag_id or not game_nick: return Response({'code': 400, 'msg': '标签ID和游戏昵称必填'}) # 校验费用是否为0 try: chenghao = Chenghao.objects.get(id=tag_id) except Chenghao.DoesNotExist: return Response({'code': 400, 'msg': '标签不存在'}) # 计算本次次数 if reapply and jilu_id: last_record = ShenheJilu.objects.filter( jilu_id=jilu_id, shenqingren_id=user.yonghuid, zhuangtai=2 ).first() if not last_record: return Response({'code': 400, 'msg': '原记录不存在或状态不是未通过'}) new_cishu = last_record.cishu + 1 else: max_cishu = ShenheJilu.objects.filter( shenqingren_id=user.yonghuid, chenghao=chenghao, zhuangtai__in=[1,2] ).aggregate(max=models.Max('cishu'))['max'] or 0 new_cishu = max_cishu + 1 fee = get_tag_fee(chenghao, new_cishu) if fee > 0: return Response({'code': 400, 'msg': '您有未通过的记录请查看并重考'}) # 新增:如果指定了考核官,进行合法性校验 if shenheguan_id: valid, err = validate_shenheguan(shenheguan_id, chenghao.bankuai, fee, user) if not valid: return Response({'code': 400, 'msg': err}) # 调用公共创建逻辑 success, msg, record = create_shenhe_jilu( user, tag_id, game_nick, remark, reapply, jilu_id, shenheguan_id=shenheguan_id # 新增参数 ) if success: return Response({'code': 0, 'msg': msg, 'data': {'jilu_id': record.jilu_id}}) else: return Response({'code': 400, 'msg': msg}) 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.yonghuid) # 板块筛选 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('-create_time')[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, 'create_time': rej.create_time.strftime('%Y-%m-%d %H:%M:%S') if rej.create_time else '' }) # 6. 批量获取申请人信息(昵称和头像) applicant_ids = {j.shenqingren_id for j in jilu_list} user_map = {} if applicant_ids: users = UserMain.objects.filter(yonghuid__in=applicant_ids).select_related('dashou_profile') for u in users: avatar = u.avatar or '' nicheng = '' try: # 打手扩展表可能有 nicheng nicheng = u.dashou_profile.nicheng if u.dashou_profile else '' except UserDashou.DoesNotExist: pass user_map[u.yonghuid] = { 'avatar': avatar, 'nicheng': nicheng or f'用户{u.yonghuid}' } # 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 '', 'create_time': jl.create_time.strftime('%Y-%m-%d %H:%M:%S') if jl.create_time else '', 'update_time': jl.update_time.strftime('%Y-%m-%d %H:%M:%S') if jl.update_time 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)