feat: 后台考核记录列表统计与退回待审核操作

This commit is contained in:
XingQue
2026-06-23 22:34:18 +08:00
parent 3f4712bb44
commit 8dae04c32c
2 changed files with 370 additions and 2 deletions

View File

@@ -14,7 +14,7 @@ from .view import (GetRolePermissionView,
FineApplyView, PunishDashouView,
FaKuanTongJiView, FaKuanLieBiaoView, FaKuanChuLiView, FaKuanChuangJianView,
HqbkxxView,BkxgView,CaiwuView,CwhybkhqView, HybkjtsjView, SzxxView, CwddhqlxView, CwhqjtddsjView,
CwqtczhqView, KhpzhqView, ChzsgcView,KhgglView, ShgxgsjView, ZxkfghdsView)
CwqtczhqView, KhpzhqView, ChzsgcView,KhgglView, ShgxgsjView, KhjlglView, KhjlczView, ZxkfghdsView)
urlpatterns = [
path('gljshq', GetRolePermissionView.as_view(), name='获取就角色信息'),
@@ -103,6 +103,8 @@ urlpatterns = [
path('khggl', KhgglView.as_view(), name='获取考核管信息'),
path('shgxgsj', ShgxgsjView.as_view(), name='修改考核管数据'),
path('khjlgl', KhjlglView.as_view(), name='考核记录列表与统计'),
path('khjlcz', KhjlczView.as_view(), name='考核记录操作'),
path('zxkfghds', ZxkfghdsView.as_view(), name='链接换打手或重新下单'),

View File

@@ -97,7 +97,7 @@ from config.models import (
## rank
from rank.models import (
KaoheguanBankuai, Bankuai, Chenghao, KaoheCishuFeiyong,
YonghuChenghao
YonghuChenghao, ShenheJilu, KaoheJiluFeiyong, KaoheJujueJilu
)
# 序列化器
@@ -8836,6 +8836,372 @@ class ZxkfghdsView(APIView):
return '游戏订单'
# ==================== 考核记录管理 ====================
SHENHE_ZHUANGTAI_MAP = {
0: '考核中',
1: '已通过',
2: '未通过',
3: '待审核',
}
def _batch_user_profile_map(user_uids):
"""批量获取用户头像、昵称、手机号"""
uids = list({u for u in user_uids if u})
if not uids:
return {}
users = User.query.filter(UserUID__in=uids)
dashou_nicks = {
d.user.UserUID: d.nicheng
for d in UserDashou.query.filter(user__UserUID__in=uids).select_related('user')
}
result = {}
for u in users:
result[u.UserUID] = {
'yonghuid': u.UserUID,
'avatar': u.Avatar or '',
'phone': u.Phone or '',
'nicheng': dashou_nicks.get(u.UserUID) or u.UserName or u.Phone or u.UserUID,
}
return result
def _build_shenhe_jilu_filters(request_data):
"""构造 ShenheJilu 查询条件(列表与统计共用)"""
qs = ShenheJilu.query.select_related('bankuai', 'chenghao')
jilu_id = (request_data.get('jilu_id') or '').strip()
if jilu_id:
qs = qs.filter(jilu_id=jilu_id)
zhuangtai = request_data.get('zhuangtai')
if zhuangtai is not None and zhuangtai != '':
qs = qs.filter(zhuangtai=int(zhuangtai))
assessed = request_data.get('assessed')
if assessed == 'done':
qs = qs.filter(zhuangtai__in=[1, 2])
elif assessed == 'undone':
qs = qs.filter(zhuangtai__in=[0, 3])
bankuai_id = request_data.get('bankuai_id')
if bankuai_id not in (None, ''):
qs = qs.filter(bankuai_id=int(bankuai_id))
chenghao_id = request_data.get('chenghao_id')
if chenghao_id not in (None, ''):
qs = qs.filter(chenghao_id=int(chenghao_id))
cishu = request_data.get('cishu')
if cishu not in (None, ''):
qs = qs.filter(cishu=int(cishu))
dashou_kw = (request_data.get('dashou_keyword') or '').strip()
if dashou_kw:
dashou_uids = list(
User.query.filter(
Q(UserUID=dashou_kw) |
Q(UserName__icontains=dashou_kw) |
Q(Phone__icontains=dashou_kw) |
Q(DashouProfile__nicheng__icontains=dashou_kw)
).values_list('UserUID', flat=True)
)
if dashou_uids:
qs = qs.filter(shenqingren_id__in=dashou_uids)
else:
qs = qs.none()
kg_kw = (request_data.get('kaoheguan_keyword') or '').strip()
if kg_kw:
kg_uids = list(
User.query.filter(
Q(UserUID=kg_kw) |
Q(UserName__icontains=kg_kw) |
Q(Phone__icontains=kg_kw) |
Q(DashouProfile__nicheng__icontains=kg_kw)
).values_list('UserUID', flat=True)
)
if kg_uids:
qs = qs.filter(shenheguan_id__in=kg_uids)
else:
qs = qs.none()
start_time = (request_data.get('start_time') or '').strip()
end_time = (request_data.get('end_time') or '').strip()
if start_time:
qs = qs.filter(CreateTime__gte=start_time)
if end_time:
qs = qs.filter(CreateTime__lte=end_time)
has_kaoheguan = request_data.get('has_kaoheguan')
if has_kaoheguan in (True, 'true', '1', 1):
qs = qs.exclude(shenheguan_id__isnull=True).exclude(shenheguan_id='')
elif has_kaoheguan in (False, 'false', '0', 0):
qs = qs.filter(Q(shenheguan_id__isnull=True) | Q(shenheguan_id=''))
return qs
def _serialize_shenhe_jilu_row(jl, profile_map, fee_map=None, reject_count_map=None):
dashou = profile_map.get(jl.shenqingren_id, {
'yonghuid': jl.shenqingren_id,
'avatar': '',
'phone': '',
'nicheng': jl.shenqingren_id,
})
kaoheguan = None
if jl.shenheguan_id:
kaoheguan = profile_map.get(jl.shenheguan_id, {
'yonghuid': jl.shenheguan_id,
'avatar': '',
'phone': '',
'nicheng': jl.shenheguan_id,
})
kaoheguan['game_id'] = jl.shenheguan_youxi_id or ''
current_fee = 0.0
if fee_map is not None:
current_fee = fee_map.get((jl.jilu_id, jl.cishu), 0.0)
else:
fd = KaoheJiluFeiyong.query.filter(jilu_id=jl.jilu_id, cishu=jl.cishu).first()
if fd:
current_fee = float(fd.jine)
reject_count = 0
if reject_count_map is not None:
reject_count = reject_count_map.get(jl.jilu_id, 0)
return {
'jilu_id': jl.jilu_id,
'zhuangtai': jl.zhuangtai,
'zhuangtai_text': SHENHE_ZHUANGTAI_MAP.get(jl.zhuangtai, str(jl.zhuangtai)),
'cishu': jl.cishu,
'jiaofei_jine': float(jl.jiaofei_jine or 0),
'current_fee': current_fee,
'bankuai_id': jl.bankuai.bankuai_id if jl.bankuai else None,
'bankuai_name': jl.bankuai.mingcheng if jl.bankuai else '',
'chenghao_id': jl.chenghao.id if jl.chenghao else None,
'chenghao_name': jl.chenghao.mingcheng if jl.chenghao else '',
'chenghao_icon': jl.chenghao.tubiao_url if jl.chenghao else '',
'guize_neirong': jl.guize_neirong or '',
'dashou': {
**dashou,
'game_id': jl.dashou_youxi_id or '',
'beizhu': jl.dashou_beizhu or '',
},
'kaoheguan': kaoheguan,
'reject_count': reject_count,
'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 '',
}
class KhjlglView(APIView):
"""
考核记录列表 + 统计
POST /houtai/khjlgl
"""
permission_classes = [IsAuthenticated]
def post(self, request):
try:
username_from_frontend = request.data.get('phone', None)
kefu_obj, permissions = verify_kefu_permission(request, username_from_frontend)
if kefu_obj is None:
return permissions
if 'kaohepeizhi' not in permissions:
return Response({'code': 403, 'msg': '无考核管理权限'}, status=403)
page = int(request.data.get('page', 1))
page_size = int(request.data.get('page_size', 20))
if page < 1:
page = 1
if page_size < 1:
page_size = 20
if page_size > 100:
page_size = 100
qs = _build_shenhe_jilu_filters(request.data)
stats_qs = qs
agg = stats_qs.aggregate(
total_fee=Sum('jiaofei_jine'),
)
tag_rows = stats_qs.values(
'chenghao_id', 'chenghao__mingcheng'
).annotate(
total=Count('jilu_id'),
passed=Count('jilu_id', filter=Q(zhuangtai=1)),
failed=Count('jilu_id', filter=Q(zhuangtai=2)),
in_progress=Count('jilu_id', filter=Q(zhuangtai=0)),
pending=Count('jilu_id', filter=Q(zhuangtai=3)),
).order_by('-total')
stats = {
'total': stats_qs.count(),
'passed': stats_qs.filter(zhuangtai=1).count(),
'failed': stats_qs.filter(zhuangtai=2).count(),
'in_progress': stats_qs.filter(zhuangtai=0).count(),
'pending': stats_qs.filter(zhuangtai=3).count(),
'total_fee': float(agg['total_fee'] or 0),
'tag_stats': [
{
'chenghao_id': r['chenghao_id'],
'chenghao_name': r['chenghao__mingcheng'] or '未知标签',
'total': r['total'],
'passed': r['passed'],
'failed': r['failed'],
'in_progress': r['in_progress'],
'pending': r['pending'],
}
for r in tag_rows if r['chenghao_id']
],
}
total = qs.count()
start = (page - 1) * page_size
rows = qs.order_by('-CreateTime')[start:start + page_size]
uids = []
jilu_ids = []
for jl in rows:
uids.append(jl.shenqingren_id)
if jl.shenheguan_id:
uids.append(jl.shenheguan_id)
jilu_ids.append(jl.jilu_id)
profile_map = _batch_user_profile_map(uids)
fee_pairs = [(jl.jilu_id, jl.cishu) for jl in rows]
fee_map = {}
if fee_pairs:
jids = [p[0] for p in fee_pairs]
for fd in KaoheJiluFeiyong.query.filter(jilu_id__in=jids):
fee_map[(fd.jilu_id, fd.cishu)] = float(fd.jine)
reject_counts = dict(
KaoheJujueJilu.query.filter(jilu_id__in=jilu_ids)
.values('jilu_id')
.annotate(cnt=Count('id'))
.values_list('jilu_id', 'cnt')
) if jilu_ids else {}
data_list = [
_serialize_shenhe_jilu_row(jl, profile_map, fee_map, reject_counts)
for jl in rows
]
all_bankuai = list(Bankuai.query.values('bankuai_id', 'mingcheng'))
all_tags = list(
Chenghao.query.filter(leixing='dashou')
.values('id', 'mingcheng', 'bankuai_id')
.order_by('mingcheng')
)
return Response({
'code': 0,
'msg': 'ok',
'data': {
'total': total,
'page': page,
'page_size': page_size,
'list': data_list,
'stats': stats,
'all_bankuai': all_bankuai,
'all_tags': all_tags,
},
})
except Exception as e:
logger.exception('KhjlglView 接口错误')
return Response({'code': 1, 'msg': f'服务器内部错误: {str(e)}'}, status=500)
class KhjlczView(APIView):
"""
考核记录操作 / 详情
POST /houtai/khjlcz
action:
- get_detail: 获取单条详情(含缴费明细、拒绝历史)
- to_pending: 考核中 → 待审核(取消指定考核官,与小程序 transfer 一致)
"""
permission_classes = [IsAuthenticated]
def post(self, request):
try:
username_from_frontend = request.data.get('phone', None)
kefu_obj, permissions = verify_kefu_permission(request, username_from_frontend)
if kefu_obj is None:
return permissions
if 'kaohepeizhi' not in permissions:
return Response({'code': 403, 'msg': '无考核管理权限'}, status=403)
action = request.data.get('action')
jilu_id = (request.data.get('jilu_id') or '').strip()
if not jilu_id:
return Response({'code': 1, 'msg': '缺少记录ID'}, status=400)
if action == 'get_detail':
try:
jl = ShenheJilu.query.select_related('bankuai', 'chenghao').get(jilu_id=jilu_id)
except ShenheJilu.DoesNotExist:
return Response({'code': 1, 'msg': '记录不存在'}, status=404)
uids = [jl.shenqingren_id]
if jl.shenheguan_id:
uids.append(jl.shenheguan_id)
profile_map = _batch_user_profile_map(uids)
fee_list = [
{
'cishu': f.cishu,
'jine': float(f.jine),
}
for f in KaoheJiluFeiyong.query.filter(jilu_id=jilu_id).order_by('cishu')
]
reject_list = []
for r in KaoheJujueJilu.query.filter(jilu_id=jilu_id).order_by('-CreateTime'):
kg_prof = _batch_user_profile_map([r.shenheguan_id]).get(r.shenheguan_id, {})
reject_list.append({
'cishu': r.cishu,
'shenheguan_id': r.shenheguan_id,
'shenheguan_nicheng': kg_prof.get('nicheng', r.shenheguan_id),
'yuanyin': r.yuanyin,
'CreateTime': r.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if r.CreateTime else '',
})
record = _serialize_shenhe_jilu_row(jl, profile_map)
record['fee_list'] = fee_list
record['reject_list'] = reject_list
return Response({'code': 0, 'msg': 'ok', 'data': record})
if action == 'to_pending':
with transaction.atomic():
jl = ShenheJilu.objects.select_for_update().filter(jilu_id=jilu_id).first()
if not jl:
return Response({'code': 1, 'msg': '记录不存在'}, status=404)
if jl.zhuangtai != 0:
return Response({'code': 1, 'msg': '仅「考核中」的记录可退回待审核'}, status=400)
old_kg_id = jl.shenheguan_id
jl.shenheguan_id = None
jl.shenheguan_youxi_id = None
jl.zhuangtai = 3
jl.save(update_fields=['shenheguan_id', 'shenheguan_youxi_id', 'zhuangtai', 'UpdateTime'])
if old_kg_id:
UserShenheguan.query.filter(user__UserUID=old_kg_id).update(shenhe_zhuangtai=0)
return Response({'code': 0, 'msg': '已退回待审核,考核官指定已取消'})
return Response({'code': 1, 'msg': '无效的操作类型'}, status=400)
except Exception as e:
logger.exception('KhjlczView 接口错误')
return Response({'code': 1, 'msg': f'服务器内部错误: {str(e)}'}, status=500)