将文件夹命名从拼音改为英文,整理了一些代码,将导入语句迁移到了头部

This commit is contained in:
2026-06-14 16:39:09 +08:00
parent 1fd0301e0e
commit 21bb129c5d
178 changed files with 29249 additions and 35862 deletions

0
rank/__init__.py Normal file
View File

3
rank/admin.py Normal file
View File

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

6
rank/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

@@ -0,0 +1,111 @@
# Generated by Django 4.2.27 on 2026-05-12 02:17
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('dingdan', '0026_fadanfenhonglilv_alter_fadan_shenqingren_shenfen_and_more'),
]
operations = [
migrations.CreateModel(
name='Bankuai',
fields=[
('bankuai_id', models.AutoField(primary_key=True, serialize=False, verbose_name='板块ID')),
('mingcheng', models.CharField(db_index=True, max_length=50, unique=True, verbose_name='板块名称')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
],
options={
'verbose_name': '板块',
'verbose_name_plural': '板块',
'db_table': 'bankuai',
},
),
migrations.CreateModel(
name='Chenghao',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('mingcheng', models.CharField(max_length=50, unique=True, verbose_name='称号名称')),
('leixing', models.CharField(choices=[('dashou', '打手'), ('boss', '老板'), ('shangjia', '商家'), ('guanshi', '管事'), ('zuzhang', '组长')], max_length=10, verbose_name='称号类型')),
('tubiao_url', models.CharField(blank=True, max_length=200, null=True, verbose_name='称号图标相对URL')),
('texiao_miaoshu', models.TextField(blank=True, null=True, verbose_name='特效描述(JSON)')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('bankuai', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dengji.bankuai', verbose_name='所属板块(空=全板块通用)')),
],
options={
'verbose_name': '称号',
'verbose_name_plural': '称号',
'db_table': 'chenghao',
},
),
migrations.CreateModel(
name='YonghuChenghao',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('huode_time', models.DateTimeField(auto_now_add=True, verbose_name='获得时间')),
('chenghao', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dengji.chenghao', verbose_name='称号')),
('yonghu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chenghao_list', to=settings.AUTH_USER_MODEL, verbose_name='用户')),
],
options={
'verbose_name': '用户称号关联',
'verbose_name_plural': '用户称号关联',
'db_table': 'yonghu_chenghao',
'indexes': [models.Index(fields=['yonghu', 'chenghao'], name='yonghu_chen_yonghu__cac0a1_idx')],
'unique_together': {('yonghu', 'chenghao')},
},
),
migrations.CreateModel(
name='JingyanJilu',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('yonghu_id', models.CharField(db_index=True, max_length=7, verbose_name='用户ID')),
('yonghu_leixing', models.CharField(choices=[('dashou', '打手'), ('boss', '老板'), ('shangjia', '商家'), ('guanshi', '管事'), ('zuzhang', '组长')], max_length=10, verbose_name='用户类型')),
('sudu_jingyan', models.DecimalField(decimal_places=2, default=0, max_digits=8, verbose_name='上线速度经验')),
('shili_jingyan', models.DecimalField(decimal_places=2, default=0, max_digits=8, verbose_name='打手实力经验')),
('taidu_jingyan', models.DecimalField(decimal_places=2, default=0, max_digits=8, verbose_name='服务态度经验')),
('zonghe_jingyan', models.DecimalField(decimal_places=2, default=0, max_digits=8, verbose_name='综合经验')),
('dingdan_id', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='关联订单ID')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('bankuai', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dengji.bankuai', verbose_name='所属板块')),
],
options={
'verbose_name': '经验变动记录',
'verbose_name_plural': '经验变动记录',
'db_table': 'jingyan_jilu',
'indexes': [models.Index(fields=['yonghu_id', 'bankuai', 'create_time'], name='jingyan_jil_yonghu__786992_idx')],
},
),
migrations.CreateModel(
name='DashouBiaoxian',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('dashou_id', models.CharField(db_index=True, max_length=32, verbose_name='打手ID')),
('shangxian_sudu', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='上线速度评价(1-5)')),
('fuwu_taidu', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='服务态度评价(1-5)')),
('fanche_cishu', models.PositiveIntegerField(blank=True, null=True, verbose_name='翻车次数')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('jiedan_time', models.DateTimeField(blank=True, null=True, verbose_name='接单时间')),
('tijiao_time', models.DateTimeField(blank=True, null=True, verbose_name='提交时间')),
('bankuai', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dengji.bankuai', verbose_name='所属板块')),
('dingdan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='biaoxian', to='dingdan.dingdan', verbose_name='关联订单')),
],
options={
'verbose_name': '打手订单表现',
'verbose_name_plural': '打手订单表现',
'db_table': 'dashou_biaoxian',
'indexes': [models.Index(fields=['dashou_id', 'bankuai', 'create_time'], name='dashou_biao_dashou__36e1c3_idx')],
},
),
migrations.AddIndex(
model_name='chenghao',
index=models.Index(fields=['leixing', 'bankuai'], name='chenghao_leixing_8f4371_idx'),
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 4.2.27 on 2026-05-12 02:25
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('dingdan', '0026_fadanfenhonglilv_alter_fadan_shenqingren_shenfen_and_more'),
('dengji', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='dashoubiaoxian',
name='dingdan',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='biaoxian', to='dingdan.dingdan', to_field='dingdan_id', verbose_name='关联订单'),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 4.2.27 on 2026-05-12 02:32
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('dengji', '0002_alter_dashoubiaoxian_dingdan'),
]
operations = [
migrations.AlterField(
model_name='dashoubiaoxian',
name='bankuai',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='dengji.bankuai', verbose_name='所属板块'),
),
]

View File

@@ -0,0 +1,26 @@
# dengji/migrations/0004_dingdanbiaoqian.py
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('dingdan', '0026_fadanfenhonglilv_alter_fadan_shenqingren_shenfen_and_more'), # 保持你之前的依赖
('dengji', '0003_alter_dashoubiaoxian_bankuai'),
]
operations = [
migrations.RunSQL(
sql="""
CREATE TABLE IF NOT EXISTS dingdan_biaoqian (
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
create_time DATETIME(6) NOT NULL,
chenghao_id BIGINT NOT NULL,
dingdan_id VARCHAR(32) NOT NULL,
CONSTRAINT fk_dingdanbiaoqian_chenghao FOREIGN KEY (chenghao_id) REFERENCES chenghao (id),
CONSTRAINT fk_dingdanbiaoqian_dingdan FOREIGN KEY (dingdan_id) REFERENCES dingdan (dingdan_id),
UNIQUE KEY uniq_dingdan_chenghao (dingdan_id, chenghao_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
""",
reverse_sql="DROP TABLE IF EXISTS dingdan_biaoqian;"
),
]

View File

@@ -0,0 +1,36 @@
# dengji/migrations/0005_fix_state.py
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('dingdan', '0026_fadanfenhonglilv_alter_fadan_shenqingren_shenfen_and_more'), # 保持你之前的依赖
('dengji', '0004_dingdanbiaoqian'),
]
operations = [
migrations.SeparateDatabaseAndState(
database_operations=[], # 表已存在,不做任何数据库操作
state_operations=[
migrations.CreateModel(
name='DingdanBiaoqian',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('chenghao', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dengji.chenghao', verbose_name='标签')),
('dingdan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dingdan.dingdan', to_field='dingdan_id', verbose_name='订单')),
],
options={
'verbose_name': '订单需求标签',
'verbose_name_plural': '订单需求标签',
'db_table': 'dingdan_biaoqian',
'unique_together': {('dingdan', 'chenghao')},
'indexes': [
models.Index(fields=['dingdan'], name='dingdan_bia_dingdan_4d046b_idx'),
],
},
),
],
),
]

View File

@@ -0,0 +1,35 @@
# Generated by Django 4.2.27 on 2026-05-14 18:42
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('dengji', '0005_fix_state'),
]
operations = [
migrations.CreateModel(
name='ShenheJilu',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('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, '未通过')], default=0, verbose_name='审核状态')),
('cishu', models.IntegerField(default=1, verbose_name='第几次审核')),
('jiaofei_jine', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='缴纳费用')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('bankuai', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dengji.bankuai', verbose_name='审核板块')),
('chenghao', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dengji.chenghao', verbose_name='审核标签')),
],
options={
'verbose_name': '审核记录',
'verbose_name_plural': '审核记录',
'db_table': 'shenhe_jilu',
'indexes': [models.Index(fields=['shenqingren_id', 'zhuangtai'], name='shenhe_jilu_shenqin_502e9b_idx'), models.Index(fields=['shenheguan_id'], name='shenhe_jilu_shenheg_5c9959_idx')],
},
),
]

View File

@@ -0,0 +1,29 @@
# Generated by Django 4.2.27 on 2026-05-15 00:31
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('dengji', '0006_shenhejilu'),
]
operations = [
migrations.CreateModel(
name='KaoheguanBankuai',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('kaoheguan_id', models.CharField(db_index=True, max_length=7, verbose_name='考核官用户ID')),
('bankuai', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dengji.bankuai', verbose_name='所属板块')),
],
options={
'verbose_name': '考核官板块关联',
'verbose_name_plural': '考核官板块关联',
'db_table': 'kaoheguan_bankuai',
'indexes': [models.Index(fields=['kaoheguan_id'], name='kaoheguan_b_kaohegu_00f7c5_idx')],
'unique_together': {('kaoheguan_id', 'bankuai')},
},
),
]

View File

@@ -0,0 +1,48 @@
# Generated by Django 4.2.27 on 2026-05-15 04:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dengji', '0007_kaoheguanbankuai'),
]
operations = [
migrations.AddField(
model_name='chenghao',
name='kaohe_guize',
field=models.TextField(blank=True, null=True, verbose_name='考核规则'),
),
migrations.AddField(
model_name='shenhejilu',
name='dashou_beizhu',
field=models.TextField(blank=True, null=True, verbose_name='打手备注'),
),
migrations.AddField(
model_name='shenhejilu',
name='dashou_youxi_id',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='打手游戏ID'),
),
migrations.AddField(
model_name='shenhejilu',
name='guize_neirong',
field=models.TextField(blank=True, null=True, verbose_name='规则内容'),
),
migrations.AddField(
model_name='shenhejilu',
name='jilu_id',
field=models.CharField(db_index=True, max_length=32, null=True, verbose_name='记录ID'),
),
migrations.AddField(
model_name='shenhejilu',
name='shenheguan_youxi_id',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='审核官游戏ID'),
),
migrations.AlterField(
model_name='shenhejilu',
name='zhuangtai',
field=models.IntegerField(choices=[(0, '审核中'), (1, '通过'), (2, '未通过'), (3, '待审核')], default=0, verbose_name='审核状态'),
),
]

View File

@@ -0,0 +1,58 @@
# Generated by Django 4.2.27 on 2026-05-15 13:20
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('dengji', '0008_chenghao_kaohe_guize_shenhejilu_dashou_beizhu_and_more'),
]
operations = [
migrations.AddField(
model_name='chenghao',
name='kaioi_jinpai',
field=models.BooleanField(default=False, verbose_name='是否同时开启金牌'),
),
migrations.AlterField(
model_name='shenhejilu',
name='jilu_id',
field=models.CharField(db_index=True, default='Sh111', max_length=32, unique=True, verbose_name='记录ID'),
preserve_default=False,
),
migrations.CreateModel(
name='KaoheJujueJilu',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('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='创建时间')),
('jilu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dengji.shenhejilu', to_field='jilu_id', verbose_name='审核记录')),
],
options={
'verbose_name': '拒绝原因记录',
'verbose_name_plural': '拒绝原因记录',
'db_table': 'kaohe_jujue_jilu',
'indexes': [models.Index(fields=['jilu_id', 'cishu'], name='kaohe_jujue_jilu_id_1f2b29_idx')],
},
),
migrations.CreateModel(
name='KaoheCishuFeiyong',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('cishu', models.IntegerField(verbose_name='第几次考核')),
('feiyong', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='需缴纳金额')),
('chenghao', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dengji.chenghao', verbose_name='标签')),
],
options={
'verbose_name': '考核次数费用',
'verbose_name_plural': '考核次数费用',
'db_table': 'kaohe_cishu_feiyong',
'indexes': [models.Index(fields=['chenghao', 'cishu'], name='kaohe_cishu_chengha_edc272_idx')],
'unique_together': {('chenghao', 'cishu')},
},
),
]

View File

@@ -0,0 +1,29 @@
# Generated by Django 4.2.27 on 2026-05-15 16:10
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('dengji', '0009_chenghao_kaioi_jinpai_alter_shenhejilu_jilu_id_and_more'),
]
operations = [
migrations.CreateModel(
name='KaoheJiluFeiyong',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('cishu', models.IntegerField(verbose_name='第几次考核')),
('jine', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='缴纳金额')),
('jilu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dengji.shenhejilu', to_field='jilu_id', verbose_name='考核记录')),
],
options={
'verbose_name': '考核记录费用',
'verbose_name_plural': '考核记录费用',
'db_table': 'kaohe_jilu_feiyong',
'unique_together': {('jilu', 'cishu')},
},
),
]

View File

@@ -0,0 +1,35 @@
# Generated by Django 4.2.27 on 2026-05-19 13:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dengji', '0010_kaohejilufeiyong'),
]
operations = [
migrations.CreateModel(
name='KaohePayTemp',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('dingdan_id', models.CharField(db_index=True, max_length=50, unique=True, verbose_name='订单ID')),
('yonghuid', models.CharField(db_index=True, max_length=7, 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(blank=True, max_length=32, null=True, verbose_name='原记录ID(再次申请时)')),
('shenheguan_id', models.CharField(blank=True, max_length=7, verbose_name='指定考核官ID')),
('cishu', models.IntegerField(verbose_name='本次考核次数(预计算)')),
('fee', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='本次费用(分账单位)')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
],
options={
'verbose_name': '考核支付临时表',
'verbose_name_plural': '考核支付临时表',
'db_table': 'kaohe_pay_temp',
},
),
]

View File

224
rank/models.py Normal file
View File

@@ -0,0 +1,224 @@
from django.db import models
from django.conf import settings
from orders.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 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 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 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'])]

87
rank/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
rank/tests.py Normal file
View File

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

22
rank/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='kaoheguan_register'),
path('khgsx', KaoheguanRefreshView.as_view(), name='kaoheguan_refresh'),
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='考核官获取考核记录'),
]

410
rank/utils.py Normal file
View File

@@ -0,0 +1,410 @@
# 标准库
import time
import random
import logging
from decimal import Decimal
# Django 内置
from django.db import models
from django.db.models import Q, F, Max
# 项目模型
## 当前dengji应用
from .models import (
Chenghao, KaoheCishuFeiyong, ShenheJilu, KaoheJiluFeiyong,
YonghuChenghao, KaoheguanBankuai, DingdanBiaoqian
)
## users应用
from users.models import UserMain, UserShenheguan, UserDashou
## orders应用
from orders.models import YonghuChenghao
# 日志对象
logger = logging.getLogger(__name__)
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:
# 标签校验失败不影响主流程,记录日志并保守放行(也可保守拒绝,按需求选择)
logger.error(f"标签校验异常: {e}", exc_info=True)
# 安全起见,如果校验出错,选择拒绝,避免越权
return False, '订单标签校验异常,请稍后重试'
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)
"""
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

947
rank/views.py Normal file
View File

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