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

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

No files matched your search

View File
Whitespace-only changes.
+3
View File
@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.
+5
View File
@@ -0,0 +1,5 @@
from django.apps import AppConfig
class YonghuConfig(AppConfig):
name = "yonghu"
+168
View File
@@ -0,0 +1,168 @@
# yonghu/fadan_fenhong_utils.py
# 罚款缴纳分红处理公共方法(支持更新罚单中的申请人分红金额)
import decimal
import logging
from decimal import Decimal
from django.db import transaction
from django.core.exceptions import ObjectDoesNotExist
from orders.models import Fadan, FadanFenhong, FadanFenhongLilv
from users.models import UserShangjia
logger = logging.getLogger(__name__)
# 身份映射字典
IDENTITY_MAP = {
1: {'fenhong': 2, 'lilv': 1}, # 客服
2: {'fenhong': 5, 'lilv': 2}, # 售后
3: {'fenhong': 3, 'lilv': 3}, # 管理员
4: {'fenhong': 4, 'lilv': 4}, # 店铺
5: {'fenhong': 1, 'lilv': 5}, # 商家
}
def process_fadan_fenhong(fadan_id):
"""
罚款缴纳后的分红处理(在回调中调用)
参数:
fadan_id: 罚单 ID
返回:
(success, message)
"""
try:
# 1. 获取罚单对象(必须是已缴纳状态)
try:
fadan = Fadan.objects.get(id=fadan_id, zhuangtai=2)
except Fadan.DoesNotExist:
logger.error(f"罚单 {fadan_id} 不存在或状态不是已缴纳")
return False, "罚单不存在或状态异常"
# 2. 获取申请处罚者ID和身份(源身份数字)
shenqing_chufa = fadan.shenqing_chufa
src_shenfen = fadan.shenqingren_shenfen
if not shenqing_chufa:
logger.info(f"罚单 {fadan_id} 无申请处罚者,不进行分红")
# 即使无申请者,也可将分红金额字段设为0(默认已是0)
return True, "无申请处罚者,无需分红"
# 3. 映射身份数字
if src_shenfen not in IDENTITY_MAP:
logger.error(f"未知的申请处罚者身份: {src_shenfen},罚单 {fadan_id}")
return False, f"未知身份 {src_shenfen}"
mapped = IDENTITY_MAP[src_shenfen]
fenhong_shenfen = mapped['fenhong'] # 用于 FadanFenhong.shenfen
lilv_shenfen = mapped['lilv'] # 用于 FadanFenhongLilv.shenfen
# 4. 获取罚款金额
fakuan_jine = fadan.fakuanjine
if fakuan_jine <= 0:
logger.info(f"罚单 {fadan_id} 罚款金额为0,不产生分红")
fadan.shenqingren_fenhong_jine = Decimal('0.00')
fadan.save(update_fields=['shenqingren_fenhong_jine'])
return True, "罚款金额为0,无分红"
# 5. 确定分红利率
rate = _get_fenhong_rate(shenqing_chufa, fenhong_shenfen, lilv_shenfen)
if rate is None:
logger.warning(f"未找到身份 {src_shenfen} (映射后 fenhong_shenfen={fenhong_shenfen}, lilv_shenfen={lilv_shenfen}) 的分红利率,罚单 {fadan_id} 不进行分红")
fadan.shenqingren_fenhong_jine = Decimal('0.00')
fadan.save(update_fields=['shenqingren_fenhong_jine'])
return True, "未配置分红利率,无分红"
# 6. 计算分红金额
fenhong_jine = (Decimal(str(fakuan_jine)) * Decimal(str(rate))).quantize(Decimal('0.01'))
if fenhong_jine <= 0:
logger.info(f"罚单 {fadan_id} 计算分红金额为0,无需处理")
fadan.shenqingren_fenhong_jine = Decimal('0.00')
fadan.save(update_fields=['shenqingren_fenhong_jine'])
return True, "分红金额为0"
# 7. 根据申请处罚者身份执行不同的分红逻辑
# src_shenfen == 5 代表商家(源身份)
if src_shenfen == 5:
# 商家特殊处理:只记录总额,可提现分红永远为0,直接加到商家余额
with transaction.atomic():
fenhong_record, created = FadanFenhong.objects.get_or_create(
fenhongzhe_id=shenqing_chufa,
shenfen=fenhong_shenfen,
defaults={
'zonge': Decimal('0.00'),
'ketixian_fenhong': Decimal('0.00'),
'fenhong_cishu': 0,
'yingde_lilv': rate,
'is_individual_rate': False
}
)
fenhong_record.zonge += fenhong_jine
# 可提现分红不累加,保持0
fenhong_record.fenhong_cishu += 1
fenhong_record.save()
# 直接增加商家余额(UserShangjia.yue)
try:
shangjia = UserShangjia.objects.get(user__yonghuid=shenqing_chufa)
shangjia.yue += fenhong_jine
shangjia.save(update_fields=['yue'])
logger.info(f"商家 {shenqing_chufa} 余额增加 {fenhong_jine} 元")
except UserShangjia.DoesNotExist:
logger.error(f"商家 {shenqing_chufa} 扩展表不存在,无法增加余额")
raise Exception(f"商家 {shenqing_chufa} 扩展表不存在,无法增加余额")
# 更新罚单中的申请人分红金额
fadan.shenqingren_fenhong_jine = fenhong_jine
fadan.save(update_fields=['shenqingren_fenhong_jine'])
else:
# 其他身份(客服、售后、管理员、店铺):同时增加总额和可提现分红
with transaction.atomic():
fenhong_record, created = FadanFenhong.objects.get_or_create(
fenhongzhe_id=shenqing_chufa,
shenfen=fenhong_shenfen,
defaults={
'zonge': Decimal('0.00'),
'ketixian_fenhong': Decimal('0.00'),
'fenhong_cishu': 0,
'yingde_lilv': rate,
'is_individual_rate': False
}
)
fenhong_record.zonge += fenhong_jine
fenhong_record.ketixian_fenhong += fenhong_jine
fenhong_record.fenhong_cishu += 1
fenhong_record.save()
# 更新罚单中的申请人分红金额
fadan.shenqingren_fenhong_jine = fenhong_jine
fadan.save(update_fields=['shenqingren_fenhong_jine'])
logger.info(f"罚单 {fadan_id} 分红处理成功: 申请者 {shenqing_chufa}, 分红金额 {fenhong_jine} 元, 源身份 {src_shenfen}, 映射后分红身份 {fenhong_shenfen}")
return True, "分红处理成功"
except Exception as e:
logger.error(f"罚单 {fadan_id} 分红处理异常: {str(e)}", exc_info=True)
return False, f"分红处理异常: {str(e)}"
def _get_fenhong_rate(fenhongzhe_id, fenhong_shenfen, lilv_shenfen):
"""
获取分红利率(优先使用个人单独配置,否则使用全局身份配置)
"""
try:
individual = FadanFenhong.objects.get(
fenhongzhe_id=fenhongzhe_id,
shenfen=fenhong_shenfen,
is_individual_rate=True
)
return individual.yingde_lilv
except FadanFenhong.DoesNotExist:
pass
try:
global_rate = FadanFenhongLilv.objects.get(shenfen=lilv_shenfen)
return global_rate.lilv
except FadanFenhongLilv.DoesNotExist:
return None
+213
View File
@@ -0,0 +1,213 @@
# Generated by Django 5.2.10 on 2026-01-14 19:59
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Xiugaijilu',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='记录ID')),
('yonghuid', models.CharField(max_length=7, verbose_name='被修改用户ID')),
('xiugaiid', models.CharField(max_length=30, verbose_name='修改人账号ID')),
('leixing', models.IntegerField(verbose_name='被修改用户类型')),
('xiugaitijiao', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改后的打手提现金额')),
('xiugaitijiaoq', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改前的打手提现金额')),
('jifen', models.IntegerField(default=0, verbose_name='修改后的打手积分')),
('yjifen', models.IntegerField(default=0, verbose_name='修改前的打手积分')),
('yajin', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改后的打手押金')),
('yyajin', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改前的打手押金')),
('shangjiayue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改后的商家余额')),
('shangjiayueq', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改前的商家余额')),
('guanshiyue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改后的管事余额')),
('guanshiyueq', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改前的管事余额')),
('huiyuants', models.IntegerField(default=0, verbose_name='添加会员天数')),
('huiyuan_id', models.CharField(blank=True, max_length=6, null=True, verbose_name='会员ID')),
('qitashuoming', models.TextField(blank=True, null=True, verbose_name='其他说明')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '修改记录表',
'verbose_name_plural': '修改记录表',
'db_table': 'xiugaijilu',
},
),
migrations.CreateModel(
name='UserMain',
fields=[
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='主键ID')),
('yonghuid', models.CharField(db_index=True, max_length=7, unique=True, verbose_name='用户ID')),
('openid', models.CharField(db_index=True, max_length=64, unique=True, verbose_name='微信OpenID')),
('avatar', models.CharField(blank=True, max_length=500, null=True, verbose_name='头像')),
('phone', models.CharField(blank=True, max_length=11, null=True, verbose_name='手机号')),
('password', models.CharField(blank=True, max_length=200, null=True, verbose_name='密码')),
('zhifu', models.CharField(blank=True, max_length=500, null=True, verbose_name='收款码图片')),
('skzhanghao', models.CharField(blank=True, max_length=11, null=True, verbose_name='收款账号')),
('ip', models.CharField(blank=True, max_length=50, null=True, verbose_name='来访用户ip')),
('last_login_time', models.DateTimeField(blank=True, null=True, verbose_name='最后登录时间')),
('user_type', models.CharField(choices=[('normal', '普通用户'), ('dashou', '打手'), ('shop', '商家'), ('guanshi', '管事'), ('admin', '管理员')], default='normal', max_length=10, verbose_name='用户类型')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '用户主表',
'verbose_name_plural': '用户主表',
'db_table': 'user_main',
'indexes': [models.Index(fields=['yonghuid'], name='user_main_yonghui_b8b2fe_idx'), models.Index(fields=['openid'], name='user_main_openid_c9edbe_idx'), models.Index(fields=['user_type'], name='user_main_user_ty_8515fd_idx')],
},
),
migrations.CreateModel(
name='Tixianjilu',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='提现记录ID')),
('yonghuid', models.CharField(db_index=True, max_length=7, verbose_name='提现用户ID')),
('avatar', models.CharField(blank=True, max_length=500, null=True, verbose_name='头像')),
('phone', models.CharField(blank=True, max_length=11, null=True, verbose_name='手机号')),
('nicheng', models.CharField(max_length=50, null=True, verbose_name='打手昵称')),
('leixing', models.PositiveIntegerField(blank=True, null=True, verbose_name='提现类型1:打手佣金2:管事分红')),
('zhifu', models.CharField(blank=True, max_length=500, null=True, verbose_name='收款码图片')),
('skzhanghao', models.CharField(blank=True, max_length=11, null=True, verbose_name='收款账号')),
('jine', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='提现金额')),
('zhuangtai', models.IntegerField(blank=True, db_index=True, default=1, null=True, verbose_name='提现状态1:审核2:成功3:失败')),
('fangshi', models.IntegerField(blank=True, db_index=True, null=True, verbose_name='收款方式1:微信2:支付宝')),
('shenheid', models.CharField(db_index=True, max_length=7, null=True, verbose_name='审核用户ID')),
('bhliyou', models.TextField(blank=True, null=True, verbose_name='驳回理由')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '提现记录表',
'verbose_name_plural': '提现记录表',
'db_table': 'tixianjilu',
'indexes': [models.Index(fields=['yonghuid'], name='tixianjilu_yonghui_8083bb_idx'), models.Index(fields=['zhuangtai'], name='tixianjilu_zhuangt_839d8e_idx'), models.Index(fields=['shenheid'], name='tixianjilu_shenhei_470e0c_idx'), models.Index(fields=['fangshi'], name='tixianjilu_fangshi_a1e0bc_idx')],
},
),
migrations.CreateModel(
name='AdminProfile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=255, verbose_name='密码')),
('wechat', models.CharField(blank=True, max_length=50, verbose_name='管理员微信')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='admin_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')),
],
options={
'verbose_name': '管理员扩展表',
'verbose_name_plural': '管理员扩展表',
'db_table': 'admin_profile',
'indexes': [models.Index(fields=['user'], name='admin_profi_user_id_7d067d_idx')],
},
),
migrations.CreateModel(
name='UserBoss',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nickname', models.CharField(max_length=50, verbose_name='老板昵称')),
('zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='消费总额')),
('alldingdan', models.IntegerField(default=0, verbose_name='订单总数')),
('alltui', models.IntegerField(default=0, verbose_name='退款总数')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='boss_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')),
],
options={
'verbose_name': '老板扩展表',
'verbose_name_plural': '老板扩展表',
'db_table': 'user_boss',
'indexes': [models.Index(fields=['user'], name='user_boss_user_id_4e81a4_idx')],
},
),
migrations.CreateModel(
name='UserDashou',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nicheng', models.CharField(max_length=50, null=True, verbose_name='打手昵称')),
('chenghao', models.CharField(max_length=50, null=True, verbose_name='打手称号')),
('zhuangtai', models.IntegerField(default=1, verbose_name='打手状态')),
('zaixianzhuangtai', models.IntegerField(default=1, verbose_name='打手在线状态')),
('zhanghaozhuangtai', models.IntegerField(default=1, verbose_name='打手账号状态')),
('jieshao', models.TextField(null=True, verbose_name='打手介绍')),
('jiedanzongliang', models.IntegerField(default=0, verbose_name='接单总量')),
('chengjiaozongliang', models.IntegerField(default=0, verbose_name='成交单总量')),
('tuikuanliang', models.IntegerField(default=0, verbose_name='退款订单总量')),
('yue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='可提现余额')),
('zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='打单赚取总额')),
('dianhua', models.CharField(max_length=20, null=True, verbose_name='打手电话')),
('wechat', models.CharField(max_length=50, null=True, verbose_name='打手微信号')),
('yaoqingren', models.CharField(db_index=True, max_length=7, null=True, verbose_name='邀请人ID')),
('jinrijiedan', models.IntegerField(default=0, verbose_name='今日接单数量')),
('jinrishouyi', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今日收益金额')),
('jinyuejiedan', models.IntegerField(default=0, verbose_name='今月接单数量')),
('jinyueshouyi', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今月收益金额')),
('jifen', models.IntegerField(default=0, verbose_name='打手积分')),
('yajin', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='打手押金')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='dashou_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')),
],
options={
'verbose_name': '打手扩展表',
'verbose_name_plural': '打手扩展表',
'db_table': 'user_dashou',
'indexes': [models.Index(fields=['user'], name='user_dashou_user_id_cf0410_idx'), models.Index(fields=['yaoqingren'], name='user_dashou_yaoqing_894658_idx')],
},
),
migrations.CreateModel(
name='UserGuanshi',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('yaoqingma', models.CharField(db_index=True, max_length=20, null=True, verbose_name='邀请码')),
('dianhua', models.CharField(max_length=20, null=True, verbose_name='管事电话')),
('wechat', models.CharField(max_length=50, null=True, verbose_name='管事微信号')),
('yaogingshuliang', models.IntegerField(default=0, verbose_name='邀请打手总数')),
('zhuangtai', models.IntegerField(choices=[(0, '禁用'), (1, '正常')], default=1, verbose_name='管事状态')),
('jinrichongzhi', models.IntegerField(default=0, verbose_name='今日充值打手数量')),
('jinyuechongzhi', models.IntegerField(default=0, verbose_name='今月充值打手数量')),
('chongzhifenrun', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='充值分佣总额')),
('yue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='可提现余额')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='guanshi_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')),
],
options={
'verbose_name': '管事扩展表',
'verbose_name_plural': '管事扩展表',
'db_table': 'user_guanshi',
'indexes': [models.Index(fields=['user'], name='user_guansh_user_id_cbadac_idx'), models.Index(fields=['yaoqingma'], name='user_guansh_yaoqing_21d1d4_idx')],
},
),
migrations.CreateModel(
name='UserShangjia',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nicheng', models.CharField(db_index=True, max_length=50, unique=True, verbose_name='商家昵称')),
('zhuangtai', models.IntegerField(choices=[(0, '待审核'), (1, '正常'), (2, '禁用')], default=1, verbose_name='商家状态')),
('dianhua', models.CharField(max_length=20, null=True, verbose_name='商家电话')),
('wechat', models.CharField(max_length=50, null=True, verbose_name='商家微信号')),
('fabu', models.IntegerField(default=0, verbose_name='发布订单总数量')),
('tuikuan', models.IntegerField(default=0, verbose_name='退款订单总量')),
('yue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='商家余额')),
('chengjiao', models.IntegerField(default=0, verbose_name='商家成交订单总量')),
('jinridingdan', models.IntegerField(default=0, verbose_name='商家今日订单总量')),
('jinriliushui', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='商家今日流水')),
('jinyuedingdan', models.IntegerField(default=0, verbose_name='商家今月订单总量')),
('jinyueliushui', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='商家今月流水')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='shop_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')),
],
options={
'verbose_name': '商家扩展表',
'verbose_name_plural': '商家扩展表',
'db_table': 'user_shangjia',
'indexes': [models.Index(fields=['user'], name='user_shangj_user_id_823c5c_idx'), models.Index(fields=['nicheng'], name='user_shangj_nicheng_6967bc_idx')],
},
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.27 on 2026-01-21 12:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('yonghu', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='adminprofile',
name='yaoqingma',
field=models.CharField(db_index=True, max_length=20, null=True, verbose_name='管理员邀请码'),
),
]
+35
View File
@@ -0,0 +1,35 @@
# Generated by Django 4.2.27 on 2026-01-24 23:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('yonghu', '0002_adminprofile_yaoqingma'),
]
operations = [
migrations.CreateModel(
name='RankingRecord',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID')),
('zhouqi', models.IntegerField(choices=[(1, '日榜'), (2, '月榜')], verbose_name='周期类型')),
('tongji_date', models.DateField(db_index=True, verbose_name='统计日期')),
('shenfen', models.IntegerField(choices=[(2, '打手'), (3, '管事'), (4, '商家')], verbose_name='用户身份')),
('yonghuid', models.CharField(db_index=True, max_length=7, verbose_name='用户ID')),
('paiming', models.IntegerField(verbose_name='排名')),
('zhizhi', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='指标值')),
('nicheng', models.CharField(max_length=50, null=True, verbose_name='用户昵称')),
('avatar', models.CharField(blank=True, max_length=500, null=True, verbose_name='头像')),
('chuangjian_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
],
options={
'verbose_name': '排行榜记录',
'verbose_name_plural': '排行榜记录',
'db_table': 'ranking_record',
'indexes': [models.Index(fields=['zhouqi', 'tongji_date', 'shenfen', 'paiming'], name='ranking_rec_zhouqi_fc9f4d_idx'), models.Index(fields=['yonghuid', 'zhouqi'], name='ranking_rec_yonghui_e5be77_idx'), models.Index(fields=['tongji_date'], name='ranking_rec_tongji__820158_idx')],
'unique_together': {('zhouqi', 'tongji_date', 'shenfen', 'paiming')},
},
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.27 on 2026-01-24 23:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('yonghu', '0003_rankingrecord'),
]
operations = [
migrations.AlterField(
model_name='usermain',
name='avatar',
field=models.CharField(blank=True, default='a_long/morentouxiang.jpg', max_length=500, null=True, verbose_name='头像'),
),
]
@@ -0,0 +1,35 @@
# Generated by Django 4.2.27 on 2026-02-07 19:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('yonghu', '0004_alter_usermain_avatar'),
]
operations = [
migrations.CreateModel(
name='OfficialAccountUser',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('unionid', models.CharField(db_index=True, max_length=64, verbose_name='UnionID')),
('official_openid', models.CharField(db_index=True, max_length=64, unique=True, verbose_name='服务号OpenID')),
('subscribe_time', models.DateTimeField(auto_now_add=True, verbose_name='关注时间')),
('is_subscribed', models.BooleanField(default=True, verbose_name='是否关注')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '服务号用户',
'verbose_name_plural': '服务号用户',
'db_table': 'official_account_user',
},
),
migrations.AddField(
model_name='usermain',
name='unionid',
field=models.CharField(blank=True, db_index=True, max_length=64, null=True, verbose_name='微信UnionID'),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.27 on 2026-02-07 22:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('yonghu', '0005_officialaccountuser_usermain_unionid'),
]
operations = [
migrations.AlterField(
model_name='officialaccountuser',
name='unionid',
field=models.CharField(db_index=True, max_length=64, null=True, verbose_name='UnionID'),
),
]
+36
View File
@@ -0,0 +1,36 @@
# Generated by Django 4.2.27 on 2026-02-21 14:37
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('yonghu', '0006_alter_officialaccountuser_unionid'),
]
operations = [
migrations.CreateModel(
name='UserKefu',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nicheng', models.CharField(max_length=50, verbose_name='客服昵称')),
('erjimima', models.CharField(max_length=128, verbose_name='二级密码')),
('zhuangtai', models.IntegerField(choices=[(0, '禁用'), (1, '正常')], default=1, verbose_name='客服状态')),
('jinrichuli', models.IntegerField(default=0, verbose_name='今日处理订单数')),
('jinyuechuli', models.IntegerField(default=0, verbose_name='本月处理订单数')),
('zongchuli', models.IntegerField(default=0, verbose_name='累计处理订单数')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='kefu_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')),
],
options={
'verbose_name': '客服扩展表',
'verbose_name_plural': '客服扩展表',
'db_table': 'user_kefu',
'indexes': [models.Index(fields=['user'], name='user_kefu_user_id_5fe3a1_idx'), models.Index(fields=['zhuangtai'], name='user_kefu_zhuangt_317491_idx')],
},
),
]
@@ -0,0 +1,28 @@
# Generated by Django 4.2.27 on 2026-02-27 15:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('yonghu', '0007_userkefu'),
]
operations = [
migrations.AddField(
model_name='userguanshi',
name='fenghong_erci',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='二次分红金额'),
),
migrations.AddField(
model_name='userguanshi',
name='fenghong_erci_enabled',
field=models.BooleanField(default=False, verbose_name='开启二次分红'),
),
migrations.AddField(
model_name='userguanshi',
name='fenhong_yici',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='首次分红金额'),
),
]
@@ -0,0 +1,89 @@
# Generated by Django 4.2.27 on 2026-03-16 17:04
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('yonghu', '0008_userguanshi_fenghong_erci_and_more'),
]
operations = [
migrations.AddField(
model_name='userdashou',
name='ewai_xiane',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='额外提现限额(元)'),
),
migrations.AddField(
model_name='userdashou',
name='jinritixian_jine',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今日已提现金额'),
),
migrations.AddField(
model_name='userdashou',
name='kaioi_ewai_xiane',
field=models.BooleanField(default=False, verbose_name='是否开启额外提现限额'),
),
migrations.AddField(
model_name='userdashou',
name='last_tixian_date',
field=models.DateField(blank=True, null=True, verbose_name='最近提现日期'),
),
migrations.AddField(
model_name='userguanshi',
name='ewai_xiane',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='额外提现限额(元)'),
),
migrations.AddField(
model_name='userguanshi',
name='jinritixian_jine',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今日已提现金额'),
),
migrations.AddField(
model_name='userguanshi',
name='kaioi_ewai_xiane',
field=models.BooleanField(default=False, verbose_name='是否开启额外提现限额'),
),
migrations.AddField(
model_name='userguanshi',
name='last_tixian_date',
field=models.DateField(blank=True, null=True, verbose_name='最近提现日期'),
),
migrations.AddField(
model_name='userguanshi',
name='yaoqingren',
field=models.CharField(db_index=True, max_length=7, null=True, verbose_name='邀请人ID'),
),
migrations.CreateModel(
name='UserZuzhang',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('yaoqingma', models.CharField(db_index=True, max_length=20, null=True, verbose_name='组长邀请码')),
('fenyong_zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='分佣总额(元)')),
('ketixian_jine', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='可提现金额(元)')),
('yaoqing_zongshu', models.IntegerField(default=0, verbose_name='邀请总数')),
('jinri_fenyong', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今日分佣(元)')),
('jinyue_fenyong', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今月分佣(元)')),
('zhuangtai', models.IntegerField(choices=[(0, '禁用'), (1, '正常')], default=1, verbose_name='账号状态')),
('haibao_url', models.CharField(blank=True, max_length=500, null=True, verbose_name='推广海报相对URL')),
('jinri_tixian', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今日提现金额(元)')),
('last_tixian_time', models.DateTimeField(blank=True, null=True, verbose_name='最后提现时间')),
('ewai_tixian_xiane', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='额外提现限额(元)')),
('kaioi_ewai_tixian', models.BooleanField(default=False, verbose_name='开启额外提现')),
('kaioi_ewai_fenhong', models.BooleanField(default=False, verbose_name='开启额外分红')),
('ewai_fenhong_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='更新时间')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='zuzhang_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')),
],
options={
'verbose_name': '组长扩展表',
'verbose_name_plural': '组长扩展表',
'db_table': 'user_zuzhang',
'indexes': [models.Index(fields=['user'], name='user_zuzhan_user_id_8064f2_idx'), models.Index(fields=['yaoqingma'], name='user_zuzhan_yaoqing_cba91d_idx')],
},
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.27 on 2026-03-19 12:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('yonghu', '0009_userdashou_ewai_xiane_userdashou_jinritixian_jine_and_more'),
]
operations = [
migrations.AddField(
model_name='userguanshi',
name='invite_qrcode_url',
field=models.CharField(blank=True, max_length=500, null=True, verbose_name='邀请码二维码图片URL'),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.27 on 2026-05-10 02:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('yonghu', '0010_userguanshi_invite_qrcode_url'),
]
operations = [
migrations.AddField(
model_name='usermain',
name='shoujihao_renzheng',
field=models.BooleanField(default=False, verbose_name='手机号是否已认证'),
),
]
+36
View File
@@ -0,0 +1,36 @@
# Generated by Django 4.2.27 on 2026-05-14 18:42
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('yonghu', '0011_usermain_shoujihao_renzheng'),
]
operations = [
migrations.CreateModel(
name='UserShenheguan',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('zhuangtai', models.IntegerField(choices=[(0, '禁用'), (1, '正常')], default=1, verbose_name='账号状态')),
('shenhe_zhuangtai', models.IntegerField(choices=[(0, '空闲'), (1, '审核中')], default=0, verbose_name='审核官状态')),
('shenhe_zongshu', models.IntegerField(default=0, verbose_name='审核总数')),
('tongguo_zongshu', models.IntegerField(default=0, verbose_name='通过总数')),
('yue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='可提现余额')),
('zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='赚取总额')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='shenheguan_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')),
],
options={
'verbose_name': '审核官扩展表',
'verbose_name_plural': '审核官扩展表',
'db_table': 'user_shenheguan',
'indexes': [models.Index(fields=['user'], name='user_shenhe_user_id_624f19_idx')],
},
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.27 on 2026-05-18 02:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('yonghu', '0012_usershenheguan'),
]
operations = [
migrations.AddField(
model_name='usershenheguan',
name='is_renzheng',
field=models.BooleanField(default=False, verbose_name='是否认证'),
),
]
@@ -0,0 +1,113 @@
# Generated by Django 4.2.27 on 2026-06-08 23:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('yonghu', '0013_usershenheguan_is_renzheng'),
]
operations = [
migrations.CreateModel(
name='TixianAutoRecord',
fields=[
('id', models.BigAutoField(primary_key=True, serialize=False)),
('tixian_id', models.CharField(db_index=True, max_length=32, unique=True, verbose_name='提现订单号(自定义)')),
('yonghuid', models.CharField(db_index=True, max_length=7, verbose_name='用户ID')),
('leixing', models.IntegerField(verbose_name='提现类型 1打手 2管事')),
('jine', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='申请提现金额')),
('shouxufei', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='手续费')),
('shijidaozhang', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='实际到账金额')),
('feilv', models.DecimalField(decimal_places=4, max_digits=5, verbose_name='费率')),
('zhuangtai', models.IntegerField(choices=[(0, '处理中'), (1, '成功'), (2, '失败')], default=0, verbose_name='状态')),
('wechat_transfer_no', models.CharField(blank=True, max_length=64, null=True, verbose_name='微信转账单号')),
('wechat_package', models.TextField(blank=True, null=True, verbose_name='微信返回的package_info(JSON)')),
('fail_reason', models.TextField(blank=True, null=True, verbose_name='失败原因')),
('shenhe_danhao', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='关联审核单号')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '自动提现记录',
'verbose_name_plural': '自动提现记录',
'db_table': 'tixian_auto_record',
},
),
migrations.CreateModel(
name='TixianShenheJilu',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('shenhe_danhao', models.CharField(db_index=True, max_length=32, unique=True, verbose_name='审核单号')),
('yonghuid', models.CharField(db_index=True, max_length=7, verbose_name='用户ID')),
('leixing', models.PositiveSmallIntegerField(db_index=True, verbose_name='提现类型')),
('zhuangtai', models.IntegerField(db_index=True, default=1, verbose_name='审核状态(1审核中2提现成功3提现失败4审核成功5审核失败6待收款)')),
('shenqing_jine', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='申请提现金额(扣款额)')),
('shouxufei', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='手续费')),
('shijidaozhang', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='实际到账金额')),
('feilv', models.DecimalField(decimal_places=4, default=0, max_digits=5, verbose_name='手续费率')),
('bhliyou', models.TextField(blank=True, null=True, verbose_name='驳回/失败原因')),
('bo_hui_ren_id', models.CharField(blank=True, db_index=True, max_length=11, null=True, verbose_name='驳回人用户ID')),
('shenhe_ren_id', models.CharField(blank=True, db_index=True, max_length=11, null=True, verbose_name='审核通过操作人ID')),
('tixianjilu_id', models.IntegerField(blank=True, db_index=True, null=True, verbose_name='关联Tixianjilu.id')),
('tixian_auto_id', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='最近一次打款单号(非唯一,重试收款会更新)')),
('fail_reason', models.TextField(blank=True, null=True, verbose_name='微信打款失败原因')),
('dakuan_mode', models.PositiveSmallIntegerField(db_index=True, default=1, verbose_name='打款方式(1手动2自动)')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '提现审核记录',
'verbose_name_plural': '提现审核记录',
'db_table': 'tixian_shenhe_jilu',
},
),
migrations.AddField(
model_name='tixianjilu',
name='shenhe_danhao',
field=models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='关联提现审核单号'),
),
migrations.AddField(
model_name='tixianjilu',
name='shenhe_jilu_id',
field=models.IntegerField(blank=True, db_index=True, null=True, verbose_name='关联提现审核记录ID(TixianShenheJilu.id)'),
),
migrations.AlterField(
model_name='tixianjilu',
name='shenheid',
field=models.CharField(db_index=True, max_length=11, null=True, verbose_name='审核用户ID'),
),
migrations.AddIndex(
model_name='tixianjilu',
index=models.Index(fields=['shenhe_danhao'], name='tixianjilu_shenhe__7b3e7f_idx'),
),
migrations.AddIndex(
model_name='tixianjilu',
index=models.Index(fields=['shenhe_jilu_id'], name='tixianjilu_shenhe__d9355e_idx'),
),
migrations.AddIndex(
model_name='tixianshenhejilu',
index=models.Index(fields=['yonghuid', 'zhuangtai'], name='tixian_shen_yonghui_a782e9_idx'),
),
migrations.AddIndex(
model_name='tixianshenhejilu',
index=models.Index(fields=['yonghuid', 'leixing'], name='tixian_shen_yonghui_bb44eb_idx'),
),
migrations.AddIndex(
model_name='tixianshenhejilu',
index=models.Index(fields=['shenhe_danhao'], name='tixian_shen_shenhe__4a1ca7_idx'),
),
migrations.AddIndex(
model_name='tixianautorecord',
index=models.Index(fields=['tixian_id'], name='tixian_auto_tixian__273dac_idx'),
),
migrations.AddIndex(
model_name='tixianautorecord',
index=models.Index(fields=['yonghuid'], name='tixian_auto_yonghui_61c5b6_idx'),
),
migrations.AddIndex(
model_name='tixianautorecord',
index=models.Index(fields=['zhuangtai'], name='tixian_auto_zhuangt_24d9fa_idx'),
),
]
View File
Whitespace-only changes.
+600
View File
@@ -0,0 +1,600 @@
# models.py
from django.db import models
import random
import string
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
# ==================== 用户主表 ====================
class UserMain(AbstractBaseUser):
# ID字段
id = models.AutoField(primary_key=True, verbose_name='主键ID')
yonghuid = models.CharField(max_length=7, unique=True, db_index=True, verbose_name='用户ID')
# 微信相关字段
openid = models.CharField(max_length=64, unique=True, db_index=True, verbose_name='微信OpenID')
unionid = models.CharField(max_length=64, null=True, blank=True, db_index=True, verbose_name='微信UnionID')
# 基础信息字段
#tupian_url = models.CharField(max_length=500, null=True, blank=True, verbose_name='商品图片URL') # 用CharField
avatar = models.CharField(max_length=500, null=True, blank=True,default='a_long/morentouxiang.jpg', verbose_name='头像') # 用CharField
phone = models.CharField(max_length=11, blank=True, null=True, verbose_name='手机号')
shoujihao_renzheng = models.BooleanField(default=False, verbose_name='手机号是否已认证')
password = models.CharField(max_length=200, null=True, blank=True, verbose_name='密码')
zhifu = models.CharField(max_length=500, null=True, blank=True, verbose_name='收款码图片') # 用CharField
skzhanghao = models.CharField(max_length=11, blank=True, null=True, verbose_name='收款账号')
ip = models.CharField(max_length=50, blank=True, null=True, verbose_name='来访用户ip')
last_login_time = models.DateTimeField(null=True, blank=True, verbose_name='最后登录时间')
# 用户类型字段
user_type = models.CharField(
max_length=10,
choices=[
('normal', '普通用户'),
('dashou', '打手'),
('shop', '商家'),
('guanshi', '管事'),
('admin', '管理员')
],
default='normal',
verbose_name='用户类型'
)
# 状态和时间字段
#is_active = models.BooleanField(default=True, verbose_name='是否激活')
#last_login = models.DateTimeField(blank=True, null=True, verbose_name='最后登录时间')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
# 邀请码字段
#yaoqingma = models.CharField(max_length=20, blank=True, null=True, verbose_name='邀请码')
# 只需要添加这3行
USERNAME_FIELD = 'openid' # 用openid作为用户名
REQUIRED_FIELDS = ['yonghuid'] # 必填字段
class Meta:
db_table = 'user_main'
verbose_name = '用户主表'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['yonghuid']),
models.Index(fields=['openid']),
#models.Index(fields=['phone']),
models.Index(fields=['user_type']),
#models.Index(fields=['create_time']),
]
def __str__(self):
return f'{self.yonghuid}'
# 新增服务号用户表
class OfficialAccountUser(models.Model):
unionid = models.CharField(max_length=64, null =True,db_index=True, verbose_name='UnionID')
official_openid = models.CharField(max_length=64, db_index=True, unique=True, verbose_name='服务号OpenID')
subscribe_time = models.DateTimeField(auto_now_add=True, verbose_name='关注时间')
is_subscribed = models.BooleanField(default=True, verbose_name='是否关注')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'official_account_user'
verbose_name = '服务号用户'
verbose_name_plural = verbose_name
def __str__(self):
return f"{self.unionid[:8]}... (关注: {self.is_subscribed})"
# ==================== 老板扩展表 ====================
class UserBoss(models.Model):
user = models.OneToOneField(UserMain, on_delete=models.CASCADE, related_name='boss_profile',
verbose_name='关联用户')
nickname = models.CharField(max_length=50, verbose_name='老板昵称')
zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='消费总额')
alldingdan = models.IntegerField(default=0, verbose_name='订单总数')
alltui = models.IntegerField(default=0, verbose_name='退款总数')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
db_table = 'user_boss'
verbose_name = '老板扩展表'
verbose_name_plural = verbose_name
indexes = [models.Index(fields=['user']), ]
# ==================== 打手扩展表 ====================
class UserDashou(models.Model):
user = models.OneToOneField(UserMain, on_delete=models.CASCADE, related_name='dashou_profile',
verbose_name='关联用户')
nicheng = models.CharField(max_length=50, null=True, verbose_name='打手昵称')
chenghao = models.CharField(max_length=50, null=True,verbose_name='打手称号')
zhuangtai = models.IntegerField(default=1, verbose_name='打手状态')
zaixianzhuangtai = models.IntegerField(default=1, verbose_name='打手在线状态')
zhanghaozhuangtai = models.IntegerField(default=1, verbose_name='打手账号状态')
jieshao = models.TextField(null=True, verbose_name='打手介绍')
jiedanzongliang = models.IntegerField(default=0, verbose_name='接单总量')
chengjiaozongliang = models.IntegerField(default=0, verbose_name='成交单总量')
tuikuanliang = models.IntegerField(default=0, verbose_name='退款订单总量')
yue = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='可提现余额')
zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='打单赚取总额')
dianhua = models.CharField(max_length=20, null=True, verbose_name='打手电话')
wechat = models.CharField(max_length=50,null=True, verbose_name='打手微信号')
yaoqingren = models.CharField(max_length=7,null=True, db_index=True, verbose_name='邀请人ID')
jinrijiedan = models.IntegerField(default=0, verbose_name='今日接单数量')
jinrishouyi = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='今日收益金额')
jinyuejiedan = models.IntegerField(default=0, verbose_name='今月接单数量')
jinyueshouyi = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='今月收益金额')
jifen = models.IntegerField(default=0, verbose_name='打手积分')
yajin= models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='打手押金')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
last_tixian_date = models.DateField(null=True, blank=True, verbose_name='最近提现日期') # 用于重置今日提现金额
jinritixian_jine = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='今日已提现金额')
kaioi_ewai_xiane = models.BooleanField(default=False, verbose_name='是否开启额外提现限额') # True=开启额外限额
ewai_xiane = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='额外提现限额(元)')
class Meta:
db_table = 'user_dashou'
verbose_name = '打手扩展表'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['user']),
#models.Index(fields=['zhuangtai']),
models.Index(fields=['yaoqingren']),
#models.Index(fields=['nicheng']),
]
# ==================== 商家扩展表 ====================
class UserShangjia(models.Model):
user = models.OneToOneField(UserMain, on_delete=models.CASCADE, related_name='shop_profile',
verbose_name='关联用户')
nicheng = models.CharField(max_length=50, unique=True, db_index=True, verbose_name='商家昵称')
zhuangtai = models.IntegerField(choices=[(0, '待审核'), (1, '正常'), (2, '禁用')], default=1,
verbose_name='商家状态')
dianhua = models.CharField(max_length=20,null=True,verbose_name='商家电话')
wechat = models.CharField(max_length=50, null=True, verbose_name='商家微信号')
fabu = models.IntegerField(default=0, verbose_name='发布订单总数量')
tuikuan = models.IntegerField(default=0, verbose_name='退款订单总量')
yue = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='商家余额')
chengjiao = models.IntegerField(default=0, verbose_name='商家成交订单总量')
jinridingdan = models.IntegerField(default=0, verbose_name='商家今日订单总量')
jinriliushui = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='商家今日流水')
jinyuedingdan = models.IntegerField(default=0, verbose_name='商家今月订单总量')
jinyueliushui = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='商家今月流水')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
db_table = 'user_shangjia'
verbose_name = '商家扩展表'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['user']),
models.Index(fields=['nicheng']),
]
# ==================== 管事扩展表 ====================
class UserGuanshi(models.Model):
user = models.OneToOneField(UserMain, on_delete=models.CASCADE, related_name='guanshi_profile',
verbose_name='关联用户')
yaoqingren = models.CharField(max_length=7,null=True, db_index=True, verbose_name='邀请人ID')
yaoqingma = models.CharField(max_length=20,null=True,db_index=True, verbose_name='邀请码')
invite_qrcode_url = models.CharField(max_length=500, null=True, blank=True, verbose_name='邀请码二维码图片URL')
dianhua = models.CharField(max_length=20,null=True, verbose_name='管事电话')
wechat = models.CharField(max_length=50,null=True, verbose_name='管事微信号')
yaogingshuliang = models.IntegerField(default=0, verbose_name='邀请打手总数')
zhuangtai = models.IntegerField(choices=[(0, '禁用'), (1, '正常')], default=1, verbose_name='管事状态')
jinrichongzhi = models.IntegerField(default=0, verbose_name='今日充值打手数量')
jinyuechongzhi = models.IntegerField(default=0, verbose_name='今月充值打手数量')
chongzhifenrun = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='充值分佣总额')
yue = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='可提现余额')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
# 新增:首次分红金额(针对特殊用户)
fenhong_yici = models.DecimalField(max_digits=10,decimal_places=2,default=0.00,verbose_name='首次分红金额')
# 新增:二次分红开关
fenghong_erci_enabled = models.BooleanField(default=False,verbose_name='开启二次分红')
# 新增:二次分红金额
fenghong_erci = models.DecimalField(max_digits=10,decimal_places=2,default=0.00,verbose_name='二次分红金额')
last_tixian_date = models.DateField(null=True, blank=True, verbose_name='最近提现日期')
jinritixian_jine = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='今日已提现金额')
kaioi_ewai_xiane = models.BooleanField(default=False, verbose_name='是否开启额外提现限额')
ewai_xiane = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='额外提现限额(元)')
class Meta:
db_table = 'user_guanshi'
verbose_name = '管事扩展表'
verbose_name_plural = verbose_name
indexes = [models.Index(fields=['user']),
models.Index(fields=['yaoqingma']),
]
class UserZuzhang(models.Model):
"""组长扩展表(与UserMain一对一关联)"""
user = models.OneToOneField(
UserMain,
on_delete=models.CASCADE,
related_name='zuzhang_profile',
verbose_name='关联用户',
db_index=True
)
# 组长邀请码
yaoqingma = models.CharField(max_length=20, null=True, db_index=True, verbose_name='组长邀请码')
# 分佣总额
fenyong_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='分佣总额(元)')
# 可提现金额
ketixian_jine = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='可提现金额(元)')
# 邀请总数
yaoqing_zongshu = models.IntegerField(default=0, verbose_name='邀请总数')
# 今日分佣
jinri_fenyong = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='今日分佣(元)')
# 今月分佣
jinyue_fenyong = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='今月分佣(元)')
# 账号状态
zhuangtai = models.IntegerField(choices=[(0, '禁用'), (1, '正常')], default=1, verbose_name='账号状态')
# 推广海报相对URL
haibao_url = models.CharField(max_length=500, null=True, blank=True, verbose_name='推广海报相对URL')
# 今日提现
jinri_tixian = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='今日提现金额(元)')
# 最后提现时间
last_tixian_time = models.DateTimeField(null=True, blank=True, verbose_name='最后提现时间')
# 额外提现限额
ewai_tixian_xiane = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='额外提现限额(元)')
# 是否开启额外提现
kaioi_ewai_tixian = models.BooleanField(default=False, verbose_name='开启额外提现')
# 是否开启额外分红
kaioi_ewai_fenhong = models.BooleanField(default=False, verbose_name='开启额外分红')
# 额外分红金额
ewai_fenhong_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='更新时间')
class Meta:
db_table = 'user_zuzhang'
verbose_name = '组长扩展表'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['user']),
models.Index(fields=['yaoqingma']),
]
def __str__(self):
return f"组长 {self.user.yonghuid}"
class UserShenheguan(models.Model):
"""审核官扩展表(与UserMain一对一关联)"""
user = models.OneToOneField(UserMain, on_delete=models.CASCADE, related_name='shenheguan_profile', verbose_name='关联用户', db_index=True)
zhuangtai = models.IntegerField(choices=[(0, '禁用'), (1, '正常')], default=1, verbose_name='账号状态')
shenhe_zhuangtai = models.IntegerField(choices=[(0, '空闲'), (1, '审核中')], default=0, verbose_name='审核官状态')
is_renzheng = models.BooleanField(default=False, verbose_name='是否认证')
shenhe_zongshu = models.IntegerField(default=0, verbose_name='审核总数')
tongguo_zongshu = models.IntegerField(default=0, verbose_name='通过总数')
yue = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='可提现余额')
zonge = models.DecimalField(max_digits=12, 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='更新时间')
class Meta:
db_table = 'user_shenheguan'
verbose_name = '审核官扩展表'
verbose_name_plural = verbose_name
indexes = [models.Index(fields=['user'])]
# ==================== 管理员扩展表 ====================
class AdminProfile(models.Model):
user = models.OneToOneField(UserMain, on_delete=models.CASCADE, related_name='admin_profile',
verbose_name='关联用户')
password = models.CharField(max_length=255, verbose_name='密码')
wechat = models.CharField(max_length=50, blank=True, verbose_name='管理员微信')
yaoqingma = models.CharField(max_length=20, null=True, db_index=True,verbose_name='管理员邀请码') # 🟢 新增字段
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
db_table = 'admin_profile'
verbose_name = '管理员扩展表'
verbose_name_plural = verbose_name
indexes = [models.Index(fields=['user']), ]
class UserKefu(models.Model):
user = models.OneToOneField(UserMain, on_delete=models.CASCADE, related_name='kefu_profile', verbose_name='关联用户')
nicheng = models.CharField(max_length=50, verbose_name='客服昵称')
erjimima = models.CharField(max_length=128, verbose_name='二级密码')
zhuangtai = models.IntegerField(choices=[(0, '禁用'), (1, '正常')], default=1, verbose_name='客服状态')
jinrichuli = models.IntegerField(default=0, verbose_name='今日处理订单数')
jinyuechuli = models.IntegerField(default=0, verbose_name='本月处理订单数')
zongchuli = models.IntegerField(default=0, verbose_name='累计处理订单数')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'user_kefu'
verbose_name = '客服扩展表'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['user']),
models.Index(fields=['zhuangtai']),
]
def __str__(self):
return f"{self.nicheng} ({self.user.yonghuid})"
class Tixianjilu(models.Model):
id = models.AutoField(primary_key=True, verbose_name='提现记录ID')
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='提现用户ID')
avatar = models.CharField(max_length=500, null=True, blank=True, verbose_name='头像') # 用CharField
phone = models.CharField(max_length=11, blank=True, null=True, verbose_name='手机号')
nicheng = models.CharField(max_length=50, null=True, verbose_name='打手昵称')
leixing = models.PositiveIntegerField(null=True, blank=True, verbose_name='提现类型1:打手佣金2:管事分红')
zhifu = models.CharField(max_length=500, null=True, blank=True, verbose_name='收款码图片') # 用CharField
skzhanghao = models.CharField(max_length=11, blank=True, null=True, verbose_name='收款账号')
jine = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='提现金额')
# 状态与平台:用整数表示,具体含义由业务逻辑定义
zhuangtai = models.IntegerField(default=1, null=True, blank=True, db_index=True, verbose_name='提现状态1:审核2:成功3:失败')
fangshi = models.IntegerField(null=True, blank=True,db_index=True, verbose_name='收款方式1:微信2:支付宝')
shenheid = models.CharField(max_length=11, null=True, db_index=True, verbose_name='审核用户ID')
bhliyou = models.TextField(null=True, blank=True, verbose_name='驳回理由')
# ---------- 自动提现审核新流程关联字段(旧数据为空,兼容历史记录) ----------
shenhe_jilu_id = models.IntegerField(
null=True, blank=True, db_index=True,
verbose_name='关联提现审核记录ID(TixianShenheJilu.id)'
)
shenhe_danhao = models.CharField(
max_length=32, null=True, blank=True, db_index=True,
verbose_name='关联提现审核单号'
)
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'tixianjilu'
verbose_name = '提现记录表'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['yonghuid']),
models.Index(fields=['zhuangtai']),
models.Index(fields=['shenheid']),
models.Index(fields=['fangshi']),
models.Index(fields=['shenhe_danhao']),
models.Index(fields=['shenhe_jilu_id']),
]
class TixianShenheJilu(models.Model):
"""
提现审核记录表(自动打款新流程)
状态码与前端 TX_STATUS / Tixianjilu.zhuangtai 完全对标:
1=审核中 2=提现成功(已收款) 3=提现失败 4=审核成功 5=审核失败 6=待收款
"""
# 审核单号(唯一,前端字段 shenhe_danhao)
shenhe_danhao = models.CharField(
max_length=32, unique=True, db_index=True, verbose_name='审核单号'
)
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='用户ID')
# 提现类型:1打手佣金 2管事分红 3组长分红 4考核官分佣 5打手押金 6商家余额
leixing = models.PositiveSmallIntegerField(db_index=True, verbose_name='提现类型')
zhuangtai = models.IntegerField(
default=1, db_index=True,
verbose_name='审核状态(1审核中2提现成功3提现失败4审核成功5审核失败6待收款)'
)
# 金额:shenqing_jine=申请扣款金额;shijidaozhang=实际打款到账金额
shenqing_jine = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='申请提现金额(扣款额)')
shouxufei = models.DecimalField(max_digits=10, decimal_places=2, default=0, verbose_name='手续费')
shijidaozhang = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='实际到账金额')
feilv = models.DecimalField(max_digits=5, decimal_places=4, default=0, verbose_name='手续费率')
# 审核/驳回
bhliyou = models.TextField(null=True, blank=True, verbose_name='驳回/失败原因')
bo_hui_ren_id = models.CharField(
max_length=11, null=True, blank=True, db_index=True, verbose_name='驳回人用户ID'
)
shenhe_ren_id = models.CharField(
max_length=11, null=True, blank=True, db_index=True, verbose_name='审核通过操作人ID'
)
# 关联提现记录表主键
tixianjilu_id = models.IntegerField(null=True, blank=True, db_index=True, verbose_name='关联Tixianjilu.id')
# 最近一次打款单号(仅作记录,不唯一;同一审核单可多次收款产生多条打款记录)
tixian_auto_id = models.CharField(
max_length=32, null=True, blank=True, db_index=True,
verbose_name='最近一次打款单号(非唯一,重试收款会更新)'
)
fail_reason = models.TextField(null=True, blank=True, verbose_name='微信打款失败原因')
# 打款方式:1=手动打款 2=自动打款(微信商家转账)
dakuan_mode = models.PositiveSmallIntegerField(
default=1, db_index=True, verbose_name='打款方式(1手动2自动)'
)
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'tixian_shenhe_jilu'
verbose_name = '提现审核记录'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['yonghuid', 'zhuangtai']),
models.Index(fields=['yonghuid', 'leixing']),
models.Index(fields=['shenhe_danhao']),
]
def __str__(self):
return f'{self.shenhe_danhao}({self.yonghuid})'
class TixianAutoRecord(models.Model):
"""
自动提现记录表(微信自动打款)
"""
id = models.BigAutoField(primary_key=True)
tixian_id = models.CharField(
max_length=32,
unique=True,
db_index=True,
verbose_name='提现订单号(自定义)'
)
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='用户ID')
leixing = models.IntegerField(verbose_name='提现类型 1打手 2管事')
jine = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='申请提现金额')
shouxufei = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='手续费')
shijidaozhang = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='实际到账金额')
feilv = models.DecimalField(max_digits=5, decimal_places=4, verbose_name='费率')
zhuangtai = models.IntegerField(
default=0,
choices=[(0, '处理中'), (1, '成功'), (2, '失败')],
verbose_name='状态'
)
wechat_transfer_no = models.CharField(
max_length=64,
null=True,
blank=True,
verbose_name='微信转账单号'
)
wechat_package = models.TextField(
null=True,
blank=True,
verbose_name='微信返回的package_info(JSON)'
)
fail_reason = models.TextField(null=True, blank=True, verbose_name='失败原因')
# 关联提现审核单号(新流程必填,旧数据为空)
shenhe_danhao = models.CharField(
max_length=32, null=True, blank=True, db_index=True, verbose_name='关联审核单号'
)
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'tixian_auto_record'
verbose_name = '自动提现记录'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['tixian_id']),
models.Index(fields=['yonghuid']),
models.Index(fields=['zhuangtai']),
]
def __str__(self):
return f"{self.tixian_id} - {self.get_zhuangtai_display()}"
# yonghu/models.py
class Xiugaijilu(models.Model):
id = models.AutoField(primary_key=True, verbose_name='记录ID')
yonghuid = models.CharField(max_length=7, verbose_name='被修改用户ID')
xiugaiid = models.CharField(max_length=30, verbose_name='修改人账号ID')
leixing = models.IntegerField(verbose_name='被修改用户类型')
# 打手相关金钱字段
xiugaitijiao = models.DecimalField(max_digits=12, decimal_places=2, default=0.00,
verbose_name='修改后的打手提现金额')
xiugaitijiaoq = models.DecimalField(max_digits=12, decimal_places=2, default=0.00,
verbose_name='修改前的打手提现金额')
# 打手积分
jifen = models.IntegerField(default=0, verbose_name='修改后的打手积分')
yjifen = models.IntegerField(default=0, verbose_name='修改前的打手积分')
# 打手押金
yajin = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='修改后的打手押金')
yyajin = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='修改前的打手押金')
# 商家余额
shangjiayue = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='修改后的商家余额')
shangjiayueq = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='修改前的商家余额')
# 管事余额
guanshiyue = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='修改后的管事余额')
guanshiyueq = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='修改前的管事余额')
# 会员相关
huiyuants = models.IntegerField(default=0, verbose_name='添加会员天数')
huiyuan_id = models.CharField(max_length=6, null=True, blank=True, verbose_name='会员ID')
# 其他字段
qitashuoming = models.TextField(null=True, blank=True, verbose_name='其他说明')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'xiugaijilu'
verbose_name = '修改记录表'
verbose_name_plural = verbose_name
def __str__(self):
return f"修改记录-用户{self.yonghuid}"
# yonghu/models.py - 新增排行榜模型
class RankingRecord(models.Model):
# id: 主键ID
id = models.AutoField(primary_key=True, verbose_name='ID')
# zhouqi: 周期类型,1=日榜,2=月榜
zhouqi = models.IntegerField(choices=[(1, '日榜'), (2, '月榜')], verbose_name='周期类型')
# tongji_date: 统计日期(日榜存具体日期,月榜存月份第一天)
tongji_date = models.DateField(db_index=True, verbose_name='统计日期')
# shenfen: 用户身份,2=打手,3=管事,4=商家(1=普通用户不统计)
shenfen = models.IntegerField(choices=[(2, '打手'), (3, '管事'), (4, '商家')], verbose_name='用户身份')
# yonghuid: 用户ID(冗余存储,避免连表查询)
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='用户ID')
# paiming: 排名(从1开始)
paiming = models.IntegerField(verbose_name='排名')
# zhizhi: 指标数值(打手:收益金额,管事:充值人数,商家:流水金额)
zhizhi = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='指标值')
# nicheng: 用户昵称
nicheng = models.CharField(max_length=50, null=True,verbose_name='用户昵称')
# avatar: 用户头像URL
avatar = models.CharField(max_length=500, blank=True, null=True, verbose_name='头像')
# chuangjian_time: 记录创建时间
chuangjian_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
db_table = 'ranking_record'
verbose_name = '排行榜记录'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['zhouqi', 'tongji_date', 'shenfen', 'paiming']),
models.Index(fields=['yonghuid', 'zhouqi']),
models.Index(fields=['tongji_date']),
]
unique_together = [['zhouqi', 'tongji_date', 'shenfen', 'paiming']]
def __str__(self):
zhouqi_str = '日' if self.zhouqi == 1 else '月'
shenfen_map = {2: '打手', 3: '管事', 4: '商家'}
return f"{self.tongji_date} {zhouqi_str}榜 {shenfen_map.get(self.shenfen)} 第{self.paiming}名"
+217
View File
@@ -0,0 +1,217 @@
"""
排行榜接口 — 基于 houtai 日统计表聚合查询
POST /yonghu/phbhqsj
"""
import logging
from datetime import date, timedelta
from decimal import Decimal
from django.db.models import Sum
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from backend.models import (
DashouRiTongji,
GuanshiRiTongji,
ZuzhangRiTongji,
ShangjiaRiTongji,
)
from users.models import UserMain, UserDashou, UserShangjia, UserBoss
logger = logging.getLogger(__name__)
MAX_RANK = 50
RIQI_OPTIONS = frozenset({'今日', '本周', '本月', '总榜', '昨日', '上周', '上月'})
SHENFEN_OPTIONS = frozenset({'dashou', 'guanshi', 'zuzhang', 'shangjia'})
ROLE_CONFIG = {
'dashou': {
'model': DashouRiTongji,
'sort_field': 'chengjiao_zonge',
'fields': ('jiedan_zongliang', 'chengjiao_zongliang', 'jiedan_zonge', 'chengjiao_zonge'),
'int_fields': frozenset({'jiedan_zongliang', 'chengjiao_zongliang'}),
},
'guanshi': {
'model': GuanshiRiTongji,
'sort_field': 'shouru_zonge',
'fields': ('yaoqing_dashou_shu', 'chongzhi_dashou_shu', 'shouru_zonge'),
'int_fields': frozenset({'yaoqing_dashou_shu', 'chongzhi_dashou_shu'}),
},
'zuzhang': {
'model': ZuzhangRiTongji,
'sort_field': 'shouru_zonge',
'fields': ('yaoqing_guanshi_shu', 'fenyong_jine', 'shouru_zonge'),
'int_fields': frozenset({'yaoqing_guanshi_shu'}),
},
'shangjia': {
'model': ShangjiaRiTongji,
'sort_field': 'jiesuan_jine',
'fields': ('paifa_dingdan_shu', 'paifa_jine', 'jiesuan_dingdan_shu', 'jiesuan_jine'),
'int_fields': frozenset({'paifa_dingdan_shu', 'jiesuan_dingdan_shu'}),
},
}
def _resolve_date_range(riqi: str):
"""根据前端 riqi 参数返回 (start_date, end_date) 闭区间。"""
today = date.today()
if riqi == '今日':
return today, today
if riqi == '昨日':
d = today - timedelta(days=1)
return d, d
if riqi == '本周':
monday = today - timedelta(days=today.weekday())
return monday, today
if riqi == '上周':
this_monday = today - timedelta(days=today.weekday())
last_sunday = this_monday - timedelta(days=1)
last_monday = last_sunday - timedelta(days=6)
return last_monday, last_sunday
if riqi == '本月':
return today.replace(day=1), today
if riqi == '上月':
first_this_month = today.replace(day=1)
last_day_prev = first_this_month - timedelta(days=1)
return last_day_prev.replace(day=1), last_day_prev
return None
def _fmt_value(val, is_int: bool):
if val is None:
return 0 if is_int else 0.0
if is_int:
return int(val)
if isinstance(val, Decimal):
return float(val.quantize(Decimal('0.01')))
return float(val)
class PhbHqsjView(APIView):
"""
排行榜数据接口
POST /yonghu/phbhqsj
请求参数:
shenfen: dashou | guanshi | zuzhang | shangjia
riqi: 今日 | 本周 | 本月 | 总榜 | 昨日 | 上周 | 上月
响应 data.list[](最多50条,已按各身份排序字段降序):
mingci, yonghuid, nicheng, touxiang + 各身份统计字段
"""
permission_classes = [IsAuthenticated]
def post(self, request):
shenfen = (request.data.get('shenfen') or '').strip()
riqi = (request.data.get('riqi') or '').strip()
if shenfen not in SHENFEN_OPTIONS:
return Response({
'code': 400,
'msg': '参数错误:shenfen 必须为 dashou/guanshi/zuzhang/shangjia',
'data': {'list': []},
})
if riqi not in RIQI_OPTIONS:
return Response({
'code': 400,
'msg': '参数错误:riqi 必须为 今日/本周/本月/总榜/昨日/上周/上月',
'data': {'list': []},
})
try:
if riqi == '总榜':
rows = self._query_rank_rows(shenfen, all_time=True)
else:
date_range = _resolve_date_range(riqi)
if date_range is None:
return Response({
'code': 400,
'msg': '参数错误:无效的日期范围',
'data': {'list': []},
})
rows = self._query_rank_rows(shenfen, date_range[0], date_range[1])
nick_map, avatar_map = self._load_user_display(rows, shenfen)
cfg = ROLE_CONFIG[shenfen]
int_fields = cfg['int_fields']
result_list = []
for idx, row in enumerate(rows):
yonghuid = row['yonghuid']
item = {
'mingci': idx + 1,
'yonghuid': yonghuid,
'nicheng': nick_map.get(yonghuid, '用户'),
'touxiang': avatar_map.get(yonghuid, '') or '',
}
for field in cfg['fields']:
item[field] = _fmt_value(row.get(field), field in int_fields)
result_list.append(item)
return Response({
'code': 200,
'msg': '获取成功',
'data': {'list': result_list},
})
except Exception as e:
logger.error('排行榜查询失败 shenfen=%s riqi=%s: %s', shenfen, riqi, e, exc_info=True)
return Response({
'code': 500,
'msg': '服务器内部错误',
'data': {'list': []},
})
def _query_rank_rows(self, shenfen, start_date=None, end_date=None, all_time=False):
cfg = ROLE_CONFIG[shenfen]
model = cfg['model']
sort_field = cfg['sort_field']
fields = cfg['fields']
annotate_kwargs = {f: Sum(f) for f in fields}
if all_time:
rows = (
model.objects
.values('yonghuid')
.annotate(**annotate_kwargs)
.order_by(f'-{sort_field}')[:MAX_RANK]
)
return list(rows)
if start_date == end_date:
qs = model.objects.filter(riqi=start_date).order_by(f'-{sort_field}')[:MAX_RANK]
return [
{'yonghuid': r.yonghuid, **{f: getattr(r, f) for f in fields}}
for r in qs
]
rows = (
model.objects
.filter(riqi__gte=start_date, riqi__lte=end_date)
.values('yonghuid')
.annotate(**annotate_kwargs)
.order_by(f'-{sort_field}')[:MAX_RANK]
)
return list(rows)
def _load_user_display(self, rows, shenfen):
yonghuids = [r['yonghuid'] for r in rows if r.get('yonghuid')]
if not yonghuids:
return {}, {}
users = UserMain.objects.filter(yonghuid__in=yonghuids)
avatar_map = {u.yonghuid: u.avatar or '' for u in users}
nick_map = {}
if shenfen == 'dashou':
for p in UserDashou.objects.filter(user__yonghuid__in=yonghuids).select_related('user'):
nick_map[p.user.yonghuid] = p.nicheng or '用户'
elif shenfen == 'shangjia':
for p in UserShangjia.objects.filter(user__yonghuid__in=yonghuids).select_related('user'):
nick_map[p.user.yonghuid] = p.nicheng or '用户'
else:
for b in UserBoss.objects.filter(user__yonghuid__in=yonghuids).select_related('user'):
nick_map[b.user.yonghuid] = b.nickname or '用户'
return nick_map, avatar_map
+280
View File
@@ -0,0 +1,280 @@
# yonghu/ranking_tasks.py
"""
排行榜数据转移任务 - 生产环境专用
在清空前将数据转移到排行榜表
"""
from celery import shared_task
from django.db import transaction
from django.utils import timezone
from datetime import datetime, timedelta
import logging
from decimal import Decimal
from users.models import (
UserMain, UserDashou, UserShangjia, UserGuanshi, UserBoss,
RankingRecord
)
logger = logging.getLogger(__name__)
def get_user_nicheng(user, shenfen):
"""
获取用户昵称
规则:打手用打手nicheng,其他用老板nickname
"""
try:
if shenfen == 2: # 打手
dashou_profile = getattr(user, 'dashou_profile', None)
if dashou_profile and dashou_profile.nicheng:
return dashou_profile.nicheng
else: # 管事或商家
boss_profile = getattr(user, 'boss_profile', None)
if boss_profile and boss_profile.nickname:
return boss_profile.nickname
# 默认返回用户ID
return f"用户{user.yonghuid}"
except Exception:
return f"用户{user.yonghuid}"
@shared_task
def zhuanyi_ribang():
"""
转移日榜数据 - 每天23:55执行
在清零前将今日数据转移到排行榜表
"""
try:
# 统计日期(今天)
today = timezone.now().date()
logger.info(f"开始转移日榜数据,日期: {today}")
records_to_create = []
# 1. 转移打手日榜(今日收益 > 0)
dashou_list = UserDashou.objects.filter(
jinrishouyi__gt=0 # 只转移收益大于0的
).select_related('user').order_by('-jinrishouyi')
for i, dashou in enumerate(dashou_list, start=1):
if i > 100: # 最多100条
break
user = dashou.user
record = RankingRecord(
zhouqi=1, # 日榜
tongji_date=today,
shenfen=2, # 打手
yonghuid=user.yonghuid,
paiming=i,
zhizhi=dashou.jinrishouyi,
nicheng=get_user_nicheng(user, 2),
avatar=user.avatar or ''
)
records_to_create.append(record)
# 2. 转移商家日榜(今日流水 > 0)
shangjia_list = UserShangjia.objects.filter(
jinriliushui__gt=0 # 只转移流水大于0的
).select_related('user').order_by('-jinriliushui')
for i, shangjia in enumerate(shangjia_list, start=1):
if i > 100: # 最多100条
break
user = shangjia.user
record = RankingRecord(
zhouqi=1,
tongji_date=today,
shenfen=4, # 商家
yonghuid=user.yonghuid,
paiming=i,
zhizhi=shangjia.jinriliushui,
nicheng=get_user_nicheng(user, 4),
avatar=user.avatar or ''
)
records_to_create.append(record)
# 3. 转移管事日榜(今日充值人数 > 0)
guanshi_list = UserGuanshi.objects.filter(
jinrichongzhi__gt=0 # 只转移充值人数大于0的
).select_related('user').order_by('-jinrichongzhi')
for i, guanshi in enumerate(guanshi_list, start=1):
if i > 100: # 最多100条
break
user = guanshi.user
record = RankingRecord(
zhouqi=1,
tongji_date=today,
shenfen=3, # 管事
yonghuid=user.yonghuid,
paiming=i,
zhizhi=Decimal(guanshi.jinrichongzhi),
nicheng=get_user_nicheng(user, 3),
avatar=user.avatar or ''
)
records_to_create.append(record)
# 批量保存
if records_to_create:
with transaction.atomic():
RankingRecord.objects.bulk_create(records_to_create)
logger.info(f"日榜数据转移完成,保存{len(records_to_create)}条记录")
result = {
'success': True,
'date': today.strftime('%Y-%m-%d'),
'total_count': len(records_to_create),
}
return result
except Exception as e:
logger.error(f"日榜数据转移失败: {str(e)}", exc_info=True)
raise
@shared_task
def zhuanyi_yuebang():
"""
转移月榜数据 - 每月最后一天23:50执行
在清零前将本月数据转移到排行榜表
"""
try:
# 🔥 获取当前时间
now = timezone.now()
today = now.date()
# 🔥 判断今天是否是当月的最后一天
# 方法:计算明天的日期,如果明天的月份和今天不同,那么今天就是最后一天
tomorrow = today + timedelta(days=1)
if tomorrow.month == today.month:
# 今天不是最后一天,直接返回
logger.info(f"今天不是当月最后一天({today}),跳过月榜转移")
return {
'success': True,
'message': f'今天不是当月最后一天,跳过执行',
'is_last_day': False,
'today': today.strftime('%Y-%m-%d'),
}
logger.info(f"今天是当月最后一天({today}),开始转移月榜数据")
# 🔥 统计日期为当月第一天(如2024-01-01)
tongji_date = datetime(today.year, today.month, 1).date()
records_to_create = []
# 1. 转移打手月榜(今月收益 > 0)
dashou_list = UserDashou.objects.filter(
jinyueshouyi__gt=0 # 只转移收益大于0的
).select_related('user').order_by('-jinyueshouyi')
for i, dashou in enumerate(dashou_list, start=1):
if i > 100: # 最多100条
break
user = dashou.user
record = RankingRecord(
zhouqi=2, # 月榜
tongji_date=tongji_date,
shenfen=2, # 打手
yonghuid=user.yonghuid,
paiming=i,
zhizhi=dashou.jinyueshouyi,
nicheng=get_user_nicheng(user, 2),
avatar=user.avatar or ''
)
records_to_create.append(record)
# 2. 转移商家月榜(今月流水 > 0)
shangjia_list = UserShangjia.objects.filter(
jinyueliushui__gt=0 # 只转移流水大于0的
).select_related('user').order_by('-jinyueliushui')
for i, shangjia in enumerate(shangjia_list, start=1):
if i > 100: # 最多100条
break
user = shangjia.user
record = RankingRecord(
zhouqi=2,
tongji_date=tongji_date,
shenfen=4, # 商家
yonghuid=user.yonghuid,
paiming=i,
zhizhi=shangjia.jinyueliushui,
nicheng=get_user_nicheng(user, 4),
avatar=user.avatar or ''
)
records_to_create.append(record)
# 3. 转移管事月榜(今月充值人数 > 0)
guanshi_list = UserGuanshi.objects.filter(
jinyuechongzhi__gt=0 # 只转移充值人数大于0的
).select_related('user').order_by('-jinyuechongzhi')
for i, guanshi in enumerate(guanshi_list, start=1):
if i > 100: # 最多100条
break
user = guanshi.user
record = RankingRecord(
zhouqi=2,
tongji_date=tongji_date,
shenfen=3, # 管事
yonghuid=user.yonghuid,
paiming=i,
zhizhi=Decimal(guanshi.jinyuechongzhi),
nicheng=get_user_nicheng(user, 3),
avatar=user.avatar or ''
)
records_to_create.append(record)
# 批量保存
if records_to_create:
with transaction.atomic():
RankingRecord.objects.bulk_create(records_to_create)
logger.info(f"月榜数据转移完成,保存{len(records_to_create)}条记录")
result = {
'success': True,
'month': tongji_date.strftime('%Y-%m'),
'total_count': len(records_to_create),
'is_last_day': True,
'today': today.strftime('%Y-%m-%d'),
}
return result
except Exception as e:
logger.error(f"月榜数据转移失败: {str(e)}", exc_info=True)
raise
@shared_task
def qingli_jiulishuju():
"""
清理旧历史数据 - 每月1号凌晨1点执行
清理3个月前的排行榜数据,保留最近3个月的数据
"""
try:
# 计算3个月前的日期
three_months_ago = timezone.now().date() - timedelta(days=90)
# 删除旧数据
deleted_count, _ = RankingRecord.objects.filter(
tongji_date__lt=three_months_ago
).delete()
logger.info(f"清理旧历史数据完成,删除{deleted_count}条记录")
return {
'success': True,
'deleted_count': deleted_count,
'before_date': three_months_ago.strftime('%Y-%m-%d')
}
except Exception as e:
logger.error(f"清理旧历史数据失败: {str(e)}")
raise
+176
View File
@@ -0,0 +1,176 @@
"""
阿龙电竞 - 用户定时任务
处理每日/每月清零等用户相关定时任务
完整版本,包含所有异常处理
"""
from celery import shared_task
from django.db import transaction
from django.db.models import F, Q
from django.utils import timezone
from datetime import datetime
import logging
# 导入模型
from users.models import UserDashou, UserShangjia, UserGuanshi
from utils.celery_utils import log_task_execution, rollback_on_failure
logger = logging.getLogger(__name__)
@shared_task
@rollback_on_failure("每日清零任务")
def daily_reset_task():
"""
每日清零任务 - 每天凌晨0点执行
清零打手、商家、管事的今日相关字段
"""
try:
with transaction.atomic():
start_time = timezone.now()
today = datetime.now().strftime('%Y-%m-%d')
logger.info(f"开始执行每日清零任务,日期: {today}")
# 1. 清零打手今日数据
dashou_updated = UserDashou.objects.filter(
Q(jinrijiedan__gt=0) | Q(jinrishouyi__gt=0)
).update(
jinrijiedan=0,
jinrishouyi=0
)
logger.info(f"打手今日数据清零完成,影响记录: {dashou_updated}条")
# 2. 清零商家今日数据
shangjia_updated = UserShangjia.objects.filter(
Q(jinridingdan__gt=0) | Q(jinriliushui__gt=0)
).update(
jinridingdan=0,
jinriliushui=0
)
logger.info(f"商家今日数据清零完成,影响记录: {shangjia_updated}条")
# 3. 清零管事今日数据
guanshi_updated = UserGuanshi.objects.filter(
jinrichongzhi__gt=0
).update(
jinrichongzhi=0
)
logger.info(f"管事今日数据清零完成,影响记录: {guanshi_updated}条")
end_time = timezone.now()
duration = (end_time - start_time).total_seconds()
# 记录执行结果
result_msg = (
f"每日清零完成 - 日期: {today}, "
f"打手: {dashou_updated}条, "
f"商家: {shangjia_updated}条, "
f"管事: {guanshi_updated}条, "
f"耗时: {duration:.2f}秒"
)
log_task_execution("每日清零任务", True, result_msg)
return {
"success": True,
"date": today,
"updated_dashou": dashou_updated,
"updated_shangjia": shangjia_updated,
"updated_guanshi": guanshi_updated,
"duration_seconds": duration,
"message": result_msg
}
except Exception as e:
error_msg = f"每日清零任务失败: {str(e)}"
logger.error(error_msg)
log_task_execution("每日清零任务", False, error_msg)
return {
"success": False,
"error": str(e),
"date": datetime.now().strftime('%Y-%m-%d'),
"message": error_msg
}
@shared_task
@rollback_on_failure("每月清零任务")
def monthly_reset_task():
"""
每月清零任务 - 每月1日凌晨0点执行
清零打手、商家、管事的本月相关字段
"""
try:
with transaction.atomic():
start_time = timezone.now()
current_month = datetime.now().strftime('%Y-%m')
logger.info(f"开始执行每月清零任务,月份: {current_month}")
# 1. 清零打手本月数据
dashou_updated = UserDashou.objects.filter(
Q(jinyuejiedan__gt=0) | Q(jinyueshouyi__gt=0)
).update(
jinyuejiedan=0,
jinyueshouyi=0
)
logger.info(f"打手本月数据清零完成,影响记录: {dashou_updated}条")
# 2. 清零商家本月数据
shangjia_updated = UserShangjia.objects.filter(
Q(jinyuedingdan__gt=0) | Q(jinyueliushui__gt=0)
).update(
jinyuedingdan=0,
jinyueliushui=0
)
logger.info(f"商家本月数据清零完成,影响记录: {shangjia_updated}条")
# 3. 清零管事本月数据
guanshi_updated = UserGuanshi.objects.filter(
jinyuechongzhi__gt=0
).update(
jinyuechongzhi=0
)
logger.info(f"管事本月数据清零完成,影响记录: {guanshi_updated}条")
end_time = timezone.now()
duration = (end_time - start_time).total_seconds()
# 记录执行结果
result_msg = (
f"每月清零完成 - 月份: {current_month}, "
f"打手: {dashou_updated}条, "
f"商家: {shangjia_updated}条, "
f"管事: {guanshi_updated}条, "
f"耗时: {duration:.2f}秒"
)
log_task_execution("每月清零任务", True, result_msg)
return {
"success": True,
"month": current_month,
"updated_dashou": dashou_updated,
"updated_shangjia": shangjia_updated,
"updated_guanshi": guanshi_updated,
"duration_seconds": duration,
"message": result_msg
}
except Exception as e:
error_msg = f"每月清零任务失败: {str(e)}"
logger.error(error_msg)
log_task_execution("每月清零任务", False, error_msg)
return {
"success": False,
"error": str(e),
"month": datetime.now().strftime('%Y-%m'),
"message": error_msg
}
+3
View File
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.
File diff suppressed because it is too large. Load diff
+382
View File
@@ -0,0 +1,382 @@
# yonghu/tixian_shenhe_views.py
"""
自动提现审核 — 用户侧接口:
① TixianZddkshApplyView → POST /yonghu/zddksh 提交审核(扣款)
② process_audit_collect → POST /yonghu/tixiansq 收款(P0:一单到底,先查微信再决定是否新建)
"""
import decimal
import json
import logging
import random
import string
import time
import requests
from django.conf import settings
from django.db import transaction
from django.utils import timezone
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from utils.redis_lock import acquire_lock, release_lock
from utils.wechat_v3 import build_authorization
from .models import TixianAutoRecord
from .tixian_shenhe_services import (
RECONCILE_ALLOW_NEW,
RECONCILE_COMPLETED,
RECONCILE_PENDING,
RECONCILE_WAIT_CONFIRM,
check_collect_quota_limits,
create_audit_application,
get_audit_for_collect,
handle_post_transfer_failure,
reconcile_shenhe_wechat_bills,
release_collect_quota_for_record,
reserve_collect_quota_limits,
resolve_collect_context,
validate_collect_eligibility,
)
logger = logging.getLogger('yonghu.tixian_shenhe')
def generate_tixian_id():
"""生成唯一微信商户打款单号:TX + 时间戳(13位) + 4位随机数"""
timestamp = str(int(time.time() * 1000))
rand = ''.join(random.choices(string.digits, k=4))
return f'TX{timestamp}{rand}'
def _build_collect_success_response(payload):
return Response({
'code': 0,
'msg': '请确认收款',
'data': payload,
})
def _is_wx_balance_insufficient(err_code='', err_msg=''):
blob = f'{err_code} {err_msg}'.upper()
return any(k in blob for k in ('NOT_ENOUGH', 'INSUFFICIENT', '余额不足', '资金不足'))
def _verify_collect_quota_or_response(user_main, audit, shenhe_danhao):
"""收款限额校验;不通过时直接返回 Response"""
ok, msg, limit_kind = check_collect_quota_limits(
user_main,
audit.leixing,
audit.shenqing_jine,
audit.shijidaozhang,
shenhe_danhao,
)
if not ok:
limit_code = 33 if limit_kind == 'platform' else 400
logger.warning(
'收款限额校验未通过 yonghuid=%s shenhe_danhao=%s leixing=%s kind=%s msg=%s',
user_main.yonghuid, shenhe_danhao, audit.leixing, limit_kind, msg,
)
return Response({'code': limit_code, 'msg': msg})
return None
def _mark_auto_record_failed(tixian_id, fail_reason):
"""微信明确失败时标记打款记录终态,避免后续一直被当成「处理中」"""
TixianAutoRecord.objects.filter(tixian_id=tixian_id, zhuangtai=0).update(
zhuangtai=2,
fail_reason=(fail_reason or '打款失败')[:500],
update_time=timezone.now(),
)
def _build_wx_transfer_body(wx_cfg, tixian_id, openid, yonghuid, leixing, shijidaozhang):
transfer_scene_id = wx_cfg['TRANSFER_SCENE_ID']
role_map = {1: '打手', 2: '管事', 3: '组长', 4: '考核官', 5: '打手', 6: '商家'}
desc_map = {1: '佣金提现', 2: '分红提现', 3: '分红提现', 4: '分佣提现', 5: '押金提现', 6: '余额提现'}
transfer_scene_report_infos = []
if transfer_scene_id == '1005':
transfer_scene_report_infos = [
{'info_type': '岗位类型', 'info_content': role_map.get(leixing, '用户')},
{'info_type': '报酬说明', 'info_content': desc_map.get(leixing, '提现')},
]
elif transfer_scene_id == '1009':
transfer_scene_report_infos = [
{'info_type': '采购商品名称', 'info_content': '平台服务'},
]
return {
'appid': wx_cfg['APPID'],
'out_bill_no': tixian_id,
'transfer_scene_id': transfer_scene_id,
'openid': openid,
'transfer_amount': int(shijidaozhang * 100),
'transfer_remark': f'平台提现-{yonghuid}',
'transfer_scene_report_infos': transfer_scene_report_infos,
}
def _call_wechat_transfer(body, wx_cfg):
url = 'https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills'
body_json = json.dumps(body, separators=(',', ':'))
auth = build_authorization('POST', url, body_json)
headers = {
'Authorization': auth,
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': 'Mozilla/5.0',
}
resp = requests.post(url, data=body_json, headers=headers, timeout=15)
try:
wx_result = resp.json()
except ValueError:
wx_result = {}
return resp, wx_result
class TixianZddkshApplyView(APIView):
"""POST /yonghu/zddksh 提交审核申请(扣款,不创建 TixianAutoRecord)"""
permission_classes = [permissions.IsAuthenticated]
def post(self, request):
user_main = request.user
lock_key = f'tixian_audit_apply:{user_main.yonghuid}'
identifier = acquire_lock(lock_key, timeout=10)
if not identifier:
return Response({'code': 429, 'msg': '操作频繁,请稍后重试'})
try:
leixing = request.data.get('leixing')
jine_str = request.data.get('jine')
if leixing is None or jine_str is None:
return Response({'code': 1, 'msg': '缺少参数: leixing 或 jine'})
try:
leixing = int(leixing)
jine = decimal.Decimal(str(jine_str))
except (ValueError, decimal.InvalidOperation):
return Response({'code': 2, 'msg': '参数格式错误'})
if leixing not in [1, 2, 3, 4, 5, 6]:
return Response({'code': 3, 'msg': '提现类型无效'})
if jine <= decimal.Decimal('0.1'):
return Response({'code': 4, 'msg': '提现金额必须大于0.1'})
try:
data = create_audit_application(user_main, leixing, jine)
except ValueError as e:
return Response({'code': 400, 'msg': str(e)})
except Exception as e:
logger.error(f'提现审核申请异常: {e}', exc_info=True)
return Response({'code': 99, 'msg': '提现申请失败,请稍后重试'})
return Response({'code': 0, 'msg': '提现申请已提交,请等待审核', 'data': data})
finally:
release_lock(lock_key, identifier)
def process_audit_collect(request):
"""
POST /yonghu/tixiansq 用户收款(P0 防多提)
主路径:tixianjilu_id → Tixianjilu → TixianShenheJilu(状态6)
若 TixianAutoRecord 已有 tixian_id → 只问微信,非终态绝不新建
仅微信 FAIL/CANCELLED 终态后才允许新建打款记录
"""
user_main = request.user
yonghuid = user_main.yonghuid
tixianjilu_id = request.data.get('tixianjilu_id') or request.data.get('id')
shenhe_danhao = (request.data.get('shenhe_danhao') or '').strip()
shenhe_jilu_id = request.data.get('shenhe_jilu_id')
logger.info(
'收款请求 yonghuid=%s tixianjilu_id=%s shenhe_jilu_id=%s shenhe_danhao=%s',
yonghuid, tixianjilu_id, shenhe_jilu_id, shenhe_danhao,
)
try:
if not user_main.openid:
return Response({'code': 10, 'msg': '用户未绑定微信,无法收款'})
wx_cfg = settings.WECHAT_PAY_V3_CONFIG
if not wx_cfg.get('APPID') or not wx_cfg.get('MCHID') or not wx_cfg.get('TRANSFER_SCENE_ID'):
logger.error('微信打款配置不完整 APPID=%s MCHID=%s', wx_cfg.get('APPID'), wx_cfg.get('MCHID'))
return Response({'code': 11, 'msg': '系统配置异常,请联系客服'})
ctx, err = resolve_collect_context(
yonghuid,
tixianjilu_id=tixianjilu_id,
shenhe_danhao=shenhe_danhao,
shenhe_jilu_id=shenhe_jilu_id,
)
if not ctx:
return Response({'code': 400, 'msg': err})
audit = ctx['audit']
shenhe_danhao = ctx['shenhe_danhao']
tixianjilu_id_val = ctx['jilu'].id if ctx.get('jilu') else audit.tixianjilu_id
leixing = audit.leixing
# 二次资格校验:不通过仅返回错误,审核单保持待收款(6),不退款,用户补齐资质后可再点收款
collect_ok, collect_msg = validate_collect_eligibility(user_main, leixing)
if not collect_ok:
logger.warning(
'收款校验未通过 yonghuid=%s shenhe_danhao=%s leixing=%s msg=%s',
yonghuid, shenhe_danhao, leixing, collect_msg,
)
return Response({'code': 400, 'msg': collect_msg})
quota_resp = _verify_collect_quota_or_response(user_main, audit, shenhe_danhao)
if quota_resp:
return quota_resp
lock_key = f'tixian_collect:{shenhe_danhao}'
identifier = acquire_lock(lock_key, timeout=15)
if not identifier:
return Response({'code': 429, 'msg': '操作频繁,请稍后重试'})
tixian_id = None
quota_reserved = False
try:
action, payload = reconcile_shenhe_wechat_bills(shenhe_danhao)
if action == RECONCILE_COMPLETED:
return Response({'code': 8, 'msg': payload.get('msg', '该提现已完成,请勿重复操作')})
if action == RECONCILE_WAIT_CONFIRM:
quota_resp = _verify_collect_quota_or_response(user_main, audit, shenhe_danhao)
if quota_resp:
return quota_resp
return _build_collect_success_response(payload)
if action == RECONCILE_PENDING:
msg = payload.get('msg') if isinstance(payload, dict) else payload
return Response({'code': 12, 'msg': msg or '有收款处理中,请稍后再试'})
if action != RECONCILE_ALLOW_NEW:
return Response({'code': 12, 'msg': '收款处理中,请稍后再试'})
with transaction.atomic():
audit, err = get_audit_for_collect(
shenhe_danhao, yonghuid, audit.id,
)
if not audit:
return Response({'code': 400, 'msg': err})
# 金额一律用审核表已落库数据(申请时扣款+算费),收款不再重算
jine = audit.shenqing_jine
shouxufei = audit.shouxufei
shijidaozhang = audit.shijidaozhang
feilv = audit.feilv
ok, msg, limit_kind = reserve_collect_quota_limits(
user_main, leixing, jine, shijidaozhang, shenhe_danhao,
)
if not ok:
limit_code = 33 if limit_kind == 'platform' else 400
return Response({'code': limit_code, 'msg': msg})
quota_reserved = True
tixian_id = generate_tixian_id()
TixianAutoRecord.objects.create(
tixian_id=tixian_id,
yonghuid=yonghuid,
leixing=leixing,
jine=jine,
shouxufei=shouxufei,
shijidaozhang=shijidaozhang,
feilv=feilv,
zhuangtai=0,
shenhe_danhao=shenhe_danhao,
)
audit.tixian_auto_id = tixian_id
audit.save(update_fields=['tixian_auto_id', 'update_time'])
body = _build_wx_transfer_body(
wx_cfg, tixian_id, user_main.openid, yonghuid, leixing, shijidaozhang,
)
try:
resp, wx_result = _call_wechat_transfer(body, wx_cfg)
if resp.status_code == 200 and wx_result.get('state') == 'WAIT_USER_CONFIRM':
package_info = wx_result.get('package_info')
auto_record = TixianAutoRecord.objects.get(tixian_id=tixian_id)
auto_record.wechat_package = json.dumps(package_info, ensure_ascii=False)
auto_record.save(update_fields=['wechat_package'])
return _build_collect_success_response({
'tixian_id': tixian_id,
'package_info': package_info,
'shouxufei': str(shouxufei),
'shijidaozhang': str(shijidaozhang),
'current_rate': str(feilv),
'mch_id': wx_cfg['MCHID'],
'shenhe_danhao': shenhe_danhao,
'tixianjilu_id': tixianjilu_id_val,
})
err_code = wx_result.get('code', '')
err_msg = wx_result.get('message', wx_result.get('detail', '微信接口调用失败'))
logger.error(
'微信发起转账未进入待确认: bill=%s code=%s msg=%s HTTP%s',
tixian_id, err_code, err_msg, resp.status_code,
)
is_balance_issue = _is_wx_balance_insufficient(err_code, err_msg)
if is_balance_issue:
_mark_auto_record_failed(tixian_id, err_msg)
if quota_reserved:
release_collect_quota_for_record(
TixianAutoRecord.objects.get(tixian_id=tixian_id),
)
quota_reserved = False
return Response({
'code': 99,
'msg': '运营账户资金不足,需等管理员充值后才能提现',
})
post_action, post_payload = handle_post_transfer_failure(
tixian_id, shenhe_danhao, err_code, err_msg,
)
if post_action == RECONCILE_WAIT_CONFIRM:
quota_resp = _verify_collect_quota_or_response(user_main, audit, shenhe_danhao)
if quota_resp:
return quota_resp
return _build_collect_success_response(post_payload)
if post_action == RECONCILE_COMPLETED:
return Response({'code': 8, 'msg': '该提现已完成,请勿重复操作'})
if post_action == RECONCILE_PENDING:
pending_msg = (
post_payload.get('msg', post_payload)
if isinstance(post_payload, dict) else post_payload
)
return Response({'code': 12, 'msg': pending_msg or '有收款处理中,请稍后再试'})
user_msg = err_msg or '微信收款发起失败,请稍后重试'
_mark_auto_record_failed(tixian_id, user_msg)
if quota_reserved:
release_collect_quota_for_record(
TixianAutoRecord.objects.get(tixian_id=tixian_id),
)
quota_reserved = False
return Response({'code': 99, 'msg': user_msg})
except requests.RequestException as e:
logger.error(
'微信打款网络异常(保持待查单): bill=%s err=%s',
tixian_id, e, exc_info=True,
)
return Response({
'code': 12,
'msg': '收款请求已提交,系统正在核对状态,请稍后再试,切勿重复点击',
})
finally:
release_lock(lock_key, identifier)
except Exception as e:
logger.error(
'收款接口未捕获异常 yonghuid=%s tixianjilu_id=%s shenhe_danhao=%s err=%s',
yonghuid, tixianjilu_id, shenhe_danhao, e,
exc_info=True,
)
return Response({'code': 99, 'msg': '系统繁忙,请稍后重试'})
+166
View File
@@ -0,0 +1,166 @@
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from .views import WechatMiniProgramLoginView, UserInfoUpdateView, DashouXinxiAPIView, \
ZaixianZhuangtaiAPIView, GuanshiXinxiView, ShangJiaXinXiView, BossBiaoshiView, YaoqingmaView, \
DashouZhuceView, GuanshiYaoqingDashouListView, TixianXinxiHuoquView, ShoukuanXinxiShangchuanView, \
TixianShenqingView, TixianJiluHuoquViewV2, AdminLoginView, AdminFinancialDataView, AdGetDingdanList, \
AdGetOrderTypes, AdGuanLiYongHu, AdYaoQingDaShou, CfGuanLi, AdTongYiChuFa, AdckyhxqView, AdcjxgView, AddtxshView, \
AdtixianqkView, DashouZiliaoHuoquView, DashouGengxinView,ShangjiaZhuceView,\
PaihangbangGuanliQueryView,PaihangbangRiqiliebiaoView, WechatLoginAndDashouRegisterView, \
YonghuTixianShenheXiugaiView, ChufaJiluHuoquView, DashouShensuView, ShangjiaChufaJiluHuoquView, \
WeixinOfficialCallbackView,KefuLoginView, KefuStatsView, KefuGetOrderTypesView, KefuGetOrderListView, \
KefuGetShangjiaOrderListView, KefuGetOrderDetailView, KefuChangeDashouView, KefuRecoverOrderView, \
KefuPlatformRefundView, KefuRejectRefundView, KefuMerchantRefundView, KefuRejectSettlementView, \
KefuTransferHallView, KefuCancelDesignationView, KefuGetDashouListView, KefuGetDashouDetailView, \
KefuUpdateDashouView, KefuPunishmentListView, KefuPunishmentActionView, KefuPunishmentDetailView, \
KefuWithdrawListView, KefuWithdrawDetailView, KefuWithdrawActionView, KefuPunishView, AdKfglView, AdKftjView, \
AdKfxgView,GuanshiRegisterView, WechatLoginAndGuanshiRegisterView,ZuzhangZhuceView, ZuzhangXinxiView,KefuForceCompleteView,HuoQuYaoQingRenView, DashouJianquanView, LaobanJianquanView,\
FaKuanJiFenTongJiView, DaShouFaKuanLieBiaoView,\
FaKuanShenSuView, FaKuanPayView,FaKuanHuitiaoView, FaKuanPayPollView, FaKuanPayFailView,KaohePayView,KaohePayCallbackView, KaohePayPollView,KaohePayFailView, TixianAssetView, GuanshiContactView,TixianQueRenAutoView, TixianShenqingV3View, TixianCallbackV3View
from .tixian_shenhe_views import TixianZddkshApplyView
from .paihang_views import PhbHqsjView
urlpatterns = [
path('wechatlogin', WechatMiniProgramLoginView.as_view(), name='用户一键微信登录'),
path('xiugai', UserInfoUpdateView.as_view(), name='用户信息修改'),
# 打手信息接口
path('dashouxinxi', DashouXinxiAPIView.as_view(), name='打手信息查询'),
# 更改在线状态接口
path('zaixianzhuangtai', ZaixianZhuangtaiAPIView.as_view(), name='打手在线状态更改'),
path('guanshixinxi', GuanshiXinxiView.as_view(), name='获取管事信息'),
path('shangjiaxinxi', ShangJiaXinXiView.as_view(), name='获取商家信息'),
path('bossbiaoshi', BossBiaoshiView.as_view(), name='获取老板标识'),
path('yaoqingma', YaoqingmaView.as_view(), name='管事生成邀请码'),
path('dashouzhuce',DashouZhuceView.as_view(), name='打手邀请码注册'),
path('gshqyqds', GuanshiYaoqingDashouListView.as_view(), name='管事获取邀请打手'),
path('tixianhq', TixianXinxiHuoquView.as_view(), name='获取体现缓存信息'), # 获取提现信息
path('sksc', ShoukuanXinxiShangchuanView.as_view(), name='上传提现相关数据或收款码'), # 上传收款信息
path('tixian', TixianShenqingView.as_view(), name='提现申请'), # 提交提现申请
path('tixianjiluhq', TixianJiluHuoquViewV2.as_view(), name='打手提现记录查询'),
path('adlog', AdminLoginView.as_view(), name='管理元登录'),
path('adhq', AdminFinancialDataView.as_view(), name='管理元获取收支记录'),
path('adgetordertypes', AdGetOrderTypes.as_view(), name='管理员获取订单类型'),
# 订单列表获取接口
path('adddhq', AdGetDingdanList.as_view(), name='管理员获取订单列表'),
path('adyqds', AdYaoQingDaShou.as_view(), name='管理员获取邀请码'),
# 用户管理列表接口
path('adgl', AdGuanLiYongHu.as_view(), name='管理员获取用户'),
path('cfgl', CfGuanLi.as_view(), name='处罚记录获取'),
path('adtycf', AdTongYiChuFa.as_view(), name='同意或拒绝处罚'),
path('adckyhxq', AdckyhxqView.as_view(), name='管理员获取用户具体数据'),
# 修改用户信息接口
path('adcjxg',AdcjxgView.as_view(), name='管理员修改用户数据'),
path('addtxsh', AddtxshView.as_view(), name='管理员获取提现审核'),
# 处理提现申请接口
path('adtixianqk', AdtixianqkView.as_view(), name='管理员提现修改'),
# 打手资料获取
path('dszjxxhq', DashouZiliaoHuoquView.as_view(), name='打手获取自己信息'),
# 打手信息更新
path('dsgxxx', DashouGengxinView.as_view(), name='打手修改自己信息'),
path('shangjiahuce', ShangjiaZhuceView.as_view(), name='商家注册'),
path('paihangbang', PaihangbangGuanliQueryView.as_view(), name='管理查询排行榜'),
path('adhqphrq', PaihangbangRiqiliebiaoView.as_view(), name='管理原获取排行榜日期'),
path('wdlyhdl', WechatLoginAndDashouRegisterView.as_view(), name='打手扫码注册'),
path('yonghutxshxg', YonghuTixianShenheXiugaiView.as_view(), name='用户自己体现修改'),
path('dshqcfjl', ChufaJiluHuoquView.as_view(), name='打手处罚记录获取'),
# 打手申诉接口
path('dscfss', DashouShensuView.as_view(), name='打手处罚上诉申请'),
path('sjcfjlhq', ShangjiaChufaJiluHuoquView.as_view(), name='商家处罚记录获取'),
path('officialcallback', csrf_exempt(WeixinOfficialCallbackView.as_view()), name='用户关注取关服务号回调'),
path('kefujinru', KefuLoginView.as_view(), name='客服登录'),
path('kfjrhqzl', KefuStatsView.as_view(), name='客服获取数据'),
path('kfhqptddlx', KefuGetOrderTypesView.as_view(), name='客服获取订单类型'),
path('kfhqddlb', KefuGetOrderListView.as_view(), name='客服获取订单列表'),
path('kfhqsjddlb', KefuGetShangjiaOrderListView.as_view(),name='客服获取商家订单列表'),
path('kefuhqddxq', KefuGetOrderDetailView.as_view(), name='获取订单详情页面'),
path('kefughds', KefuChangeDashouView.as_view(), name='客服更换打手'),
path('kefuhf', KefuRecoverOrderView.as_view(), name='客服恢复订单'),
path('kefupttk', KefuPlatformRefundView.as_view(), name='客服给平台订单退款'),
path('kefujjtk', KefuRejectRefundView.as_view(), name='客服拒绝商家退款'),
path('kefusjtk', KefuMerchantRefundView.as_view(), name='客服同意商家退款'),
path('kefujjjs', KefuRejectSettlementView.as_view(), name='客服拒绝结算'),
path('kefuzydt', KefuTransferHallView.as_view(), name='客服订单返回大厅'),
path('kefuqxzd', KefuCancelDesignationView.as_view(), name='客服取消指定'),
path('kefuhqdslb', KefuGetDashouListView.as_view(), name='客服获取打手数据'),
path('kefuhqdsxq', KefuGetDashouDetailView.as_view(), name='客服获取打手具体信息'),
path('kefuxgds', KefuUpdateDashouView.as_view(), name='客服修改打手数据'),
path('kefu_cfgl', KefuPunishmentListView.as_view(), name='客服获取处罚记录'),
path('kefu_cf_detail', KefuPunishmentDetailView.as_view(), name='kefu_cf_detail'),
path('kefu_cf_action', KefuPunishmentActionView.as_view(), name='客服处理处罚'),
path('kefuqiangzhijiedan', KefuForceCompleteView.as_view(), name='客服强制结单'),
path('kefu_txsh_list', KefuWithdrawListView.as_view(), name='客服获取体现数据'),
path('kefu_txsh_detail', KefuWithdrawDetailView.as_view(), name='客服获取体现详情数据'),
path('kefu_txsh_action', KefuWithdrawActionView.as_view(), name='客服处理体现'),
path('kefuchufa', KefuPunishView.as_view(), name='客服申请出发打手'),
path('adkfgl', AdKfglView.as_view(), name='管理员获取客服数据'),
path('adkftj', AdKftjView.as_view(), name='管理添加客服'),
path('adkfxg', AdKfxgView.as_view(), name='管理修改客服'),
# 已登录用户注册管事
#path('guanshizhuce', GuanshiRegisterView.as_view(), name='管事注册,已经登陆'),
path('guanshizhuce', GuanshiRegisterView.as_view(), name='管事注册,已经登陆'),
# 未登录用户微信登录+管事注册
path('zuzhangyqmzc', WechatLoginAndGuanshiRegisterView.as_view(), name='管事注册,没有登录'),
path('zuzhangzhuce', ZuzhangZhuceView.as_view(), name='组长注册'),
path('zuzhangxinxi', ZuzhangXinxiView.as_view(), name='组长刷新获取信息'),
path('hqwdyqr', HuoQuYaoQingRenView.as_view(), name='打手联系获取管事'),
path('dshqltqx', DashouJianquanView.as_view(), name='前端鉴权打手是否有使用消息功能'), # 打手鉴权
path('lbhqltqx', LaobanJianquanView.as_view(), name='鉴权老板是否有使用消息功能'), # 老板鉴权
# 获取罚款 + 积分处罚综合统计
path('cffktjhq', FaKuanJiFenTongJiView.as_view(), name='获取罚款 + 积分处罚综合统计'),
# 获取打手自己的罚款列表
path('dsfklbhq', DaShouFaKuanLieBiaoView.as_view(), name='获取打手自己的罚款列表'),
# 提交罚款申诉
path('fkss', FaKuanShenSuView.as_view(), name='提交罚款申诉'),
path('fkjn', FaKuanPayView.as_view(), name='提交罚款微信支付申请'), # 下单
path('fk_huitiao', csrf_exempt(FaKuanHuitiaoView.as_view()), name='提交罚款微信支付回调'), # 回调
path('fkzffc', FaKuanPayPollView.as_view(), name='提交罚款申微信支付成功轮询'), # 轮询
path('fkzfsb', FaKuanPayFailView.as_view(), name='提交罚款微信支付失败'), # 失败
path('khzf', KaohePayView.as_view(), name='考核支付下单'),
path('kh_huitiao', csrf_exempt(KaohePayCallbackView.as_view()), name='考核支付回调'),
path('khzffc', KaohePayPollView.as_view(), name='考核支付复查轮询'),
path('khzfsb', KaohePayFailView.as_view(), name='考核支付失败'),
path('tixianzichan', TixianAssetView.as_view(), name='小程序提现数据获取'),
path('guanshi_contact', GuanshiContactView.as_view(), name='获取组长'),
path('zddksh', TixianZddkshApplyView.as_view(), name='自动提现审核申请'),
path('tixiansq', TixianShenqingV3View.as_view(), name='自动提现收款'),
path('tixianqr', TixianQueRenAutoView.as_view(), name='自动提现确认'),
path('tixiancallback', csrf_exempt(TixianCallbackV3View.as_view()), name='提现回调'),
path('phbhqsj', PhbHqsjView.as_view(), name='排行榜数据'),
]
+12885
View File
File diff suppressed because it is too large. Load diff