进行了完整的视图拆分,同步了 merchant_ops 的修改

This commit is contained in:
2026-07-05 10:01:09 +08:00
parent 9d98ac9e83
commit c5ae30507b
46 changed files with 32768 additions and 188172 deletions

View File

@@ -1,957 +0,0 @@
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 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') or request.data.get('ProductTypeID')
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, leixing='dashou')
data = []
from jituan.services.club_context import resolve_effective_club_id
from jituan.services.club_kaohe_chenghao import filter_chenghao_ids_for_pool
club_id = resolve_effective_club_id(request, request.user)
allowed_ids = set(
filter_chenghao_ids_for_pool(club_id, list(chenghao_list.values_list('id', flat=True)))
)
for ch in chenghao_list:
if ch.id not in allowed_ids:
continue
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.UserUID).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.UserUID).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.UserUID,
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.UserUID)
.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('-CreateTime')[
offset:offset + page_size]
records = []
for jl in jilu_list:
user = User.objects.filter(UserUID=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, # 新增
'CreateTime': jl.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if jl.CreateTime 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.UserUID, 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.UserUID:
return Response({'code': 400, 'msg': '不能审核自己提交的申请'})
# 5. 板块权限:考核官必须属于该板块
is_related = KaoheguanBankuai.objects.filter(
kaoheguan_id=yonghu.UserUID,
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.UserUID
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.UserUID,
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 = User.objects.filter(UserUID=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('-CreateTime').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,
'CreateTime': jl.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if jl.CreateTime 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.UserUID,
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 = User.objects.get(UserUID=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.UserUID,
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.UserUID
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.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]
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('-CreateTime').first()
fail_reason = last_fail.yuanyin if last_fail else ''
# 考核官头像
kg_user = User.objects.filter(UserUID=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,
'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 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.UserUID, 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.UserUID, 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.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)

30
rank/views/__init__.py Normal file
View File

@@ -0,0 +1,30 @@
"""rank.views package aggregate entry - re-export all submodule symbols.
Preserves external `from rank.views import X` for rank/urls.py.
"""
from .biaoqian import (
DashouBiaoqianView,
BankuaiBiaoqianView,
KaoheJinpaiTagsView,
)
from .kaoheguan import (
KAOHEGUAN_INVITE_CODE,
KaoheguanRegisterView,
KaoheguanRefreshView,
KaoheFreeApplyView,
)
from .shenhe import (
ShenheDatingModuleView,
ShenheDatingDataView,
ShenheQiangdanView,
ShenheXiangqingView,
ShenheCaozuoView,
)
from .kaohe_jilu import (
KaoheJiluView,
KaoheJiluListView,
)

179
rank/views/biaoqian.py Normal file
View File

@@ -0,0 +1,179 @@
"""rank.views.biaoqian - 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 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') or request.data.get('ProductTypeID')
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, leixing='dashou')
data = []
from jituan.services.club_context import resolve_effective_club_id
from jituan.services.club_kaohe_chenghao import filter_chenghao_ids_for_pool
club_id = resolve_effective_club_id(request, request.user)
allowed_ids = set(
filter_chenghao_ids_for_pool(club_id, list(chenghao_list.values_list('id', flat=True)))
)
for ch in chenghao_list:
if ch.id not in allowed_ids:
continue
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}})
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.UserUID
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)

243
rank/views/kaohe_jilu.py Normal file
View File

@@ -0,0 +1,243 @@
"""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]
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('-CreateTime').first()
fail_reason = last_fail.yuanyin if last_fail else ''
# 考核官头像
kg_user = User.objects.filter(UserUID=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,
'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)

186
rank/views/kaoheguan.py Normal file
View File

@@ -0,0 +1,186 @@
"""rank.views.kaoheguan - 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__)
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.UserUID).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.UserUID).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.UserUID,
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 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.UserUID, 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.UserUID, 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)

401
rank/views/shenhe.py Normal file
View File

@@ -0,0 +1,401 @@
"""rank.views.shenhe - 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 ShenheDatingModuleView(APIView):
"""审核大厅获取模块列表(板块 + 待审核数量 + 是否关联)"""
permission_classes = [IsAuthenticated]
def post(self, request):
try:
yonghu = request.user
related_ids = set(KaoheguanBankuai.objects.filter(kaoheguan_id=yonghu.UserUID)
.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('-CreateTime')[
offset:offset + page_size]
records = []
for jl in jilu_list:
user = User.objects.filter(UserUID=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, # 新增
'CreateTime': jl.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if jl.CreateTime 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.UserUID, 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.UserUID:
return Response({'code': 400, 'msg': '不能审核自己提交的申请'})
# 5. 板块权限:考核官必须属于该板块
is_related = KaoheguanBankuai.objects.filter(
kaoheguan_id=yonghu.UserUID,
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.UserUID
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.UserUID,
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 = User.objects.filter(UserUID=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('-CreateTime').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,
'CreateTime': jl.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if jl.CreateTime 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.UserUID,
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 = User.objects.get(UserUID=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.UserUID,
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)