第一次提交:小程序前端最新版本

This commit is contained in:
XingQue
2026-06-14 02:38:05 +08:00
commit 5e90f7c87c
677 changed files with 153758 additions and 0 deletions

0
dengji/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

3
dengji/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
dengji/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class DengjiConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "dengji"

View File

235
dengji/models.py Normal file
View File

@@ -0,0 +1,235 @@
from django.db import models
from django.conf import settings
from dingdan.models import Dingdan
# ==================== 板块表 ====================
class Bankuai(models.Model):
bankuai_id = models.AutoField(primary_key=True, verbose_name='板块ID')
mingcheng = models.CharField(max_length=50, unique=True, db_index=True, verbose_name='板块名称')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
db_table = 'bankuai'
verbose_name = '板块'
verbose_name_plural = verbose_name
def __str__(self):
return self.mingcheng
# ==================== 打手订单表现记录 ====================
class DashouBiaoxian(models.Model):
dingdan = models.ForeignKey('dingdan.Dingdan', to_field='dingdan_id', on_delete=models.CASCADE, related_name='biaoxian', verbose_name='关联订单', db_index=True)
dashou_id = models.CharField(max_length=32, db_index=True, verbose_name='打手ID')
bankuai = models.ForeignKey(Bankuai, on_delete=models.PROTECT, to_field='bankuai_id', verbose_name='所属板块', db_index=True,null=True)
shangxian_sudu = models.PositiveSmallIntegerField(null=True, blank=True, verbose_name='上线速度评价(1-5)')
fuwu_taidu = models.PositiveSmallIntegerField(null=True, blank=True, verbose_name='服务态度评价(1-5)')
fanche_cishu = models.PositiveIntegerField(null=True, blank=True, verbose_name='翻车次数')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
jiedan_time = models.DateTimeField(null=True, blank=True, verbose_name='接单时间')
tijiao_time = models.DateTimeField(null=True, blank=True, verbose_name='提交时间')
class Meta:
db_table = 'dashou_biaoxian'
verbose_name = '打手订单表现'
verbose_name_plural = verbose_name
indexes = [models.Index(fields=['dashou_id', 'bankuai', 'create_time'])]
# ==================== 经验变动记录 ====================
class JingyanJilu(models.Model):
yonghu_id = models.CharField(max_length=7, db_index=True, verbose_name='用户ID')
yonghu_leixing = models.CharField(max_length=10, choices=[('dashou','打手'),('boss','老板'),('shangjia','商家'),('guanshi','管事'),('zuzhang','组长')], verbose_name='用户类型')
bankuai = models.ForeignKey(Bankuai, on_delete=models.PROTECT, to_field='bankuai_id', verbose_name='所属板块', db_index=True)
sudu_jingyan = models.DecimalField(max_digits=8, decimal_places=2, default=0, verbose_name='上线速度经验')
shili_jingyan = models.DecimalField(max_digits=8, decimal_places=2, default=0, verbose_name='打手实力经验')
taidu_jingyan = models.DecimalField(max_digits=8, decimal_places=2, default=0, verbose_name='服务态度经验')
zonghe_jingyan = models.DecimalField(max_digits=8, decimal_places=2, default=0, verbose_name='综合经验')
dingdan_id = models.CharField(max_length=32, null=True, blank=True, db_index=True, verbose_name='关联订单ID')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'jingyan_jilu'
verbose_name = '经验变动记录'
verbose_name_plural = verbose_name
indexes = [models.Index(fields=['yonghu_id', 'bankuai', 'create_time'])]
# ==================== 称号主表 ====================
class Chenghao(models.Model):
mingcheng = models.CharField(max_length=50, unique=True, verbose_name='称号名称')
leixing = models.CharField(max_length=10, choices=[('dashou','打手'),('boss','老板'),('shangjia','商家'),('guanshi','管事'),('zuzhang','组长')], verbose_name='称号类型')
bankuai = models.ForeignKey(Bankuai, on_delete=models.PROTECT, to_field='bankuai_id', null=True, blank=True, verbose_name='所属板块(空=全板块通用)')
tubiao_url = models.CharField(max_length=200, null=True, blank=True, verbose_name='称号图标相对URL')
texiao_miaoshu = models.TextField(null=True, blank=True, verbose_name='特效描述(JSON)')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
kaohe_guize = models.TextField(null=True, blank=True, verbose_name='考核规则') # 新增
kaioi_jinpai = models.BooleanField(default=False, verbose_name='是否同时开启金牌')
class Meta:
db_table = 'chenghao'
verbose_name = '称号'
verbose_name_plural = verbose_name
indexes = [models.Index(fields=['leixing', 'bankuai'])]
# ==================== 用户 - 称号关联 ====================
class YonghuChenghao(models.Model):
yonghu = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='chenghao_list', verbose_name='用户', db_index=True)
chenghao = models.ForeignKey(Chenghao, on_delete=models.CASCADE, verbose_name='称号')
huode_time = models.DateTimeField(auto_now_add=True, verbose_name='获得时间')
class Meta:
db_table = 'yonghu_chenghao'
verbose_name = '用户称号关联'
verbose_name_plural = verbose_name
unique_together = ('yonghu', 'chenghao')
# 修复:只索引本表字段
indexes = [models.Index(fields=['yonghu', 'chenghao'])]
class DingdanBiaoqian(models.Model):
"""订单需求标签关联表"""
dingdan = models.ForeignKey(
Dingdan,
to_field='dingdan_id', # 关联到字符串字段 dingdan_id
on_delete=models.CASCADE,
verbose_name='订单',
db_index=True
)
chenghao = models.ForeignKey('dengji.Chenghao', on_delete=models.CASCADE, verbose_name='标签')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
db_table = 'dingdan_biaoqian'
verbose_name = '订单需求标签'
verbose_name_plural = verbose_name
unique_together = ('dingdan', 'chenghao')
indexes = [models.Index(fields=['dingdan'])]
class ShenheJilu(models.Model):
"""审核记录表"""
shenqingren_id = models.CharField(max_length=7, verbose_name='申请人用户ID')
shenheguan_id = models.CharField(max_length=7, null = True,verbose_name='审核官用户ID')
zhuangtai = models.IntegerField(choices=[(0, '审核中'), (1, '通过'), (2, '未通过'), (3, '待审核')], default=0, verbose_name='审核状态')
bankuai = models.ForeignKey(Bankuai, on_delete=models.PROTECT, to_field='bankuai_id', null=True, blank=True, verbose_name='审核板块')
chenghao = models.ForeignKey(Chenghao, on_delete=models.PROTECT, null=True, blank=True, verbose_name='审核标签')
cishu = models.IntegerField(default=1, verbose_name='第几次审核')
jiaofei_jine = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='缴纳费用')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
jilu_id = models.CharField(max_length=32, unique=True, db_index=True, verbose_name='记录ID') # 新增
guize_neirong = models.TextField(null=True, blank=True, verbose_name='规则内容')
shenheguan_youxi_id = models.CharField(max_length=50, null=True, blank=True, verbose_name='审核官游戏ID')
dashou_youxi_id = models.CharField(max_length=50, null=True, blank=True, verbose_name='打手游戏ID')
dashou_beizhu = models.TextField(null=True, blank=True, verbose_name='打手备注')
class Meta:
db_table = 'shenhe_jilu'
verbose_name = '审核记录'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['shenqingren_id', 'zhuangtai']),
models.Index(fields=['shenheguan_id']),
]
class KaoheJiluFeiyong(models.Model):
"""每次考核记录实际缴纳的费用明细"""
jilu = models.ForeignKey(ShenheJilu, on_delete=models.CASCADE, to_field='jilu_id', verbose_name='考核记录')
cishu = models.IntegerField(verbose_name='第几次考核')
jine = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='缴纳金额')
class Meta:
db_table = 'kaohe_jilu_feiyong'
verbose_name = '考核记录费用'
verbose_name_plural = verbose_name
unique_together = ('jilu', 'cishu')
class KaohePayTemp(models.Model):
"""考核支付临时上下文(仅用于支付成功后回调取参,不删除数据)"""
dingdan_id = models.CharField(max_length=50, unique=True, db_index=True, verbose_name='订单ID')
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='用户ID')
tag_id = models.IntegerField(verbose_name='标签ID')
game_nick = models.CharField(max_length=100, verbose_name='游戏昵称')
remark = models.TextField(blank=True, verbose_name='备注')
reapply = models.BooleanField(default=False, verbose_name='是否再次申请')
jilu_id = models.CharField(max_length=32, null=True, blank=True, verbose_name='原记录ID(再次申请时)')
shenheguan_id = models.CharField(max_length=7, blank=True, verbose_name='指定考核官ID')
cishu = models.IntegerField(verbose_name='本次考核次数(预计算)')
fee = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='本次费用(分账单位)')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
db_table = 'kaohe_pay_temp'
verbose_name = '考核支付临时表'
verbose_name_plural = verbose_name
class KaoheCishuFeiyong(models.Model):
"""各标签各考核次数所需缴纳金额"""
chenghao = models.ForeignKey(Chenghao, on_delete=models.CASCADE, verbose_name='标签')
cishu = models.IntegerField(verbose_name='第几次考核')
feiyong = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='需缴纳金额')
class Meta:
db_table = 'kaohe_cishu_feiyong'
verbose_name = '考核次数费用'
verbose_name_plural = verbose_name
unique_together = ('chenghao', 'cishu')
indexes = [models.Index(fields=['chenghao', 'cishu'])]
class KaoheJujueJilu(models.Model):
"""考核拒绝原因记录"""
jilu = models.ForeignKey(ShenheJilu, on_delete=models.CASCADE, to_field='jilu_id', verbose_name='审核记录')
cishu = models.IntegerField(verbose_name='对应次数')
shenheguan_id = models.CharField(max_length=7, verbose_name='考核官用户ID')
yuanyin = models.TextField(verbose_name='拒绝原因')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
db_table = 'kaohe_jujue_jilu'
verbose_name = '拒绝原因记录'
verbose_name_plural = verbose_name
indexes = [models.Index(fields=['jilu_id', 'cishu'])]
class KaoheguanBankuai(models.Model):
"""考核官与板块多对多关联表"""
kaoheguan_id = models.CharField(max_length=7, db_index=True, verbose_name='考核官用户ID')
bankuai = models.ForeignKey(Bankuai, on_delete=models.CASCADE, to_field='bankuai_id', verbose_name='所属板块')
class Meta:
db_table = 'kaoheguan_bankuai'
verbose_name = '考核官板块关联'
verbose_name_plural = verbose_name
unique_together = ('kaoheguan_id', 'bankuai')
indexes = [models.Index(fields=['kaoheguan_id'])]
class ShenheGuize(models.Model):
"""审核规则表"""
guize_id = models.AutoField(primary_key=True, verbose_name='规则ID')
neirong = models.TextField(verbose_name='规则内容')
class Meta:
db_table = 'shenhe_guize'
verbose_name = '审核规则'
verbose_name_plural = verbose_name

87
dengji/services.py Normal file
View File

@@ -0,0 +1,87 @@
# dengji/services.py
import logging
from django.db import transaction, IntegrityError
from django.utils import timezone
from .models import DashouBiaoxian
logger = logging.getLogger('dengji')
def record_dashou_biaoxian(dingdan_id, dashou_id, xingwei, **kwargs):
"""
统一记录打手订单表现
:param dingdan_id: 订单ID
:param dashou_id: 打手ID
:param xingwei: 行为类型 1-抢单 2-提交订单 3-结算
:param kwargs: 可选字段(与模型字段同名):
jiedan_time, tijiao_time,
bankuai, shangxian_sudu, fuwu_taidu, fanche_cishu
注意不管是哪种行为dingdan_id 和 dashou_id 都是必传的,
如果没传时间且业务需要时间,则自动取当前时间。
"""
try:
# ----- 抢单:创建记录 -----
if xingwei == 1:
jiedan_time = kwargs.get('jiedan_time') or timezone.now()
bankuai = kwargs.get('bankuai') # 可为 None
# 避免重复创建(同一订单只会有一条表现记录)
obj, created = DashouBiaoxian.objects.get_or_create(
dingdan_id=dingdan_id,
dashou_id=dashou_id,
defaults={
'bankuai': bankuai,
'jiedan_time': jiedan_time,
}
)
if not created:
logger.info(f"表现记录已存在,跳过创建: dingdan={dingdan_id}, dashou={dashou_id}")
else:
logger.info(f"创建抢单表现记录: dingdan={dingdan_id}, dashou={dashou_id}")
# ----- 提交订单:更新提交时间 -----
elif xingwei == 2:
tijiao_time = kwargs.get('tijiao_time') or timezone.now()
# 只更新已存在的记录,找不到就直接跳过
updated = DashouBiaoxian.objects.filter(
dingdan_id=dingdan_id,
dashou_id=dashou_id
).update(tijiao_time=tijiao_time)
if updated:
logger.info(f"更新提交时间: dingdan={dingdan_id}, dashou={dashou_id}")
else:
logger.info(f"无对应记录,跳过提交时间更新: dingdan={dingdan_id}, dashou={dashou_id}")
# ----- 结算:更新评价信息 -----
elif xingwei == 3:
# 构建要更新的字段,只更新 kwargs 中传入的非 None 字段
update_fields = {}
for field in ['shangxian_sudu', 'fuwu_taidu', 'fanche_cishu']:
val = kwargs.get(field)
if val is not None:
update_fields[field] = val
# 如果还传了板块也可以更新
bankuai = kwargs.get('bankuai')
if bankuai is not None:
update_fields['bankuai'] = bankuai
if update_fields:
updated = DashouBiaoxian.objects.filter(
dingdan_id=dingdan_id,
dashou_id=dashou_id
).update(**update_fields)
if updated:
logger.info(f"更新评价信息: dingdan={dingdan_id}, dashou={dashou_id}, fields={update_fields}")
else:
logger.info(f"无对应记录,跳过评价更新: dingdan={dingdan_id}, dashou={dashou_id}")
else:
logger.info(f"结算时未传入任何评价字段,跳过更新")
else:
logger.warning(f"未知的行为类型: {xingwei}")
except IntegrityError as e:
# 唯一约束冲突get_or_create 理论上已处理,但并发时仍可能撞上)
logger.warning(f"表现记录写入冲突,可能并发创建: {e}")
except Exception as e:
# 无论发生什么错误,都不影响主流程
logger.error(f"记录打手表现异常: dingdan={dingdan_id}, dashou={dashou_id}, xingwei={xingwei}, error={e}", exc_info=True)

3
dengji/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

22
dengji/urls.py Normal file
View File

@@ -0,0 +1,22 @@
from django.urls import path
from .views import DashouBiaoqianView, BankuaiBiaoqianView, KaoheguanRegisterView, \
KaoheguanRefreshView, ShenheDatingModuleView, ShenheDatingDataView, ShenheQiangdanView, ShenheCaozuoView, \
ShenheXiangqingView, KaoheJinpaiTagsView, KaoheJiluView, KaoheFreeApplyView, KaoheJiluListView
urlpatterns = [
path('dsbqhq', DashouBiaoqianView.as_view(), name='打手标签获取'),
# 其他已有路由保持不变
path('bkqhbq', BankuaiBiaoqianView.as_view(), name='抢单页面获取标签'),
path('khgzc', KaoheguanRegisterView.as_view(), name='审核官注册'),
path('khgsx', KaoheguanRefreshView.as_view(), name='审核官刷新'),
path('shdthqmk', ShenheDatingModuleView.as_view(), name='审核官获取板块'),
path('hqshsj', ShenheDatingDataView.as_view(), name='审核官获取待审核列表'),
path('shqd', ShenheQiangdanView.as_view(), name='审核官接待审核'),
path('hqkhxq', ShenheXiangqingView.as_view(), name='获取审核内容'),
path('khgxgkh', ShenheCaozuoView.as_view(), name='修改审核状态'),
path('dskhhq', KaoheJinpaiTagsView.as_view(), name='考核金牌页面标签'),
path('dskhjl', KaoheJiluView.as_view(), name='打手考核记录'),
path('dssqkh', KaoheFreeApplyView.as_view(), name='免费考核申请接口'),
path('khghqkhjl', KaoheJiluListView.as_view(), name='考核官获取考核记录'),
]

403
dengji/utils.py Normal file
View File

@@ -0,0 +1,403 @@
# dingdan/utils.py
from .models import DingdanBiaoqian
from yonghu.models import UserMain
from dengji.models import YonghuChenghao
def check_dashou_biaoqian_required(order_id, dashou_yonghuid):
"""
检查打手是否满足订单的标签需求。
- 如果订单没有需求标签,直接放行。
- 如果有,则要求打手至少拥有其中一个标签。
返回 (allowed: bool, message: str)
"""
try:
# 1. 获取订单的需求标签ID集合
required_ids = set(
DingdanBiaoqian.objects.filter(dingdan_id=order_id)
.values_list('chenghao_id', flat=True)
)
if not required_ids:
return True, ''
# 2. 找到打手对应的用户记录
try:
user = UserMain.objects.get(yonghuid=dashou_yonghuid)
except UserMain.DoesNotExist:
return False, '用户信息异常'
# 3. 获取打手已拥有的标签ID集合只取打手类型
owned_ids = set(
YonghuChenghao.objects.filter(
yonghu=user,
chenghao__leixing='dashou'
).values_list('chenghao_id', flat=True)
)
if not owned_ids:
return False, '您未拥有任何打手标签,无法抢此订单'
# 4. 交集判断
if required_ids.intersection(owned_ids):
return True, ''
else:
return False, '您的标签不符合此订单要求,无法抢单'
except Exception as e:
# 标签校验失败不影响主流程,记录日志并保守放行(也可保守拒绝,按需求选择)
import logging
logger = logging.getLogger('dingdan')
logger.error(f"标签校验异常: {e}", exc_info=True)
# 安全起见,如果校验出错,选择拒绝,避免越权
return False, '订单标签校验异常,请稍后重试'
# dengji/utils.py
from .models import Chenghao, KaoheCishuFeiyong, ShenheJilu, KaoheJiluFeiyong, YonghuChenghao, KaoheguanBankuai
from yonghu.models import UserMain, UserShenheguan, UserDashou
from django.db import models
from decimal import Decimal
from django.db.models import Q, Max
import time
import random
import logging
from django.db.models import F, Max
logger = logging.getLogger('dengji')
def get_tag_fee(chenghao, cishu):
"""
获取标签在指定考核次数下的费用。
- 若有对应次数的配置,直接返回。
- 若没有该次数,取该标签下已有最大次数的费用(作为后续次数的价格)。
- 若该标签完全没有费用记录,返回 0.00(免费)。
"""
fee_obj = KaoheCishuFeiyong.objects.filter(chenghao=chenghao, cishu=cishu).first()
if fee_obj:
return fee_obj.feiyong
max_obj = KaoheCishuFeiyong.objects.filter(chenghao=chenghao).order_by('-cishu').first()
if max_obj:
return max_obj.feiyong
return Decimal('0.00')
def generate_unique_jilu_id():
"""
生成唯一的审核记录 ID格式SH + 时间戳(10位) + 随机数(4位)
循环检查数据库避免重复,最多尝试 10 次。
"""
for _ in range(10):
timestamp = str(int(time.time() * 1000))[-10:]
random_part = str(random.randint(1000, 9999))
jilu_id = f'SH{timestamp}{random_part}'
if not ShenheJilu.objects.filter(jilu_id=jilu_id).exists():
return jilu_id
raise Exception('无法生成唯一记录ID请稍后重试')
def validate_shenheguan(shenheguan_id, bankuai, fee, applicant_user):
"""
校验指定考核官是否合法
- 考核官必须存在且未被禁用
- 必须属于对应板块
- 必须处于空闲状态
- 若费用>0则要求已认证
返回 (valid, error_msg)
"""
if not shenheguan_id:
return True, ''
try:
kg = UserShenheguan.objects.select_related('user').get(
user__yonghuid=shenheguan_id,
zhuangtai=1
)
except UserShenheguan.DoesNotExist:
return False, '考核官不存在或已被禁用'
# 板块权限
if not KaoheguanBankuai.objects.filter(kaoheguan_id=shenheguan_id, bankuai=bankuai).exists():
return False, '该考核官不属于此板块'
# 空闲状态
if kg.shenhe_zhuangtai != 0:
return False, '该考核官当前忙碌,无法指定'
# 费用>0需要认证
if float(fee) > 0 and not kg.is_renzheng:
return False, '费用大于0时只能指定已认证的考核官'
return True, ''
def create_shenhe_jilu(user, tag_id, game_nick, remark, reapply, jilu_id=None, shenheguan_id=''):
"""
创建或更新审核记录。
- 首次申请:创建新记录,生成唯一 jilu_id。
- 再次申请:基于原记录(必须状态为未通过)更新次数、累加总费用、更新备注和游戏昵称,
保留审核官信息,状态重置为待审核。不创建新记录。
- 每次申请均创建一条费用明细记录KaoheJiluFeiyong记录本次缴费。
- 若原记录有考核官,则将其状态改为审核中。
- 新增 shenheguan_id 参数指定考核官ID仅在原记录无考核官时可传入。
返回 (success, message, record)
"""
try:
# 1. 校验标签存在
try:
chenghao = Chenghao.objects.get(id=tag_id)
except Chenghao.DoesNotExist:
return False, '标签不存在', None
# 2. 判断用户是否已拥有该标签
if YonghuChenghao.objects.filter(yonghu=user, chenghao=chenghao).exists():
return False, '您已拥有该标签,无需重复考核', None
# 3. 检查是否有进行中的审核记录(待审核、审核中)
has_pending = ShenheJilu.objects.filter(
shenqingren_id=user.yonghuid,
chenghao=chenghao,
zhuangtai__in=[0, 3]
).exists()
if has_pending:
return False, '您有未完成的考核申请,请等待结果', None
last_record = None
current_fee = Decimal('0.00')
# 4. 处理再次申请
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 False, '原记录不存在或状态不是未通过', None
new_cishu = last_record.cishu + 1
current_fee = get_tag_fee(chenghao, new_cishu)
# 确定最终状态:有考核官 = 审核中(0),否则待审核(3)
final_kg_id = last_record.shenheguan_id # 保留原来的
if shenheguan_id and not last_record.shenheguan_id:
final_kg_id = shenheguan_id # 新指定
final_zhuangtai = 0 if final_kg_id else 3 # 有考核官就是审核中
if final_zhuangtai == 3:
final_kg_id = None
# 原子化更新
ShenheJilu.objects.filter(pk=last_record.pk).update(
cishu=new_cishu,
jiaofei_jine=F('jiaofei_jine') + current_fee,
dashou_youxi_id=game_nick,
dashou_beizhu=remark,
guize_neirong=chenghao.kaohe_guize or '',
zhuangtai=final_zhuangtai,
shenheguan_id=final_kg_id,
)
last_record.refresh_from_db()
record = last_record
# 如果原考核官被替换或清除,释放旧考核官
if last_record.shenheguan_id != final_kg_id:
if last_record.shenheguan_id:
UserShenheguan.objects.filter(
user__yonghuid=last_record.shenheguan_id
).update(shenhe_zhuangtai=0)
else:
# 首次申请
max_cishu = ShenheJilu.objects.filter(
shenqingren_id=user.yonghuid,
chenghao=chenghao,
zhuangtai__in=[1, 2]
).aggregate(max=Max('cishu'))['max'] or 0
new_cishu = max_cishu + 1
current_fee = get_tag_fee(chenghao, new_cishu)
bankuai = chenghao.bankuai
rule_text = chenghao.kaohe_guize or ''
new_jilu_id = generate_unique_jilu_id()
initial_zhuangtai = 0 if shenheguan_id else 3 # 指定→审核中,否则待审核
record = ShenheJilu.objects.create(
jilu_id=new_jilu_id,
shenqingren_id=user.yonghuid,
zhuangtai=initial_zhuangtai,
bankuai=bankuai,
chenghao=chenghao,
cishu=new_cishu,
jiaofei_jine=current_fee,
dashou_youxi_id=game_nick,
dashou_beizhu=remark,
guize_neirong=rule_text,
shenheguan_id=shenheguan_id if shenheguan_id else None,
)
# 5. 创建费用明细(防重复)
KaoheJiluFeiyong.objects.get_or_create(
jilu=record,
cishu=record.cishu,
defaults={'jine': current_fee}
)
# 6. 更新考核官统计(仅当状态为审核中且有考核官)
if record.zhuangtai == 0 and record.shenheguan_id:
UserShenheguan.objects.filter(
user__yonghuid=record.shenheguan_id
).update(
shenhe_zongshu=F('shenhe_zongshu') + 1,
shenhe_zhuangtai=1
)
return True, '申请成功', record
except Exception as e:
logger.error(f"创建审核记录异常: {e}", exc_info=True)
return False, '系统异常', None
def create_shenhe_jilu_from_temp(user, temp_data):
"""
根据临时表数据创建审核记录(专供支付回调使用)
参数 temp_data 是 KaohePayTemp 对象
返回 (success, message, record)
"""
from .models import ShenheJilu, KaoheJiluFeiyong, Chenghao, YonghuChenghao
from django.db.models import F
import logging
logger = logging.getLogger('dengji')
try:
logger.info(f"开始从临时表创建审核记录: 用户={user.yonghuid}, 订单={temp_data.dingdan_id}")
chenghao = Chenghao.objects.get(id=temp_data.tag_id)
logger.info(f"获取标签成功: {chenghao.mingcheng}")
# 检查是否已拥有标签
if YonghuChenghao.objects.filter(yonghu=user, chenghao=chenghao).exists():
logger.warning(f"用户 {user.yonghuid} 已拥有标签 {chenghao.mingcheng}")
return False, '您已拥有该标签,无需重复考核', None
# 检查是否有进行中的审核
if ShenheJilu.objects.filter(
shenqingren_id=user.yonghuid,
chenghao=chenghao,
zhuangtai__in=[0, 3]
).exists():
logger.warning(f"用户 {user.yonghuid} 有未完成的考核申请")
return False, '您有未完成的考核申请,请等待结果', None
# 使用临时表中预计算好的次数和费用
new_cishu = temp_data.cishu
current_fee = temp_data.fee
reapply = temp_data.reapply
jilu_id = temp_data.jilu_id
shenheguan_id = temp_data.shenheguan_id
logger.info(
f"临时表数据: cishu={new_cishu}, fee={current_fee}, reapply={reapply}, jilu_id={jilu_id}, shenheguan_id={shenheguan_id}")
# 再次申请校验
if reapply:
if not jilu_id:
logger.error("再次申请但未提供原记录ID")
return False, '再次申请必须提供原记录ID', None
last = ShenheJilu.objects.filter(
jilu_id=jilu_id,
shenqingren_id=user.yonghuid,
zhuangtai=2
).first()
if not last:
logger.error(f"原记录不存在或状态不是未通过: jilu_id={jilu_id}")
return False, '原记录不存在或状态不是未通过', None
if new_cishu != last.cishu + 1:
logger.error(f"考核次数不一致: 临时表={new_cishu}, 原记录次数={last.cishu}")
return False, '考核次数不一致', None
logger.info(f"再次申请校验通过原记录ID: {jilu_id}")
# 创建或更新审核记录
if reapply and jilu_id:
# 更新原记录
final_kg_id = last.shenheguan_id
if shenheguan_id and not last.shenheguan_id:
final_kg_id = shenheguan_id
final_zhuangtai = 0 if final_kg_id else 3
if final_zhuangtai == 3:
final_kg_id = None
ShenheJilu.objects.filter(pk=last.pk).update(
cishu=new_cishu,
jiaofei_jine=F('jiaofei_jine') + current_fee,
dashou_youxi_id=temp_data.game_nick,
dashou_beizhu=temp_data.remark,
guize_neirong=chenghao.kaohe_guize or '',
zhuangtai=final_zhuangtai,
shenheguan_id=final_kg_id,
)
last.refresh_from_db()
record = last
# 释放旧考核官
if last.shenheguan_id != final_kg_id and last.shenheguan_id:
UserShenheguan.objects.filter(user__yonghuid=last.shenheguan_id).update(shenhe_zhuangtai=0)
logger.info(f"释放旧考核官: {last.shenheguan_id}")
logger.info(f"更新审核记录成功: jilu_id={record.jilu_id}, 新次数={new_cishu}")
else:
# 首次申请
bankuai = chenghao.bankuai
rule_text = chenghao.kaohe_guize or ''
new_jilu_id = generate_unique_jilu_id()
initial_zhuangtai = 0 if shenheguan_id else 3
record = ShenheJilu.objects.create(
jilu_id=new_jilu_id,
shenqingren_id=user.yonghuid,
zhuangtai=initial_zhuangtai,
bankuai=bankuai,
chenghao=chenghao,
cishu=new_cishu,
jiaofei_jine=current_fee,
dashou_youxi_id=temp_data.game_nick,
dashou_beizhu=temp_data.remark,
guize_neirong=rule_text,
shenheguan_id=shenheguan_id if shenheguan_id else None,
)
logger.info(f"创建审核记录成功: jilu_id={new_jilu_id}, 状态={initial_zhuangtai}")
# 记录费用明细
fee_detail, created = KaoheJiluFeiyong.objects.get_or_create(
jilu=record,
cishu=record.cishu,
defaults={'jine': current_fee}
)
if created:
logger.info(f"创建费用明细成功: 记录ID={record.jilu_id}, 金额={current_fee}")
else:
logger.warning(f"费用明细已存在: 记录ID={record.jilu_id}, 次数={record.cishu}")
# 更新考核官状态
if record.zhuangtai == 0 and record.shenheguan_id:
UserShenheguan.objects.filter(user__yonghuid=record.shenheguan_id).update(
shenhe_zongshu=F('shenhe_zongshu') + 1,
shenhe_zhuangtai=1
)
logger.info(f"更新考核官状态: {record.shenheguan_id}, 审核总数+1, 状态=忙碌")
logger.info(f"从临时表创建审核记录完成: jilu_id={record.jilu_id}")
return True, '申请成功', record
except Chenghao.DoesNotExist:
logger.error(f"标签不存在: tag_id={temp_data.tag_id}")
return False, '标签不存在', None
except Exception as e:
logger.error(f"从临时表创建审核记录异常: {str(e)}", exc_info=True)
return False, '系统异常', None

975
dengji/views.py Normal file
View File

@@ -0,0 +1,975 @@
# dengji/views.py
import logging
import json
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from shangpin.models import ShangpinLeixing
from .models import YonghuChenghao, Chenghao, KaoheCishuFeiyong, KaoheJujueJilu
from .utils import get_tag_fee, create_shenhe_jilu
logger = logging.getLogger('dengji')
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})
from shangpin.models import ShangpinLeixing
from .models import YonghuChenghao, Chenghao
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}})
# dengji/views.py
import logging
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from .models import KaoheguanBankuai, Bankuai
from django.db import transaction
from yonghu.models import UserShenheguan, UserMain, UserDashou
logger = logging.getLogger('dengji')
# 硬编码邀请码
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': '服务器内部错误'})
import json
from django.db import transaction, IntegrityError
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from .models import Bankuai, ShenheJilu, KaoheguanBankuai, Chenghao
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)
import json
import logging
from decimal import Decimal
from django.db import transaction, models
from django.db.models import F, Q, OuterRef, Subquery, Count, Max
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
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)
# dengji/utils.py
from .models import Chenghao, KaoheCishuFeiyong, ShenheJilu, KaoheJiluFeiyong, YonghuChenghao
from yonghu.models import UserMain
from django.db import models
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)
from .utils import validate_shenheguan
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)