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

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

0
dingdan/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
dingdan/admin.py Normal file
View File

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

10
dingdan/apps.py Normal file
View File

@@ -0,0 +1,10 @@
from django.apps import AppConfig
class DingdanConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'dingdan'
def ready(self):
# 导入信号处理,确保信号被注册
import dingdan.signals

View File

@@ -0,0 +1,43 @@
# dingdan/cross_platform_tasks.py
#不在使用
import logging
import requests
from django.conf import settings
logger = logging.getLogger(__name__)
def send_partner_sync_order(partner_order_id, jine, dashou_fencheng, jieshao, beizhu, create_time,nicheng):
"""
异步发送派单通知给对方平台使用简单线程非Celery
注意:此函数应在线程中调用,避免阻塞主请求。
生产环境建议使用Celery此处提供线程兼容版本。
"""
url = f"{settings.PARTNER_BASE_URL}/dingdan/partner_sync_order"
payload = {
'token': settings.CROSS_PLATFORM_TOKEN,
'partner_order_id': partner_order_id,
'jine': jine,
'dashou_fencheng': dashou_fencheng,
'jieshao': jieshao,
'beizhu': beizhu,
'create_time': create_time,
'nicheng': nicheng,
}
try:
response = requests.post(url, json=payload, timeout=5)
if response.status_code == 200:
data = response.json()
if data.get('code') == 0:
logger.info(f"跨平台派单同步成功: {partner_order_id}")
# 可在此处保存对方返回的 local_order_id 到本地订单(如果需要)
# local_order_id = data['data'].get('local_order_id')
# Dingdan.objects.filter(dingdan_id=partner_order_id).update(partner_order_id=local_order_id)
else:
logger.error(f"跨平台派单同步业务失败: {data}")
else:
logger.error(f"跨平台派单同步HTTP失败: {response.status_code}")
except Exception as e:
logger.error(f"跨平台派单同步异常: {str(e)}", exc_info=True)

View File

@@ -0,0 +1,186 @@
# Generated by Django 6.0 on 2025-12-20 23:54
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="Dingdan",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"dingdan_id",
models.CharField(max_length=32, unique=True, verbose_name="订单ID"),
),
(
"zhuangtai",
models.IntegerField(
blank=True, default=1, null=True, verbose_name="订单状态"
),
),
(
"fadan_pingtai",
models.IntegerField(blank=True, null=True, verbose_name="发单平台"),
),
(
"jine",
models.DecimalField(
blank=True,
decimal_places=2,
max_digits=10,
null=True,
verbose_name="订单金额",
),
),
(
"dashou_fencheng",
models.DecimalField(
blank=True,
decimal_places=2,
max_digits=10,
null=True,
verbose_name="打手分成",
),
),
(
"jiedan_dashou_id",
models.CharField(
blank=True, max_length=32, null=True, verbose_name="接单打手ID"
),
),
(
"zhiding_id",
models.CharField(
blank=True, max_length=32, null=True, verbose_name="指定ID"
),
),
(
"shangpin_id",
models.IntegerField(blank=True, null=True, verbose_name="商品ID"),
),
(
"jieshao",
models.TextField(blank=True, null=True, verbose_name="订单介绍"),
),
(
"beizhu",
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": "dingdan",
"indexes": [
models.Index(
fields=["zhuangtai"], name="dingdan_zhuangt_2febe8_idx"
),
models.Index(
fields=["dingdan_id"], name="dingdan_dingdan_83dcb6_idx"
),
models.Index(
fields=["jiedan_dashou_id"], name="dingdan_jiedan__921bfa_idx"
),
],
},
),
migrations.CreateModel(
name="DingdanPingtai",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"laoban_id",
models.CharField(
blank=True, max_length=32, null=True, verbose_name="老板ID"
),
),
(
"laoban_pingjia",
models.TextField(blank=True, null=True, verbose_name="老板评价"),
),
(
"dingdan",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="pingtai_kuozhan",
to="dingdan.dingdan",
verbose_name="订单",
),
),
],
options={
"verbose_name": "平台订单扩展",
"verbose_name_plural": "平台订单扩展",
"db_table": "dingdan_pingtai",
},
),
migrations.CreateModel(
name="DingdanShangjia",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"shangjia_id",
models.CharField(
blank=True, max_length=32, null=True, verbose_name="商家ID"
),
),
(
"shangjia_pingjia",
models.TextField(blank=True, null=True, verbose_name="商家评价"),
),
(
"dingdan",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="shangjia_kuozhan",
to="dingdan.dingdan",
verbose_name="订单",
),
),
],
options={
"verbose_name": "商家订单扩展",
"verbose_name_plural": "商家订单扩展",
"db_table": "dingdan_shangjia",
},
),
]

View File

@@ -0,0 +1,38 @@
# Generated by Django 6.0 on 2025-12-23 21:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="dingdan",
name="huiyuan_id",
field=models.CharField(
blank=True, max_length=6, null=True, verbose_name="会员ID"
),
),
migrations.AddField(
model_name="dingdan",
name="yaoqiuleixing",
field=models.PositiveIntegerField(
blank=True, null=True, verbose_name="抢单要求类型"
),
),
migrations.AddField(
model_name="dingdan",
name="yongjin",
field=models.DecimalField(
blank=True,
decimal_places=2,
max_digits=10,
null=True,
verbose_name="佣金要求",
),
),
]

View File

@@ -0,0 +1,55 @@
# Generated by Django 6.0 on 2025-12-23 21:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0002_dingdan_huiyuan_id_dingdan_yaoqiuleixing_and_more"),
]
operations = [
migrations.CreateModel(
name="Lilubiao",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"lilu",
models.DecimalField(
decimal_places=4,
max_digits=5,
null=True,
verbose_name="平台分成利率1是平台,2是商家",
),
),
(
"fadanpingtai",
models.CharField(
max_length=10,
null=True,
verbose_name="订单所属平台,(商家或平台)",
),
),
(
"create_time",
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
),
(
"update_time",
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
),
],
options={
"db_table": "lilubiao",
},
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 6.0 on 2025-12-24 23:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0003_lilubiao"),
]
operations = [
migrations.AddField(
model_name="dingdan",
name="nicheng",
field=models.CharField(
blank=True, max_length=50, null=True, verbose_name="游戏昵称"
),
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 6.0 on 2025-12-25 22:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0004_dingdan_nicheng"),
]
operations = [
migrations.AddField(
model_name="dingdan",
name="tupian",
field=models.CharField(
blank=True, max_length=500, null=True, verbose_name="商品图片URL"
),
),
]

View File

@@ -0,0 +1,157 @@
# Generated by Django 6.0 on 2025-12-26 17:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0005_dingdan_tupian"),
]
operations = [
migrations.AddField(
model_name="dingdan",
name="dashou_liuyan",
field=models.TextField(blank=True, null=True, verbose_name="打手留言"),
),
migrations.AddField(
model_name="dingdan",
name="leixing_id",
field=models.PositiveIntegerField(
blank=True, null=True, verbose_name="商品类型ID"
),
),
migrations.AlterField(
model_name="dingdan",
name="dingdan_id",
field=models.CharField(
db_index=True, max_length=32, unique=True, verbose_name="订单ID"
),
),
migrations.AlterField(
model_name="dingdan",
name="jiedan_dashou_id",
field=models.CharField(
blank=True,
db_index=True,
max_length=32,
null=True,
verbose_name="接单打手ID",
),
),
migrations.AlterField(
model_name="dingdan",
name="zhuangtai",
field=models.IntegerField(
blank=True, db_index=True, default=1, null=True, verbose_name="订单状态"
),
),
migrations.CreateModel(
name="Dashoutupian",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"dingdan_id",
models.CharField(
db_index=True, max_length=32, null=True, verbose_name="订单ID"
),
),
(
"dashou",
models.CharField(
blank=True,
db_index=True,
max_length=32,
null=True,
verbose_name="接单打手ID",
),
),
(
"tupian",
models.CharField(
blank=True,
max_length=500,
null=True,
verbose_name="打手提交图片URL",
),
),
(
"create_time",
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
),
(
"update_time",
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
),
],
options={
"db_table": "dashoutupian",
"indexes": [
models.Index(
fields=["dashou"], name="dashoutupia_dashou_7de8bd_idx"
),
models.Index(
fields=["dingdan_id"], name="dashoutupia_dingdan_97c685_idx"
),
],
},
),
migrations.CreateModel(
name="Pingfen",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"dashou",
models.CharField(
blank=True,
db_index=True,
max_length=32,
null=True,
verbose_name="接单打手ID",
),
),
(
"zongfen",
models.PositiveIntegerField(default=0, verbose_name="总分"),
),
(
"cishu",
models.PositiveIntegerField(default=0, verbose_name="评分次数"),
),
(
"create_time",
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
),
(
"update_time",
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
),
],
options={
"verbose_name": "评分表",
"db_table": "pingfenbiao",
"indexes": [
models.Index(
fields=["dashou"], name="pingfenbiao_dashou_f62f10_idx"
)
],
},
),
]

View File

@@ -0,0 +1,280 @@
# Generated by Django 6.0 on 2025-12-30 15:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0006_dingdan_dashou_liuyan_dingdan_leixing_id_and_more"),
]
operations = [
migrations.CreateModel(
name="Chufajilu",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"dashouid",
models.CharField(
db_index=True,
max_length=7,
null=True,
verbose_name="被罚打手ID",
),
),
(
"qingqiuid",
models.CharField(
db_index=True,
max_length=7,
null=True,
verbose_name="请求处罚ID",
),
),
(
"chuliid",
models.CharField(
db_index=True,
max_length=7,
null=True,
verbose_name="处理处罚ID",
),
),
(
"cfliyou",
models.TextField(blank=True, null=True, verbose_name="处罚理由"),
),
(
"sqzhuangtai",
models.IntegerField(
blank=True,
default=0,
null=True,
verbose_name="处罚申请状态:0待处理,1成功,2驳回",
),
),
(
"bhliyou",
models.TextField(
blank=True, null=True, verbose_name="驳回处罚理由"
),
),
("jifen", models.IntegerField(default=0, verbose_name="扣除打手积分")),
(
"dingdan_id",
models.CharField(
db_index=True, max_length=32, null=True, verbose_name="订单ID"
),
),
(
"create_time",
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
),
(
"update_time",
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
),
],
options={
"db_table": "chufajilu",
},
),
migrations.CreateModel(
name="Tuikuanjilu",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"dingdan_id",
models.CharField(max_length=32, unique=True, verbose_name="订单ID"),
),
(
"dashouid",
models.CharField(
max_length=7, null=True, verbose_name="接单打手ID"
),
),
(
"qingqiuid",
models.CharField(
max_length=7, null=True, verbose_name="请求用户ID"
),
),
(
"chuliid",
models.CharField(
db_index=True, max_length=7, null=True, verbose_name="处理人ID"
),
),
(
"cfliyou",
models.TextField(blank=True, null=True, verbose_name="理由"),
),
(
"sqzhuangtai",
models.IntegerField(
blank=True,
db_index=True,
default=0,
null=True,
verbose_name="处罚申请状态:0待处理,1成功,2驳回",
),
),
(
"bhliyou",
models.TextField(blank=True, null=True, verbose_name="驳回理由"),
),
(
"jine",
models.DecimalField(
blank=True,
decimal_places=2,
max_digits=10,
null=True,
verbose_name="订单金额",
),
),
(
"dashou_fencheng",
models.DecimalField(
blank=True,
decimal_places=2,
max_digits=10,
null=True,
verbose_name="打手分成",
),
),
(
"jieshao",
models.TextField(blank=True, null=True, verbose_name="订单介绍"),
),
(
"beizhu",
models.TextField(blank=True, null=True, verbose_name="订单备注"),
),
(
"nicheng",
models.CharField(
blank=True, max_length=50, null=True, verbose_name="游戏昵称"
),
),
(
"create_time",
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
),
(
"update_time",
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
),
],
options={
"db_table": "tuikuanjilu",
},
),
migrations.AddField(
model_name="dingdanshangjia",
name="bhliyou",
field=models.TextField(
blank=True, null=True, verbose_name="驳回商家处罚理由"
),
),
migrations.AddField(
model_name="dingdanshangjia",
name="cfliyou",
field=models.TextField(blank=True, null=True, verbose_name="商家处罚理由"),
),
migrations.AddField(
model_name="dingdanshangjia",
name="sqzhuangtai",
field=models.IntegerField(
blank=True,
default=0,
null=True,
verbose_name="处罚申请状态:0待处理,1成功,2驳回",
),
),
migrations.AlterField(
model_name="dingdanpingtai",
name="laoban_id",
field=models.CharField(
blank=True,
db_index=True,
max_length=32,
null=True,
verbose_name="老板ID",
),
),
migrations.AlterField(
model_name="dingdanshangjia",
name="shangjia_id",
field=models.CharField(
blank=True,
db_index=True,
max_length=32,
null=True,
verbose_name="商家ID",
),
),
migrations.AddIndex(
model_name="dingdanpingtai",
index=models.Index(
fields=["laoban_id"], name="dingdan_pin_laoban__d55e6f_idx"
),
),
migrations.AddIndex(
model_name="dingdanshangjia",
index=models.Index(
fields=["shangjia_id"], name="dingdan_sha_shangji_64d28b_idx"
),
),
migrations.AddIndex(
model_name="chufajilu",
index=models.Index(
fields=["dashouid"], name="chufajilu_dashoui_07aac0_idx"
),
),
migrations.AddIndex(
model_name="chufajilu",
index=models.Index(
fields=["qingqiuid"], name="chufajilu_qingqiu_ac160e_idx"
),
),
migrations.AddIndex(
model_name="chufajilu",
index=models.Index(fields=["chuliid"], name="chufajilu_chuliid_cd63ac_idx"),
),
migrations.AddIndex(
model_name="chufajilu",
index=models.Index(
fields=["dingdan_id"], name="chufajilu_dingdan_b8f0c2_idx"
),
),
migrations.AddIndex(
model_name="tuikuanjilu",
index=models.Index(
fields=["chuliid"], name="tuikuanjilu_chuliid_0b0f16_idx"
),
),
migrations.AddIndex(
model_name="tuikuanjilu",
index=models.Index(
fields=["sqzhuangtai"], name="tuikuanjilu_sqzhuan_d89dc9_idx"
),
),
]

View File

@@ -0,0 +1,122 @@
# Generated by Django 6.0 on 2025-12-30 17:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0007_chufajilu_tuikuanjilu_dingdanshangjia_bhliyou_and_more"),
]
operations = [
migrations.CreateModel(
name="Ptliushui",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"zonge",
models.DecimalField(
blank=True,
decimal_places=2,
max_digits=10,
null=True,
verbose_name="收取总金额",
),
),
(
"zhichuzonge",
models.DecimalField(
blank=True,
decimal_places=2,
max_digits=10,
null=True,
verbose_name="支出总金额",
),
),
(
"zongshouyi",
models.DecimalField(
blank=True,
decimal_places=2,
max_digits=10,
null=True,
verbose_name="总收益",
),
),
(
"rizonge",
models.DecimalField(
blank=True,
decimal_places=2,
max_digits=10,
null=True,
verbose_name="日收取",
),
),
(
"rizhichu",
models.DecimalField(
blank=True,
decimal_places=2,
max_digits=10,
null=True,
verbose_name="日支出",
),
),
(
"rishouyi",
models.DecimalField(
blank=True,
decimal_places=2,
max_digits=10,
null=True,
verbose_name="日收益",
),
),
(
"zongshoucishu",
models.IntegerField(default=0, verbose_name="总收取次数"),
),
(
"rishoucishu",
models.IntegerField(default=0, verbose_name="日收取次数"),
),
(
"zongzhichucishu",
models.IntegerField(default=0, verbose_name="总支出次数"),
),
(
"rizhichucishu",
models.IntegerField(default=0, verbose_name="日支出次数"),
),
],
options={
"db_table": "ptliushui",
},
),
migrations.RenameField(
model_name="tuikuanjilu",
old_name="cfliyou",
new_name="liyou",
),
migrations.AlterField(
model_name="tuikuanjilu",
name="sqzhuangtai",
field=models.IntegerField(
blank=True,
db_index=True,
default=0,
null=True,
verbose_name="申请状态:0待处理,1成功,2驳回",
),
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 6.0 on 2025-12-31 11:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0008_ptliushui_rename_cfliyou_tuikuanjilu_liyou_and_more"),
]
operations = [
migrations.AddField(
model_name="dingdan",
name="liaotian_pindao",
field=models.CharField(
blank=True, max_length=255, null=True, verbose_name="聊天频道名"
),
),
]

View File

@@ -0,0 +1,89 @@
# Generated by Django 6.0 on 2025-12-31 15:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0009_dingdan_liaotian_pindao"),
]
operations = [
migrations.RemoveField(
model_name="dingdan",
name="liaotian_pindao",
),
migrations.AddField(
model_name="dingdan",
name="user1_id",
field=models.CharField(
db_index=True, max_length=30, null=True, verbose_name="用户1标识"
),
),
migrations.AddField(
model_name="dingdan",
name="user2_id",
field=models.CharField(
db_index=True, max_length=30, null=True, verbose_name="用户2打手标识"
),
),
migrations.CreateModel(
name="Liaotian",
fields=[
(
"id",
models.BigAutoField(
primary_key=True, serialize=False, verbose_name="主键ID"
),
),
(
"user1_id",
models.CharField(
db_index=True, max_length=30, verbose_name="用户1标识"
),
),
(
"user2_id",
models.CharField(
db_index=True, max_length=30, verbose_name="用户2标识"
),
),
(
"dingdan_id",
models.CharField(max_length=32, unique=True, verbose_name="订单ID"),
),
(
"zhuangtai",
models.IntegerField(
choices=[(1, "开启中"), (0, "已关闭")],
db_index=True,
default=1,
verbose_name="聊天状态",
),
),
(
"create_time",
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
),
(
"update_time",
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
),
],
options={
"db_table": "liaotian",
"indexes": [
models.Index(
fields=["user1_id"], name="liaotian_user1_i_6be6cd_idx"
),
models.Index(
fields=["user2_id"], name="liaotian_user2_i_9f61a4_idx"
),
models.Index(
fields=["zhuangtai"], name="liaotian_zhuangt_5e8be5_idx"
),
],
},
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 6.0 on 2026-01-01 00:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0010_remove_dingdan_liaotian_pindao_dingdan_user1_id_and_more"),
]
operations = [
migrations.AddField(
model_name="dingdanshangjia",
name="sjnicheng",
field=models.CharField(max_length=50, null=True, verbose_name="商家昵称"),
),
]

View File

@@ -0,0 +1,41 @@
# Generated by Django 4.2.27 on 2026-01-20 15:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0011_dingdanshangjia_sjnicheng"),
]
operations = [
migrations.AddField(
model_name="dingdan",
name="auto_expire_at",
field=models.DateTimeField(
blank=True, null=True, verbose_name="计划过期时间"
),
),
migrations.AddField(
model_name="dingdan",
name="auto_task_id",
field=models.CharField(
blank=True, default="", max_length=255, verbose_name="延时任务ID"
),
),
migrations.AddField(
model_name="dingdan",
name="pending_dispatch",
field=models.BooleanField(
db_index=True, default=False, verbose_name="待调度标记"
),
),
migrations.AddField(
model_name="dingdan",
name="status_8_time",
field=models.DateTimeField(
blank=True, null=True, verbose_name="状态变为8的时间"
),
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 4.2.27 on 2026-01-21 11:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0012_dingdan_auto_expire_at_dingdan_auto_task_id_and_more"),
]
operations = [
migrations.AlterField(
model_name="chufajilu",
name="chuliid",
field=models.CharField(
db_index=True, max_length=11, null=True, verbose_name="处理处罚ID"
),
),
]

View File

@@ -0,0 +1,96 @@
# Generated by Django 4.2.27 on 2026-02-06 15:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0013_alter_chufajilu_chuliid"),
]
operations = [
migrations.AddField(
model_name="chufajilu",
name="ssliyou",
field=models.TextField(blank=True, null=True, verbose_name="打手申诉理由"),
),
migrations.AlterField(
model_name="chufajilu",
name="sqzhuangtai",
field=models.IntegerField(
blank=True,
default=0,
null=True,
verbose_name="处罚申请状态:0待处理,1成功,2驳回3申诉",
),
),
migrations.AlterField(
model_name="lilubiao",
name="lilu",
field=models.DecimalField(
decimal_places=4,
max_digits=5,
null=True,
verbose_name="平台分成利率1是平台,3是商家",
),
),
migrations.CreateModel(
name="Chufatupian",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"dingdan_id",
models.CharField(
db_index=True, max_length=32, null=True, verbose_name="订单ID"
),
),
(
"yonghuid",
models.CharField(
blank=True,
db_index=True,
max_length=32,
null=True,
verbose_name="用户ID",
),
),
(
"tupian",
models.CharField(
blank=True,
max_length=500,
null=True,
verbose_name="证据图片URL",
),
),
(
"create_time",
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
),
(
"update_time",
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
),
],
options={
"db_table": "chufatupian",
"indexes": [
models.Index(
fields=["yonghuid"], name="chufatupian_yonghui_71e585_idx"
),
models.Index(
fields=["dingdan_id"], name="chufatupian_dingdan_ce0dd4_idx"
),
],
},
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 4.2.27 on 2026-02-13 22:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0014_chufajilu_ssliyou_alter_chufajilu_sqzhuangtai_and_more"),
]
operations = [
migrations.AddField(
model_name="dingdan",
name="tkly",
field=models.CharField(
blank=True, max_length=500, null=True, verbose_name="退款/拒绝理由"
),
),
]

View File

@@ -0,0 +1,31 @@
# Generated by Django 4.2.27 on 2026-02-19 00:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0015_dingdan_tkly"),
]
operations = [
migrations.AddField(
model_name="dingdan",
name="is_cross",
field=models.IntegerField(
default=0, verbose_name="是否跨平台订单(0否,1是)"
),
),
migrations.AddField(
model_name="dingdan",
name="partner_order_id",
field=models.CharField(
blank=True,
db_index=True,
max_length=32,
null=True,
verbose_name="对方平台订单ID",
),
),
]

View File

@@ -0,0 +1,24 @@
# Generated by Django 4.2.27 on 2026-02-19 23:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0016_dingdan_is_cross_dingdan_partner_order_id"),
]
operations = [
migrations.AddField(
model_name="dingdan",
name="clkf",
field=models.CharField(
db_index=True, max_length=20, null=True, verbose_name="处理客服"
),
),
migrations.AddIndex(
model_name="dingdan",
index=models.Index(fields=["clkf"], name="dingdan_clkf_6793b1_idx"),
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 4.2.27 on 2026-02-20 17:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0017_dingdan_clkf_dingdan_dingdan_clkf_6793b1_idx"),
]
operations = [
migrations.AddField(
model_name="dingdanpingtai",
name="jjjs_biaoshi",
field=models.IntegerField(
default=0, verbose_name="拒绝结算标记(1表示拒绝结算)"
),
),
]

View File

@@ -0,0 +1,30 @@
# Generated by Django 4.2.27 on 2026-03-01 23:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0018_dingdanpingtai_jjjs_biaoshi"),
]
operations = [
migrations.AddField(
model_name="dingdan",
name="wechat_transaction_id",
field=models.CharField(
blank=True,
db_index=True,
max_length=64,
null=True,
verbose_name="微信支付订单号",
),
),
migrations.AddIndex(
model_name="dingdan",
index=models.Index(
fields=["wechat_transaction_id"], name="dingdan_wechat__278b79_idx"
),
),
]

View File

@@ -0,0 +1,33 @@
# Generated by Django 4.2.27 on 2026-03-30 12:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0019_dingdan_wechat_transaction_id_and_more"),
]
operations = [
migrations.AddField(
model_name="dingdan",
name="dispatch_type",
field=models.IntegerField(
choices=[(1, "我方派单"), (2, "对方派单")],
default=0,
verbose_name="派发情况",
),
),
migrations.AddField(
model_name="dingdan",
name="partner_club_id",
field=models.CharField(
blank=True,
db_index=True,
max_length=6,
null=True,
verbose_name="对方俱乐部ID",
),
),
]

View File

@@ -0,0 +1,257 @@
# Generated by Django 4.2.27 on 2026-04-01 00:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0020_dingdan_dispatch_type_dingdan_partner_club_id"),
]
operations = [
migrations.CreateModel(
name="PreSettlement",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"dingdan_id",
models.CharField(
db_index=True,
max_length=32,
unique=True,
verbose_name="我方订单ID",
),
),
(
"partner_order_id",
models.CharField(
db_index=True, max_length=32, verbose_name="对方订单ID"
),
),
(
"partner_club_id",
models.CharField(
db_index=True, max_length=6, verbose_name="对方俱乐部ID"
),
),
(
"dashou_id",
models.CharField(
db_index=True, max_length=7, verbose_name="接单打手ID"
),
),
(
"amount",
models.DecimalField(
decimal_places=2, max_digits=10, verbose_name="打手应分金额"
),
),
(
"order_amount",
models.DecimalField(
decimal_places=2,
default=0.0,
max_digits=10,
verbose_name="订单原金额",
),
),
(
"status",
models.SmallIntegerField(
choices=[(0, "待结算"), (1, "已结算"), (2, "已拒绝")],
default=0,
verbose_name="结算状态",
),
),
(
"process_time",
models.DateTimeField(
blank=True, null=True, verbose_name="处理时间"
),
),
(
"operator",
models.CharField(
blank=True, default="", max_length=32, 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": "pre_settlement",
"indexes": [
models.Index(
fields=["dingdan_id"], name="pre_settlem_dingdan_4f3541_idx"
),
models.Index(
fields=["partner_order_id"],
name="pre_settlem_partner_45b29e_idx",
),
models.Index(
fields=["partner_club_id"],
name="pre_settlem_partner_d2c44c_idx",
),
models.Index(
fields=["dashou_id"], name="pre_settlem_dashou__d5fe7c_idx"
),
models.Index(
fields=["status"], name="pre_settlem_status_0aa711_idx"
),
],
},
),
migrations.CreateModel(
name="OrderDashouHistory",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"dingdan_id",
models.CharField(
db_index=True, max_length=32, verbose_name="订单ID"
),
),
(
"dashou_id",
models.CharField(
db_index=True, max_length=7, verbose_name="打手ID"
),
),
(
"times",
models.PositiveSmallIntegerField(
verbose_name="第几次接单从1开始"
),
),
(
"operator",
models.CharField(max_length=11, verbose_name="操作客服ID"),
),
(
"create_time",
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
),
],
options={
"verbose_name": "订单打手变更历史",
"verbose_name_plural": "订单打手变更历史",
"db_table": "order_dashou_history",
"indexes": [
models.Index(
fields=["dingdan_id"], name="order_dasho_dingdan_1e79ab_idx"
),
models.Index(
fields=["dashou_id"], name="order_dasho_dashou__0a637c_idx"
),
],
},
),
migrations.CreateModel(
name="CrossPlatformOrderData",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"dingdan_id",
models.CharField(
db_index=True,
max_length=32,
unique=True,
verbose_name="我方订单ID",
),
),
(
"partner_order_id",
models.CharField(
db_index=True, max_length=32, verbose_name="对方平台订单ID"
),
),
(
"partner_club_id",
models.CharField(
db_index=True, max_length=6, verbose_name="对方俱乐部ID"
),
),
(
"partner_order_status",
models.IntegerField(default=0, verbose_name="对方平台订单状态"),
),
(
"partner_dashou_id",
models.CharField(
blank=True,
default="",
max_length=32,
verbose_name="对方平台接单打手ID",
),
),
(
"partner_amount",
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="更新时间"),
),
],
options={
"verbose_name": "跨平台订单扩展",
"verbose_name_plural": "跨平台订单扩展",
"db_table": "cross_platform_order_data",
"indexes": [
models.Index(
fields=["dingdan_id"], name="cross_platf_dingdan_498d08_idx"
),
models.Index(
fields=["partner_order_id"],
name="cross_platf_partner_5d5615_idx",
),
models.Index(
fields=["partner_club_id"],
name="cross_platf_partner_1054e1_idx",
),
],
},
),
]

View File

@@ -0,0 +1,24 @@
# Generated by Django 4.2.27 on 2026-04-06 23:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0021_presettlement_orderdashouhistory_and_more"),
]
operations = [
migrations.AlterField(
model_name="presettlement",
name="dingdan_id",
field=models.CharField(
db_index=True, max_length=32, verbose_name="我方订单ID"
),
),
migrations.AlterUniqueTogether(
name="presettlement",
unique_together={("dingdan_id", "dashou_id")},
),
]

View File

@@ -0,0 +1,22 @@
# Generated by Django 4.2.27 on 2026-04-10 18:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0022_alter_presettlement_dingdan_id_and_more"),
]
operations = [
migrations.AlterField(
model_name="lilubiao",
name="fadanpingtai",
field=models.CharField(
max_length=10,
null=True,
verbose_name="利率类型,平台分成利率1是平台订单打手分成,3是商家订单打手分成5打手提现佣金抽成6管事提现抽成8组长提现抽成",
),
),
]

View File

@@ -0,0 +1,25 @@
# Generated by Django 4.2.27 on 2026-06-07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("dingdan", "0023_alter_lilubiao_fadanpingtai"),
]
operations = [
migrations.AddField(
model_name="dingdan",
name="guanshi_fencheng",
field=models.DecimalField(
blank=True,
decimal_places=2,
default=0,
max_digits=10,
null=True,
verbose_name="管事分成",
),
),
]

View File

589
dingdan/models.py Normal file
View File

@@ -0,0 +1,589 @@
from django.db import models
class Dingdan(models.Model):
"""订单主表"""
# 核心编号:唯一且不可为空[citation:3][citation:9]
dingdan_id = models.CharField(max_length=32, unique=True,db_index=True, verbose_name='订单ID')
# 状态与平台:用整数表示,具体含义由业务逻辑定义
zhuangtai = models.IntegerField(default=1, null=True, blank=True,db_index=True, verbose_name='订单状态')
fadan_pingtai = models.IntegerField(null=True, blank=True, verbose_name='发单平台')
# 金额相关
jine = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='订单金额')
dashou_fencheng = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='打手分成')
guanshi_fencheng = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, default=0, verbose_name='管事分成')
# 用户与商品关联ID
jiedan_dashou_id = models.CharField(max_length=32, null=True, db_index=True,blank=True, verbose_name='接单打手ID')
dashou_liuyan = models.TextField(null=True, blank=True, verbose_name='打手留言')
zhiding_id = models.CharField(max_length=32, null=True, blank=True, verbose_name='指定ID')
shangpin_id = models.IntegerField(null=True, blank=True, verbose_name='商品ID')
leixing_id = models.PositiveIntegerField(null=True, blank=True, verbose_name='商品类型ID') # 直接存数字
tupian = models.CharField(max_length=500, null=True, blank=True, verbose_name='商品图片URL')
yaoqiuleixing = models.PositiveIntegerField(null=True, blank=True, verbose_name='抢单要求类型')
huiyuan_id = models.CharField(max_length=6, null=True, blank=True, verbose_name='会员ID')
yongjin = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='佣金要求')
# 订单描述信息
jieshao = models.TextField(null=True, blank=True, verbose_name='订单介绍')
beizhu = models.TextField(null=True, blank=True, verbose_name='订单备注')
tkly = models.CharField(max_length=500, blank=True, null=True, verbose_name='退款/拒绝理由')
# 新增字段:游戏昵称(前端提交的游戏昵称)
nicheng = models.CharField(max_length=50, null=True, blank=True, verbose_name='游戏昵称')
user1_id = models.CharField(max_length=30,null=True, db_index=True, verbose_name='用户1标识')
user2_id = models.CharField(max_length=30, null=True,db_index=True, verbose_name='用户2打手标识')
#liaotian_pindao = models.CharField(max_length=255, blank=True, null=True, verbose_name='聊天频道名')
# 时间戳
auto_task_id = models.CharField('延时任务ID', max_length=255, blank=True, default='')
auto_expire_at = models.DateTimeField('计划过期时间', null=True, blank=True)
pending_dispatch = models.BooleanField('待调度标记', default=False, db_index=True)
status_8_time = models.DateTimeField('状态变为8的时间', null=True, blank=True)
partner_order_id = models.CharField(max_length=32, null=True, blank=True, db_index=True,verbose_name='对方平台订单ID')
is_cross = models.IntegerField(default=0, verbose_name='是否跨平台订单(0否,1是)')
dispatch_type = models.IntegerField(default=0, choices=[(1, '我方派单'), (2, '对方派单')], verbose_name='派发情况')
# 🔥 新增对方俱乐部ID
partner_club_id = models.CharField(max_length=6, null=True, blank=True, db_index=True, verbose_name='对方俱乐部ID')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
clkf = models.CharField(max_length=20, null=True, db_index=True, verbose_name='处理客服')
wechat_transaction_id = models.CharField(
max_length=64,
blank=True,
null=True,
db_index=True,
verbose_name='微信支付订单号'
)
class Meta:
db_table = 'dingdan'
verbose_name = '订单'
verbose_name_plural = '订单'
# 为高频查询字段添加索引
indexes = [
models.Index(fields=['zhuangtai']),
models.Index(fields=['dingdan_id']),
models.Index(fields=['jiedan_dashou_id']),
models.Index(fields=['clkf']),
# 🆕 为新增字段添加索引(可选)
models.Index(fields=['wechat_transaction_id']),
]
def __str__(self):
return self.dingdan_id
class DingdanPingtai(models.Model):
"""平台订单扩展表 (当 fadan_pingtai=1 时使用)"""
dingdan = models.OneToOneField(Dingdan, on_delete=models.CASCADE, related_name='pingtai_kuozhan', verbose_name='订单')
laoban_id = models.CharField(max_length=32, null=True, blank=True, db_index=True,verbose_name='老板ID')
laoban_pingjia = models.TextField(null=True, blank=True, verbose_name='老板评价')
jjjs_biaoshi = models.IntegerField(default=0, verbose_name='拒绝结算标记(1表示拒绝结算)')
# ========== 新增字段 ==========
pingtai_shouyi = models.DecimalField(max_digits=10,decimal_places=2,null=True,blank=True,default=0.00,verbose_name='平台收益')
dianpu_shouyi = models.DecimalField(max_digits=10,decimal_places=2, null=True,blank=True,default=0.00,verbose_name='店铺收益')
dianpu_id = models.PositiveIntegerField(null=True,blank=True,db_index=True,verbose_name='所属店铺ID')
# ---------- 上级分红快照 ----------
shangji_dianpu_id = models.PositiveIntegerField(null=True, blank=True, db_index=True,
verbose_name='该订单的上级店铺ID')
shangji_fenhong = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, default=0.00,
verbose_name='上级分红金额')
# =============================
class Meta:
db_table = 'dingdan_pingtai'
verbose_name = '平台订单扩展'
verbose_name_plural = '平台订单扩展'
indexes = [
models.Index(fields=['laoban_id']),
models.Index(fields=['dianpu_id']), # 为新字段加索引
]
def __str__(self):
return f"平台扩展-{self.dingdan.dingdan_id}"
class DingdanShangjia(models.Model):
"""商家发单扩展表 (当 fadan_pingtai=2 时使用)"""
dingdan = models.OneToOneField(Dingdan, on_delete=models.CASCADE, related_name='shangjia_kuozhan', verbose_name='订单')
shangjia_id = models.CharField(max_length=32, null=True, blank=True,db_index=True, verbose_name='商家ID')
shangjia_pingjia = models.TextField(null=True, blank=True, verbose_name='商家评价')
cfliyou = models.TextField(null=True, blank=True, verbose_name='商家处罚理由')
sqzhuangtai =models.IntegerField(default=0, null=True, blank=True, verbose_name='处罚申请状态:0待处理,1成功,2驳回')
bhliyou = models.TextField(null=True, blank=True, verbose_name='驳回商家处罚理由')
sjnicheng =models.CharField(max_length=50,null=True, verbose_name='商家昵称')
partner_yonghu_id = models.CharField(max_length=32, blank=True, null=True, verbose_name='对方平台用户ID')
# 在 dingdan/models.py 的 DingdanShangjia 类中添加
shangyige_dingdan_id = models.CharField(
max_length=32,
null=True,
blank=True,
verbose_name='上一个订单ID'
)
class Meta:
db_table = 'dingdan_shangjia'
verbose_name = '商家订单扩展'
verbose_name_plural = '商家订单扩展'
indexes = [
models.Index(fields=['shangjia_id']),
]
def __str__(self):
return f"商家扩展-{self.dingdan.dingdan_id}"
class DingdanPeihu(models.Model):
"""陪护订单表一个订单最多2个陪护"""
# 关联订单(一对一,陪护订单依附于原订单)
dingdan_id = models.CharField(max_length=32, unique=True, db_index=True, verbose_name='订单ID')
# 主打手ID冗余方便查询必须与订单的 jiedan_dashou_id 一致)
zhudashou_id = models.CharField(max_length=32, db_index=True, verbose_name='主打手ID')
# 实际接单陪护
peihu1_id = models.CharField(max_length=32, null=True, blank=True, db_index=True, verbose_name='陪护1打手ID')
peihu2_id = models.CharField(max_length=32, null=True, blank=True, db_index=True, verbose_name='陪护2打手ID')
# 订单状态1=待抢单,2=已抢待确认,3=进行中,4=已完成,5=已取消,7=指定状态...
zhuangtai = models.IntegerField(default=1, verbose_name='陪护订单状态')
# 需求配置
peihu_xuqiu_shuliang = models.IntegerField(default=1, verbose_name='陪护需求数量')
yongjin_yaoqiu = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='抢单佣金要求')
# 等级需求关联称号ID可空表示无限制
peihu1_dengji_xuqiu = models.PositiveIntegerField(null=True, blank=True, verbose_name='陪护1等级需求(称号ID)')
peihu2_dengji_xuqiu = models.PositiveIntegerField(null=True, blank=True, verbose_name='陪护2等级需求(称号ID)')
# 处罚与分红比例(格式:"1/5" 或 "2/5"
peihu1_zhadan_danze = models.CharField(max_length=10, null=True, blank=True, verbose_name='陪护1炸单担责比例')
peihu2_zhadan_danze = models.CharField(max_length=10, null=True, blank=True, verbose_name='陪护2炸单担责比例')
peihu1_fenhong = models.CharField(max_length=10, null=True, blank=True, verbose_name='陪护1分红比例')
peihu2_fenhong = models.CharField(max_length=10, null=True, blank=True, verbose_name='陪护2分红比例')
# 指定陪护可预设打手ID可为空
zhiding_peihu1 = models.CharField(max_length=32, null=True, blank=True, verbose_name='指定陪护1 ID')
zhiding_peihu2 = models.CharField(max_length=32, null=True, blank=True, verbose_name='指定陪护2 ID')
# 时间戳
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'dingdan_peihu'
verbose_name = '陪护订单'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['zhudashou_id']),
models.Index(fields=['peihu1_id']),
models.Index(fields=['peihu2_id']),
models.Index(fields=['zhuangtai']),
]
class CrossPlatformOrderData(models.Model):
"""
跨平台订单扩展数据表
存储我方订单与对方平台订单的关联及对方订单状态
"""
dingdan_id = models.CharField(max_length=32, unique=True, db_index=True, verbose_name='我方订单ID')
partner_order_id = models.CharField(max_length=32, db_index=True, verbose_name='对方平台订单ID')
partner_club_id = models.CharField(max_length=6, db_index=True, verbose_name='对方俱乐部ID')
partner_order_status = models.IntegerField(default=0, verbose_name='对方平台订单状态') # 对方订单当前状态码
partner_dashou_id = models.CharField(max_length=32, blank=True, default='', verbose_name='对方平台接单打手ID')
partner_amount = 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 = 'cross_platform_order_data'
verbose_name = '跨平台订单扩展'
verbose_name_plural = '跨平台订单扩展'
indexes = [
models.Index(fields=['dingdan_id']),
models.Index(fields=['partner_order_id']),
models.Index(fields=['partner_club_id']),
]
def __str__(self):
return f"{self.dingdan_id} -> {self.partner_order_id}"
# 建议路径dingdan/models.py 或 独立 fadan/models.py
from django.db import models
class Fadan(models.Model):
"""罚单表——平台各类处罚信息"""
id = models.AutoField(primary_key=True, verbose_name='自增主键')
beichufa_id = models.CharField(max_length=20, db_index=True, verbose_name='被处罚用户ID') # 直接用yonghuid后续可改为外键
shenqing_chufa = models.CharField(max_length=20, blank=True, null=True, verbose_name='申请处罚者ID')
shenfen = models.IntegerField(choices=[(1, '打手'), (2, '管事'), (3, '商家'), (4, '组长')], default=1, verbose_name='被处罚者身份')
chufaliyou = models.TextField(blank=True, null=True, verbose_name='处罚理由')
fakuanjine = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='罚款金额')
guanliandingdan_id = models.CharField(max_length=32, blank=True, null=True, db_index=True, verbose_name='关联订单ID')
zhuangtai = models.IntegerField(choices=[(1, '待缴纳'), (2, '已缴纳'), (3, '申诉中'), (4, '已驳回')], default=1, db_index=True, verbose_name='罚单状态')
yingxiang_qiangdan = models.IntegerField(choices=[(1, '影响抢单'), (0, '不影响抢单')], default=1, db_index=True, verbose_name='是否影响抢单')
shensuliyou = models.TextField(blank=True, null=True, verbose_name='申诉理由')
tongyi_chufaliyou = models.TextField(blank=True, null=True, verbose_name='同意处罚理由')
bohuiliyou = 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='更新时间')
# ========== 新增字段 ==========
shenqingren_fenhong_jine = models.DecimalField(
max_digits=12, decimal_places=2, default=0.00,
verbose_name='申请人分红金额'
)
# 在 Fadan 模型中添加
partner_club_id = models.CharField(max_length=6, blank=True, null=True, db_index=True, verbose_name='对方俱乐部ID')
# 在 class Fadan 中添加
shenqingren_shenfen = models.IntegerField(
choices=[(1, '客服'), (2, '售后'), (3, '管理员'),(4,'店铺'),(5,'商家')], # 按实际扩展
default=1,
verbose_name='申请处罚人身份'
)
chulizhe_shenfen = models.IntegerField(
choices=[(1, '客服'), (2, '售后'), (3, '管理员')],
default=2,
verbose_name='处理者身份'
)
chulizhe = models.CharField(max_length=20, blank=True, null=True, verbose_name='处理者')
class Meta:
db_table = 'fadan'
verbose_name = '罚单'
verbose_name_plural = '罚单'
indexes = [
models.Index(fields=['beichufa_id', 'yingxiang_qiangdan', 'zhuangtai'], name='fadan_qiangdan_check'),
models.Index(fields=['guanliandingdan_id'], name='fadan_dingdan_idx'),
]
def __str__(self):
return f"{self.beichufa_id} - 罚款{self.fakuanjine}元 - 状态{self.zhuangtai}"
# 与 Fadan 放在同一个文件或独立的 fadan/models.py
class FadanFenhong(models.Model):
"""罚款分红表——记录罚款产生的分红信息"""
id = models.AutoField(primary_key=True, verbose_name='自增主键')
fenhongzhe_id = models.CharField(max_length=20, db_index=True, verbose_name='分红者ID')
zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='分红总额')
ketixian_fenhong = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='可提现分红')
shenfen = models.IntegerField(choices=[(1, '商家'), (2, '客服'), (3, '平台管理者'),(4,'店铺'),(5, '售后')], default=1, verbose_name='分红者身份')
fenhong_cishu = models.IntegerField(default=0, verbose_name='分红次数')
# ✅ 新增字段:是否单独开启分红利率(若开启,则使用本记录的 yingde_lilv否则使用全局配置
is_individual_rate = models.BooleanField(default=False, verbose_name='是否单独开启分红利率')
yingde_lilv = models.DecimalField(max_digits=5, decimal_places=4, default=0.0000, 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 = 'fadan_fenhong'
verbose_name = '罚款分红'
verbose_name_plural = '罚款分红'
indexes = [
models.Index(fields=['fenhongzhe_id'], name='fadan_fenhong_zhe_idx'),
models.Index(fields=['shenfen'], name='fadan_fenhong_sf_idx'),
]
def __str__(self):
return f"{self.fenhongzhe_id} - 分红{self.ketixian_fenhong}"
class FadanFenhongLilv(models.Model):
shenfen = models.IntegerField(primary_key=True, choices=[(1, '客服'), (2, '售后'), (3, '管理员'), (4, '店铺'), (5, '商家')], verbose_name='申请处罚人身份')
lilv = models.DecimalField(max_digits=5, decimal_places=4, default=0.0000, 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 = 'fadan_fenhong_lilv'
verbose_name = '罚款分红费率配置'
verbose_name_plural = verbose_name
def __str__(self):
return f"身份{self.shenfen} - 费率{self.lilv}"
# ==================== 罚款申诉图片表 ====================
class FadanShensuTupian(models.Model):
"""罚款申诉图片,关联罚单"""
fadan = models.ForeignKey(Fadan, on_delete=models.CASCADE, related_name='shensu_tupians', verbose_name='关联罚单')
beichufa_id = models.CharField(max_length=7, db_index=True, verbose_name='被处罚用户ID') # 冗余,便于直接按用户查
tupian_url = models.CharField(max_length=500, verbose_name='图片相对URL')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
# 在 FadanShensuTupian 模型中添加
yongtu = models.IntegerField(
choices=[(1, '处罚证据图片'), (2, '申诉图片')],
default=2,
verbose_name='图片用途'
)
class Meta:
db_table = 'fadan_shensu_tupian'
verbose_name = '罚款申诉图片'
verbose_name_plural = '罚款申诉图片'
indexes = [
models.Index(fields=['fadan_id']),
models.Index(fields=['beichufa_id']),
]
# apps/dingdan/models.py 中添加
class PreSettlement(models.Model):
"""
预结算表
用于记录对方平台派单订单的待结算款项,等待对方确认后才能真正入账
"""
STATUS_CHOICES = (
(0, '待结算'),
(1, '已结算'),
(2, '已拒绝'),
)
# 我方订单ID
dingdan_id = models.CharField(max_length=32, db_index=True, verbose_name='我方订单ID')
# 对方订单ID
partner_order_id = models.CharField(max_length=32, db_index=True, verbose_name='对方订单ID')
# 对方俱乐部ID
partner_club_id = models.CharField(max_length=6, db_index=True, verbose_name='对方俱乐部ID')
# 接单打手ID
dashou_id = models.CharField(max_length=7, db_index=True, verbose_name='接单打手ID')
# 打手应分金额
amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='打手应分金额')
# 订单金额(原金额,可选)
order_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='订单原金额')
# 结算状态
status = models.SmallIntegerField(choices=STATUS_CHOICES, default=0, verbose_name='结算状态')
# 处理时间(结算或拒绝的时间)
process_time = models.DateTimeField(null=True, blank=True, verbose_name='处理时间')
# 处理客服/对方操作人(可选)
operator = models.CharField(max_length=32, blank=True, default='', 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 = 'pre_settlement'
verbose_name = '预结算表'
verbose_name_plural = verbose_name
# 添加这个联合唯一约束
unique_together = [['dingdan_id', 'dashou_id']]
indexes = [
models.Index(fields=['dingdan_id']),
models.Index(fields=['partner_order_id']),
models.Index(fields=['partner_club_id']),
models.Index(fields=['dashou_id']),
models.Index(fields=['status']),
]
def __str__(self):
return f"预结算-{self.dingdan_id} -> {self.partner_order_id}"
# apps/dingdan/models.py 中添加
class OrderDashouHistory(models.Model):
"""
订单打手变更历史表
记录订单每次更换打手的操作
"""
dingdan_id = models.CharField(max_length=32, db_index=True, verbose_name='订单ID')
dashou_id = models.CharField(max_length=7, db_index=True, verbose_name='打手ID')
times = models.PositiveSmallIntegerField(verbose_name='第几次接单从1开始')
operator = models.CharField(max_length=11, verbose_name='操作客服ID')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
db_table = 'order_dashou_history'
verbose_name = '订单打手变更历史'
verbose_name_plural = '订单打手变更历史'
indexes = [
models.Index(fields=['dingdan_id']),
models.Index(fields=['dashou_id']),
]
def __str__(self):
return f"{self.dingdan_id} -> {self.dashou_id}{self.times}"
class Dashoutupian(models.Model):
dingdan_id = models.CharField(max_length=32,db_index=True, null=True, verbose_name='订单ID')
dashou = models.CharField(max_length=32, null=True, blank=True,db_index=True, verbose_name='接单打手ID')
tupian = models.CharField(max_length=500, null=True, blank=True, verbose_name='打手提交图片URL')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'dashoutupian'
indexes = [
models.Index(fields=['dashou']),
models.Index(fields=['dingdan_id']),
]
class Pingfen(models.Model):
dashou = models.CharField(max_length=32, null=True, blank=True, db_index=True,verbose_name='接单打手ID')
zongfen = models.PositiveIntegerField(default=0, verbose_name='总分')
cishu = models.PositiveIntegerField(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 = 'pingfenbiao'
verbose_name = '评分表'
indexes = [
models.Index(fields=['dashou']),
]
def __str__(self):
return f"{self.dashou} - 平均分: {self.pingjun_fen():.2f}"
def pingjun_fen(self):
"""计算平均分"""
if self.cishu > 0:
return float(self.zongfen) / self.cishu
return 0.0
class Lilubiao(models.Model):
lilu = models.DecimalField(max_digits=5, decimal_places=4,null=True, verbose_name='平台分成利率1是平台,3是商家派单抽成5是打手提现抽6管事提现抽8.组长提现抽9.审核官提现抽10.商家提现抽11打手保证金提现抽12.押金分红,13.管事分红')
#sahngjialilu = models.DecimalField(max_digits=5, decimal_places=4,null=True, verbose_name='商家利率')
fadanpingtai = models.CharField(max_length=10,null=True,verbose_name='利率类型,平台分成利率1是平台订单打手分成,3是商家订单打手分成5打手提现佣金抽成6管事提现抽成8组长提现抽成')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'lilubiao'
class Chufajilu(models.Model):
dashouid = models.CharField(max_length=7, null=True, db_index=True, verbose_name='被罚打手ID')
qingqiuid = models.CharField(max_length=7, null=True, db_index=True, verbose_name='请求处罚ID')
chuliid = models.CharField(max_length=11, null=True, db_index=True, verbose_name='处理处罚ID')
cfliyou = models.TextField(null=True, blank=True, verbose_name='处罚理由')
sqzhuangtai = models.IntegerField(default=0, null=True, blank=True, verbose_name='处罚申请状态:0待处理,1成功,2驳回3申诉')
bhliyou = models.TextField(null=True, blank=True, verbose_name='驳回处罚理由')
ssliyou = models.TextField(null=True, blank=True, verbose_name='打手申诉理由')
jifen = models.IntegerField(default=0, verbose_name='扣除打手积分')
dingdan_id = models.CharField(max_length=32,null=True,db_index=True, verbose_name='订单ID')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'chufajilu'
indexes = [
models.Index(fields=['dashouid']),
models.Index(fields=['qingqiuid']),
models.Index(fields=['chuliid']),
models.Index(fields=['dingdan_id']),
]
class Chufatupian(models.Model):
dingdan_id = models.CharField(max_length=32,db_index=True, null=True, verbose_name='订单ID')
yonghuid = models.CharField(max_length=32, null=True, blank=True,db_index=True, verbose_name='用户ID')
tupian = models.CharField(max_length=500, null=True, blank=True, verbose_name='证据图片URL')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'chufatupian'
indexes = [
models.Index(fields=['yonghuid']),
models.Index(fields=['dingdan_id']),
]
class Tuikuanjilu(models.Model):
dingdan_id = models.CharField(max_length=32, unique=True, verbose_name='订单ID')
dashouid = models.CharField(max_length=7, null=True, verbose_name='接单打手ID')
qingqiuid = models.CharField(max_length=7, null=True, verbose_name='请求用户ID')
chuliid = models.CharField(max_length=7, null=True, db_index=True, verbose_name='处理人ID')
liyou = models.TextField(null=True, blank=True, verbose_name='理由')
sqzhuangtai = models.IntegerField(default=0, null=True, blank=True,db_index=True, verbose_name='申请状态:0待处理,1成功,2驳回')
bhliyou = models.TextField(null=True, blank=True, verbose_name='驳回理由')
jine = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='订单金额')
dashou_fencheng = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='打手分成')
jieshao = models.TextField(null=True, blank=True, verbose_name='订单介绍')
beizhu = models.TextField(null=True, blank=True, verbose_name='订单备注')
# 新增字段:游戏昵称(前端提交的游戏昵称)
nicheng = models.CharField(max_length=50, 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 = 'tuikuanjilu'
indexes = [
models.Index(fields=['chuliid']),
models.Index(fields=['sqzhuangtai']),
]
class Ptliushui(models.Model):
zonge = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='收取总金额')
zhichuzonge = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='支出总金额')
zongshouyi = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='总收益')
rizonge = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='日收取')
rizhichu = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='日支出')
rishouyi = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='日收益')
zongshoucishu = models.IntegerField(default=0, verbose_name='总收取次数')
rishoucishu = models.IntegerField(default=0, verbose_name='日收取次数')
zongzhichucishu = models.IntegerField(default=0, verbose_name='总支出次数')
rizhichucishu = models.IntegerField(default=0, verbose_name='日支出次数')
class Meta:
db_table = 'ptliushui'
# models.py - ChatRelation 模型
class Liaotian(models.Model):
"""聊天关系表 - 极简设计,不设置唯一约束"""
# 主键
id = models.BigAutoField(primary_key=True, verbose_name='主键ID')
# 聊天双方的用户标识GoEasy的user_id
user1_id = models.CharField(max_length=30, db_index=True, verbose_name='用户1标识')
user2_id = models.CharField(max_length=30, db_index=True, verbose_name='用户2标识')
# 关联的订单(可以为空,但实际业务中应该关联)
dingdan_id = models.CharField(max_length=32, unique=True, verbose_name='订单ID')
# 状态与平台:用整数表示,具体含义由业务逻辑定义
zhuangtai = models.IntegerField(default=1,db_index=True,choices=[(1, '开启中'), (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 = 'liaotian'
indexes = [
models.Index(fields=['user1_id']),
models.Index(fields=['user2_id']),
models.Index(fields=['zhuangtai']),
]
def __str__(self):
return f'{self.user1_id}{self.user2_id} ({self.zhuangtai})'

272
dingdan/signals.py Normal file
View File

@@ -0,0 +1,272 @@
"""
阿龙电竞 - 订单信号处理(优化版)
核心原则:绝对不影响订单提交的性能和稳定性
设计理念:订单状态变化只记录,不执行,由接口或定时任务处理具体逻辑
"""
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.conf import settings
from django.utils import timezone
import logging
import threading
import time
from dingdan.models import Dingdan
# dingdan/signals.py
"""
阿龙电竞 - 订单信号处理器(生产稳定版)
经过验证100%可靠
"""
import sys
import importlib
import logging
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.conf import settings
from django.utils import timezone
logger = logging.getLogger(__name__)
# 🔧 关键修复确保redis模块正确加载
def ensure_redis_loaded():
"""确保redis模块正确加载避免导入冲突"""
if 'redis' not in sys.modules:
# 如果redis模块未加载尝试导入
try:
import redis
#logger.debug("✅ Redis模块正常加载")
except ImportError:
#logger.warning("⚠️ Redis模块未安装尝试自动安装")
import subprocess
subprocess.call([sys.executable, "-m", "pip", "install", "redis==4.5.4", "-q"])
import redis
# 确保redis.Redis属性存在
import redis
if redis.Redis is None:
#logger.warning("⚠️ Redis.Redis属性为None强制重新加载模块")
importlib.reload(redis)
return redis
# 预加载redis模块
ensure_redis_loaded()
@receiver(pre_save, sender='dingdan.Dingdan')
def handle_order_status_8(sender, instance, **kwargs):
"""
订单状态变为8时提交定时任务
"""
# 跳过新增的订单
if instance.pk is None:
return
try:
# 动态导入,避免循环依赖
from dingdan.models import Dingdan
# 获取旧状态
old = Dingdan.objects.filter(pk=instance.pk).values_list('zhuangtai', flat=True).first()
# 状态从 非8 变为 8
if old != 8 and instance.zhuangtai == 8:
logger.info(f"📅 订单 {instance.dingdan_id} 状态变为8提交定时任务")
# 设置时间标记
instance.status_8_time = timezone.now()
instance.pending_dispatch = True
# 使用配置中的超时时间
expire_seconds = getattr(settings, 'ORDER_EXPIRE_SECONDS', 48 * 60 * 60)
# 提交Celery任务
try:
from dingdan.tasks import process_expired_order
task_result = process_expired_order.apply_async(
args=[instance.dingdan_id],
countdown=expire_seconds,
queue='order_tasks',
priority=9
)
logger.info(f"✅ 任务提交成功,订单: {instance.dingdan_id}, 任务ID: {task_result.id}")
# 保存任务信息
instance.auto_task_id = task_result.id
instance.auto_expire_at = timezone.now() + timezone.timedelta(seconds=expire_seconds)
except Exception as e:
logger.error(f"❌ 任务提交失败: {e}")
instance.pending_dispatch = True
instance.auto_task_id = f"failed_{timezone.now().timestamp()}"
# 状态从 8 变为 非8
elif old == 8 and instance.zhuangtai != 8:
instance.status_8_time = None
instance.pending_dispatch = False
instance.auto_task_id = ''
logger.info(f"📅 订单 {instance.dingdan_id} 状态离开8")
except Exception as e:
logger.error(f"信号处理失败: {e}")
logger = logging.getLogger(__name__)
'''def try_start_celery_task_async(dingdan_id, countdown):
"""
异步尝试启动Celery任务在独立线程中运行
如果Redis不可用立即失败不影响主流程
"""
def _async_task():
try:
# 设置超时时间500毫秒避免长时间等待
import socket
socket.setdefaulttimeout(0.5)
from dingdan.tasks import process_expired_order
from celery.exceptions import TimeoutError
# 尝试启动任务,设置极短的超时
task = process_expired_order.apply_async(
args=[dingdan_id],
countdown=countdown,
queue='order_tasks',
priority=9,
expires=countdown + 3600 # 任务1小时后过期避免堆积
)
# 不等待任务结果,立即返回
logger.info(f"订单 {dingdan_id} 异步任务已提交任务ID: {task.id}")
except Exception as e:
# 记录警告,但不要影响主流程
logger.warning(f"订单 {dingdan_id} 异步任务提交失败: {str(e)}")
# 这个失败不影响订单保存,系统有补偿机制
# 启动异步线程(后台运行,不阻塞主流程)
thread = threading.Thread(target=_async_task)
thread.daemon = True # 设置为守护线程,主线程退出时会自动结束
thread.start()
@receiver(pre_save, sender=Dingdan)
def handle_order_status_change_lightweight(sender, instance, **kwargs):
"""
轻量级订单状态变化监听器
设计原则:
1. 绝对不阻塞订单保存
2. 不依赖外部服务可用性
3. 只记录,不执行
"""
try:
# 1. 快速检查:如果是新建订单,直接返回
if instance.pk is None:
return
# 2. 快速获取旧状态(使用数据库当前值,不加载完整对象)
try:
old_status = Dingdan.objects.filter(pk=instance.pk).values_list('zhuangtai', flat=True).first()
except Exception:
old_status = None
# 3. 状态变化判断
if old_status is None or old_status != instance.zhuangtai:
# 状态发生变化,记录到订单扩展字段或日志(但不要阻塞)
try:
# 这里只是示例,实际可以根据需要记录状态变化历史
pass
except:
pass # 即使记录失败也不影响主流程
# 4. 特殊状态处理状态变为8
if old_status != 8 and instance.zhuangtai == 8:
# 4.1 立即记录日志(不等待)
logger.info(f"订单 {instance.dingdan_id} 状态变为8")
# 4.2 异步尝试启动定时任务(不阻塞主流程)
# 使用线程池或直接新线程,确保不阻塞
try:
# 启动异步线程尝试提交Celery任务
if hasattr(settings, 'CELERY_BROKER_URL') and settings.CELERY_BROKER_URL:
# 在独立线程中尝试,完全不阻塞
threading.Thread(
target=try_start_celery_task_async,
args=(instance.dingdan_id, settings.ORDER_EXPIRE_SECONDS),
daemon=True
).start()
except Exception as e:
# 即使线程启动失败也不影响主流程
logger.warning(f"启动异步任务线程失败: {str(e)}")
# 5. 状态从8变为其他
elif old_status == 8 and instance.zhuangtai != 8:
# 状态不再是8记录日志即可
# 定时任务执行时会检查状态如果不是8会自动跳过
logger.info(f"订单 {instance.dingdan_id} 状态从8变为{instance.zhuangtai}")
except Exception as e:
# 捕获所有异常,确保不影响订单保存
# 只记录错误,不抛出异常
logger.error(f"信号处理异常(已捕获,不影响订单): {str(e)}")'''
'''from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.conf import settings
import logging
from dingdan.models import Dingdan
from dingdan.tasks import process_expired_order
logger = logging.getLogger(__name__)
@receiver(pre_save, sender=Dingdan)
def handle_order_status_change(sender, instance, **kwargs):
"""
监听订单状态变化
当状态变为8时启动48小时延时任务
当状态从8变为其他时任务执行时会自动跳过无需取消
"""
try:
# 如果是新建订单,没有旧记录,直接返回
if instance.pk is None:
return
# 获取数据库中旧的订单记录
old_instance = Dingdan.objects.get(pk=instance.pk)
# ============ 状态从非8变为8 ============
if old_instance.zhuangtai != 8 and instance.zhuangtai == 8:
# 启动48小时后的延时任务countdown单位
task = process_expired_order.apply_async(
args=[instance.dingdan_id],
countdown=settings.ORDER_EXPIRE_SECONDS, # 172800秒 = 48小时
queue='order_tasks',
priority=9 # 高优先级
)
logger.info(
f"订单 {instance.dingdan_id} 状态变为8已启动48小时延时任务"
f"任务ID: {task.id}"
)
# 记录任务启动成功(可选,可以存储到订单表或日志表)
# ============ 状态从8变为其他 ============
# 我们不需要取消任务因为任务执行时会检查状态是否为8
# 如果不是状态8任务会自动跳过处理
except Dingdan.DoesNotExist:
# 新订单,无需处理
pass
except Exception as e:
logger.error(f"处理订单状态变化信号失败订单ID: {instance.dingdan_id if hasattr(instance, 'dingdan_id') else '未知'}, 错误: {str(e)}")
# 不抛出异常,避免影响订单保存的主流程
# 信号处理失败不影响订单保存,有周期性检查作为备用方案'''

198
dingdan/tasks.py Normal file
View File

@@ -0,0 +1,198 @@
"""
阿龙电竞 - 订单定时任务
处理订单超时自动确认等逻辑
完整版本,包含所有必要的异常处理
"""
from celery import shared_task
from django.db import transaction
from django.db.models import F, Q
from django.conf import settings
from django.utils import timezone
from datetime import timedelta
from decimal import Decimal
import logging
# 导入模型 - 确保路径正确
from dingdan.models import Dingdan, DingdanShangjia
from yonghu.models import UserMain, UserDashou, UserShangjia
from utils.celery_utils import safe_decimal_operation, log_task_execution, rollback_on_failure
logger = logging.getLogger(__name__)
@shared_task(bind=True, max_retries=3, default_retry_delay=60)
@rollback_on_failure("处理超时订单")
def process_expired_order(self, dingdan_id):
"""
处理单个超时订单(订单状态=8超过48小时未处理
这个任务在订单状态变为8的48小时后执行
:param dingdan_id: 订单ID
:return: 处理结果
"""
try:
# 使用select_for_update锁定记录防止并发修改
with transaction.atomic():
# 获取订单,同时锁定记录
order = Dingdan.objects.select_for_update().get(
dingdan_id=dingdan_id
)
# 再次检查订单状态是否为8
if order.zhuangtai != 8:
logger.info(f"订单{dingdan_id}状态已不是8当前:{order.zhuangtai}),跳过处理")
return f"订单{dingdan_id}状态已变更,跳过处理"
# 获取发单平台类型
fadan_pingtai = order.fadan_pingtai
# 获取打手ID和分成金额
dashou_id = order.jiedan_dashou_id
dashou_fencheng = safe_decimal_operation(order.dashou_fencheng)
# 1. 更新订单状态为3已完成
order.zhuangtai = 3
order.save(update_fields=['zhuangtai'])
# 2. 更新打手信息(如果存在)
updated_dashou = False
if dashou_id:
try:
# 查询打手用户主表
dashou_user = UserMain.objects.filter(yonghuid=dashou_id).first()
if dashou_user:
# 获取打手扩展表
dashou_profile = getattr(dashou_user, 'dashou_profile', None)
if dashou_profile:
# 使用F表达式原子更新
UserDashou.objects.filter(pk=dashou_profile.pk).update(
chengjiaozongliang=F('chengjiaozongliang') + 1,
yue=F('yue') + dashou_fencheng,
zonge=F('zonge') + dashou_fencheng,
jinrishouyi=F('jinrishouyi') + dashou_fencheng,
jinyueshouyi=F('jinyueshouyi') + dashou_fencheng
)
updated_dashou = True
logger.info(f"更新打手{dashou_id}信息成功")
except Exception as e:
logger.error(f"更新打手信息失败打手ID: {dashou_id}, 错误: {str(e)}")
# 继续执行,不中断流程
# 3. 如果是商家发单,更新商家信息
updated_shangjia = False
if fadan_pingtai == 2: # 商家发单
try:
# 获取商家扩展信息
shangjia_kuozhan = getattr(order, 'shangjia_kuozhan', None)
if shangjia_kuozhan:
shangjia_id = shangjia_kuozhan.shangjia_id
if shangjia_id:
# 查询商家用户
shangjia_user = UserMain.objects.filter(yonghuid=shangjia_id).first()
if shangjia_user:
shangjia_profile = getattr(shangjia_user, 'shop_profile', None)
if shangjia_profile:
# 更新商家成交订单数量
UserShangjia.objects.filter(pk=shangjia_profile.pk).update(
chengjiao=F('chengjiao') + 1
)
updated_shangjia = True
logger.info(f"更新商家{shangjia_id}信息成功")
except Exception as e:
logger.error(f"更新商家信息失败订单ID: {dingdan_id}, 错误: {str(e)}")
# 继续执行,不中断流程
# 记录任务执行结果
result_msg = f"订单{dingdan_id}自动结算完成"
if updated_dashou:
result_msg += ",打手信息已更新"
if updated_shangjia:
result_msg += ",商家信息已更新"
# 12. 更新平台收益流水(仅在平台发单时)
if fadan_pingtai == 1:
try:
from dingdan.utils import update_platform_profit # 导入函数
update_platform_profit(dingdan_id)
except Exception as e:
# 平台收益更新失败不影响主流程,但记录日志
logger.error(f"强制结单后更新平台收益失败: {str(e)}")
log_task_execution(f"自动结算订单 {dingdan_id}", True, result_msg)
return result_msg
except Dingdan.DoesNotExist:
error_msg = f"订单{dingdan_id}不存在"
logger.warning(error_msg)
return error_msg
except Exception as e:
error_msg = f"处理订单{dingdan_id}时发生错误: {str(e)}"
logger.error(error_msg)
# 重试机制
if self.request.retries < self.max_retries:
logger.info(f"订单{dingdan_id}处理失败,准备第{self.request.retries + 1}次重试")
raise self.retry(exc=e)
log_task_execution(f"自动结算订单 {dingdan_id}", False, error_msg)
return error_msg
@shared_task
@rollback_on_failure("批量检查超时订单")
def check_order_expire_task():
"""
批量检查超时订单(补偿机制)
每5分钟执行一次作为信号处理的备用方案
查找状态为8且创建时间超过72小时的订单宽松时间防止误处理
"""
try:
# 计算更宽松的时间点当前时间减去72小时48+24小时
# 这样避免误处理刚变为状态8的订单
check_time = timezone.now() - timedelta(seconds=settings.ORDER_EXPIRE_SECONDS + 86400) # 48+24=72小时
# 查询状态为8且创建时间早于检查时间的订单
expired_orders = Dingdan.objects.filter(
zhuangtai=8,
create_time__lt=check_time
).values_list('dingdan_id', flat=True)
expired_count = expired_orders.count()
if expired_count == 0:
logger.info("补偿检查:无超时订单")
return {"success": True, "message": "无超时订单", "count": 0}
# 分批处理超时订单(避免一次处理太多)
batch_size = 10
processed_count = 0
for i in range(0, expired_count, batch_size):
batch = expired_orders[i:i+batch_size]
# 异步处理每个超时订单
for dingdan_id in batch:
process_expired_order.apply_async(
args=[dingdan_id],
queue='order_tasks',
priority=8 # 补偿任务的优先级低一些
)
processed_count += 1
result_msg = f"补偿检查:发现{expired_count}个可能超时的订单,已提交处理{processed_count}"
log_task_execution("批量检查超时订单", True, result_msg)
return {
"success": True,
"message": result_msg,
"total_count": expired_count,
"processed_count": processed_count
}
except Exception as e:
error_msg = f"批量检查超时订单失败: {str(e)}"
logger.error(error_msg)
log_task_execution("批量检查超时订单", False, error_msg)
return {
"success": False,
"message": error_msg,
"error": str(e)
}

3
dingdan/tests.py Normal file
View File

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

71
dingdan/tongzhi_tasks.py Normal file
View File

@@ -0,0 +1,71 @@
# dingdan/tongzhi_tasks.py
import logging
from concurrent.futures import ThreadPoolExecutor, as_completed
from a_long_dianjing.celery_app import app
from yonghu.models import OfficialAccountUser
from utils.weixin_broadcast import WeixinBroadcastSender
logger = logging.getLogger('weixin_broadcast')
@app.task(bind=True, max_retries=3, default_retry_delay=60)
def dingdan_guangbo(self, order_info):
"""
异步并发广播订单模板消息给所有关注服务号的用户
order_info 字典字段示例:
{
'dingdan_id': 'PT123456',
'game_type': '王者荣耀',
'amount': '88.00',
'order_desc': '星耀上王者'
}
"""
sender = WeixinBroadcastSender()
template_data = sender.build_order_message_data(order_info)
order_id = order_info.get('dingdan_id')
# 查询所有已关注用户
try:
openids = list(
OfficialAccountUser.objects
.filter(is_subscribed=True)
.values_list('official_openid', flat=True)
)
except Exception as e:
logger.error(f'查询订阅用户失败: {e}')
raise self.retry(exc=e)
if not openids:
return {'success': True, 'msg': '无订阅用户'}
total = len(openids)
logger.info(f'📊 Celery 广播订单 {order_id} 开始,目标人数: {total}')
success = 0
fail = 0
# 20 个线程并发发送,微信接口耗时 200-500ms1000 人约 15 秒完成
with ThreadPoolExecutor(max_workers=20) as executor:
future_map = {
executor.submit(sender.send_to_user, openid, template_data, order_id): openid
for openid in openids
}
for future in as_completed(future_map):
openid = future_map[future]
try:
res = future.result(timeout=30)
if res.get('success'):
success += 1
else:
fail += 1
except Exception as e:
fail += 1
logger.error(f'发送异常 {openid[:10]}...: {e}')
logger.info(f'🏁 广播完成:成功 {success},失败 {fail},总计 {total}')
return {
'success': True,
'total': total,
'success_count': success,
'fail_count': fail
}

93
dingdan/urls.py Normal file
View File

@@ -0,0 +1,93 @@
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from .views import CreateOrderView, WechatPayNotifyView, \
PaymentFailView, PaymentVerifyView, DingdanHuoquView, DingdanXiangqingView, JiedanView, \
ShangpinLeixingView, ShangjiaPaifaView, ShangjiaDingdanHuoquView, ShangjiaJiesuanView, \
ShangjiaDingdanXiangqingView, ShangjiaChufaShenqingView, ShangjiaTuikuanShenqingView, \
ShangjiaChexiaoView, DashouDingdanHuoquView, ShangpinLeixingHuoquView, QiangdanView, \
DashouDingdanHuoquView1, DashouCOSZhengshuView, DashouTijiaoView, DashouDingdanXiangqingView, AdGetDingdanXiangQing, \
AdTongYiTuiKuanPingTai, AdTongYiTuiKuanShangJia, AdQiangZhiJieDan, AdJuJueTuiKuan, AdGengHuanDaShou, \
ShangjiaCOSZhengshuView, DashouHuoquLeixingView, AdJuJueJieSuan, AdZhuanYiDaTing, AdQuXiaoZhiDing, \
PartnerNotifyClaimView, PartnerSyncOrderView, PartnerCheckClaimView, PartnerRefundNotifyView, DashouXiugaiView, \
DaiLiQunLiaoXiaoXiView, VirtualCreateOrderView, VirtualPayNotifyView, VirtualPaymentVerifyView, \
VirtualPaymentFailView, ShangjiaLianjieLiuYanZhuanFaView, DingdanXiangqingView2, JiedanView2, ZxsjghdsView, \
ShangjiaFakuanApplyView, ShangjiaFakuaiXiugaiView
urlpatterns = [
path('xiadan', CreateOrderView.as_view(), name='下单订单生成接口'),
#path('pay-notify/', WechatPayNotifyView.as_view(), name='老板下单微信支付回调'),
path('pay-notify/', csrf_exempt(WechatPayNotifyView.as_view()), name='老板下单微信支付回调'),
path('shibai', PaymentFailView.as_view(), name='老板支付失败接口'),
path('fucha', PaymentVerifyView.as_view(), name='老板支付成功复查接口'),
path('dingdanhuoqu', DingdanHuoquView.as_view(), name='老板订单获取'),
path('dingdanxiangqing', DingdanXiangqingView.as_view(), name='老板订单详情获取'),
path('jiedan', JiedanView.as_view(), name='老板结单'),
path('sjspleixing', ShangpinLeixingView.as_view(), name='派单获取商品类型'),
path('sjpaifa', ShangjiaPaifaView.as_view(), name='商家发单'),
path('sjdingdanhq', ShangjiaDingdanHuoquView.as_view(), name='商家订单获取'),
path('sjddxq', ShangjiaDingdanXiangqingView.as_view(), name='商家订单详情获取'),
path('sjjiesuan', ShangjiaJiesuanView.as_view(), name='商家结单'),
path('sjchexiao', ShangjiaChexiaoView.as_view(), name='商家撤销订单'),
path('sjtkshenqing', ShangjiaTuikuanShenqingView.as_view(), name='商家退款申请'),
path('sjchufa', ShangjiaChufaShenqingView.as_view(), name='商家处罚打手申请'),
path('ddlxhq', ShangpinLeixingHuoquView.as_view(), name='打手抢单页商品类型获取'),
path('dshqdingdan', DashouDingdanHuoquView1.as_view(), name='打手订单列表页面获取'),
# 获取订单列表
path('ddhq', DashouDingdanHuoquView.as_view(), name='打手抢单页获取订单'),
path('qiangdan', QiangdanView.as_view(), name='打手抢单'),
path('dsscpz', DashouCOSZhengshuView.as_view(), name='打手上传图片OSS参数获取接口'),
# 打手提交订单完成接口
path('dstijiao', DashouTijiaoView.as_view(), name='打手订单提交'),
# 打手订单详情获取接口
path('dsddxq', DashouDingdanXiangqingView.as_view(), name='打手获取订单详情'),
path('adddxq', AdGetDingdanXiangQing.as_view(), name='管理员获取订单详情页面具体信息'),
path('adddpttk', AdTongYiTuiKuanPingTai.as_view(), name='同意平台订单退款'),
# 商家订单退款接口(之前写的)
path('adddsjtk', AdTongYiTuiKuanShangJia.as_view(), name='同意商家退款'),
# 强制结单接口
path('adddqzjd', AdQiangZhiJieDan.as_view(), name='强制结单'),
# 拒绝退款接口
path('adddjj', AdJuJueTuiKuan.as_view(), name='拒绝退款'),
# 更换打手接口
path('adddgh', AdGengHuanDaShou.as_view(), name='管理更换打手'),
path('sjscpz', ShangjiaCOSZhengshuView.as_view(), name='商家上传证据图片获取密钥'),
path('dsqdhqddlx', DashouHuoquLeixingView.as_view(), name='打手抢单页面获取订单类型'),
path('adddjjjs', AdJuJueJieSuan.as_view(), name='管理员拒绝给打手钱'),
path('adddzydt', AdZhuanYiDaTing.as_view(), name='订单转移大厅'),
path('adddqxzd', AdQuXiaoZhiDing.as_view(), name='取消指定'),
path('partner_sync_order', csrf_exempt(PartnerSyncOrderView.as_view()), name='东道主派单接口'),
path('partner_check_claim', csrf_exempt(PartnerCheckClaimView.as_view()), name='对方抢单检查接口'),
path('partner_notify_claim', csrf_exempt(PartnerNotifyClaimView.as_view()), name='东道主订单被抢通知接口'),
path('partner_refund_notify', csrf_exempt(PartnerRefundNotifyView.as_view()), name='接受东道主退款通知'),
path('dsxiugaidd', DashouXiugaiView.as_view(), name='打手修改订单详情'),
path('kptxxfs', DaiLiQunLiaoXiaoXiView.as_view(), name='前端聊天转发'), # 群聊消息代理发送
path('virtual_xiadan', VirtualCreateOrderView.as_view(),name='虚拟支付下单'),
path('virtual_notify', VirtualPayNotifyView.as_view(),name='虚拟支付下单回调'),
path('virtual_fucha', VirtualPaymentVerifyView.as_view(),name='虚拟支付下单成功复查'),
path('shibai', VirtualPaymentFailView.as_view(),name='虚拟支付下单失败'), # 或复用原 PaymentFailView
path('sjljlbxxzf', ShangjiaLianjieLiuYanZhuanFaView.as_view(), name='商家链接老板聊天转发'),
path('dingdanxiangqing2', DingdanXiangqingView2.as_view(), name='老板获取订单详情新街口'),
path('jiedan2', JiedanView2.as_view(), name='老板结单新街口'),
path('zxsjghds', ZxsjghdsView.as_view(), name='商家更换打手'), # 商家更换打手
path('sjfksq', ShangjiaFakuanApplyView.as_view(), name='商家罚款'),
path('sjfkxgycx', ShangjiaFakuaiXiugaiView.as_view(), name='商家罚款和退款修改'),
]

441
dingdan/utils.py Normal file
View File

@@ -0,0 +1,441 @@
import threading
import requests
from django.conf import settings
from peizhi.models import Club
from shangpin.models import ShangpinLeixing
from dingdan.models import Dingdan # 根据实际路径导入
import logging
from datetime import date
from django.db import transaction
from django.db.models import F
from peizhi.models import DailyDispatchStat # 假设模型在 peizhi 应用中
logger = logging.getLogger(__name__)
# 可选:线程池限制(避免大量线程同时创建,建议使用 Celery这里简单用线程
THREAD_POOL_SIZE = 10
_semaphore = threading.Semaphore(THREAD_POOL_SIZE)
def sync_order_to_partners(dingdan_id):
"""
异步向所有开启互通的合作平台同步订单。
使用信号量限制并发线程数。
"""
def _task():
_semaphore.acquire()
try:
_sync_order_task(dingdan_id)
finally:
_semaphore.release()
thread = threading.Thread(target=_task)
thread.daemon = True
thread.start()
def _sync_order_task(dingdan_id):
"""实际同步任务(在线程中执行)"""
try:
# 获取订单对象(只取必要字段,提升性能)
dingdan = Dingdan.objects.only(
'dingdan_id', 'zhuangtai', 'jine', 'dashou_fencheng', 'jieshao',
'beizhu', 'nicheng', 'leixing_id', 'yaoqiuleixing', 'huiyuan_id',
'yongjin', 'zhiding_id', 'create_time'
).get(dingdan_id=dingdan_id)
# 基本条件判断
if dingdan.zhuangtai != 1:
logger.info(f"订单 {dingdan_id} 状态 {dingdan.zhuangtai} 不是1不同步")
return
if dingdan.zhiding_id:
logger.info(f"订单 {dingdan_id} 是指定单,不同步")
return
if dingdan.yaoqiuleixing != 1:
logger.info(f"订单 {dingdan_id} 抢单要求类型不是会员抢单,不同步")
return
if not dingdan.leixing_id:
logger.info(f"订单 {dingdan_id} 无商品类型,不同步")
return
try:
leixing = ShangpinLeixing.objects.get(id=dingdan.leixing_id)
if not leixing.is_cross_enabled:
logger.info(f"订单 {dingdan_id} 的商品类型未开启互通,不同步")
return
except ShangpinLeixing.DoesNotExist:
logger.info(f"订单 {dingdan_id} 的商品类型不存在,不同步")
return
# 查询所有开启互通的俱乐部
clubs = Club.objects.filter(is_interop_enabled=True).select_related()
if not clubs.exists():
return
# 为每个俱乐部发送订单
for club in clubs:
_send_to_single_partner(dingdan, club)
except Exception as e:
logger.error(f"同步订单任务异常: {e}", exc_info=True)
def _send_to_single_partner(dingdan, club):
"""向单个俱乐部发送订单(带重试,幂等处理)"""
# 计算实际金额:订单打手分成 × 对方费率
actual_amount = dingdan.dashou_fencheng * club.dashou_rate
actual_amount -= 1
# 构建请求数据
payload = {
'token': settings.CROSS_PLATFORM_TOKEN,
'our_club_id': club.club_id, # 我方在对方系统中的标识(存储于对方表的 partner_club_id
'partner_club_id': club.partner_club_id, # 对方自己的俱乐部ID
'partner_order_id': dingdan.dingdan_id, # 我方订单ID作为对方的外部订单号
'jine': str(actual_amount), # 对方平台应展示的订单金额
'dashou_fencheng': str(actual_amount), # 对方平台打手分成(与金额一致)
'jieshao': dingdan.jieshao,
'beizhu': dingdan.beizhu,
'create_time': dingdan.create_time.isoformat(),
'nicheng': dingdan.nicheng,
'leixing_id': dingdan.leixing_id, # 我方商品类型ID对方需要映射
'user_id': get_order_user_id(dingdan), # 对方平台下单用户ID不带前缀
}
# 重试机制最多3次指数退避
for attempt in range(1, 4):
try:
response = requests.post(
club.partner_domain + '/dingdan/partner_sync_order',
json=payload,
timeout=5
)
if response.status_code == 200:
data = response.json()
# 对方返回 code=0 表示成功(包括新建或已存在)
if data.get('code') == 0:
# 判断本次响应是新创建还是已存在
status = data.get('data', {}).get('status', '')
if status == 'existed':
# 订单已经存在于对方系统,不重复统计
logger.info(f"订单 {dingdan.dingdan_id} 在俱乐部 {club.club_id} 已存在,跳过统计")
else:
# 新创建成功,更新统计
try:
update_daily_dispatch_stat(
direction=1, # 我方派单
partner_club_id=club.partner_club_id,
dispatch_amount=actual_amount,
)
logger.info(f"成功同步订单 {dingdan.dingdan_id} 到俱乐部 {club.club_id}")
except Exception as e:
logger.error(f"更新派单统计失败: {e}", exc_info=True)
# 标记订单为跨平台(仅当第一次成功时标记,避免重复标记)
if not dingdan.is_cross:
Dingdan.objects.filter(dingdan_id=dingdan.dingdan_id).update(
is_cross=1,
dispatch_type=1 # 我方派单
)
logger.info(f"订单 {dingdan.dingdan_id} 已标记为跨平台订单")
break
else:
# 对方返回业务错误(如映射不存在等),不重试
logger.error(f"同步订单失败(业务): {data}")
break
else:
logger.error(f"同步订单HTTP失败: {response.status_code}")
except Exception as e:
logger.error(f"同步订单异常(尝试 {attempt}/3: {e}")
if attempt < 3:
import time
time.sleep(2 ** attempt) # 指数退避
else:
logger.error(f"同步订单 {dingdan.dingdan_id} 到俱乐部 {club.club_id} 最终失败")
# 获取下单方ID根据订单类型
def get_order_user_id(order):
from dingdan.models import DingdanPingtai, DingdanShangjia
if order.fadan_pingtai == 1:
try:
ext = DingdanPingtai.objects.get(dingdan=order)
return ext.laoban_id # 老板ID
except DingdanPingtai.DoesNotExist:
return None
elif order.fadan_pingtai == 2:
try:
ext = DingdanShangjia.objects.get(dingdan=order)
return ext.shangjia_id # 商家ID
except DingdanShangjia.DoesNotExist:
return None
return None
# dingdan/utils/dispatch_stat.py
import logging
import logging
from datetime import date
from decimal import Decimal
from django.db import transaction
from django.db.models import F
from peizhi.models import DailyDispatchStat # 请根据实际模型位置修改导入路径
logger = logging.getLogger(__name__)
def update_daily_dispatch_stat(direction, partner_club_id,
dispatch_amount=None, dispatch_count=1,
claimed_amount=None, claimed_count=1,
success_amount=None, success_count=1):
"""
原子更新每日派单统计(支持派单、抢单、成交三种类型)。
只更新传递了非None参数的字段未传递的字段保持不变。
参数:
direction: 1=我方派单2=对方派单
partner_club_id: 对方俱乐部ID
dispatch_amount: 派单金额Decimal如果提供则累加到 dispatch_amount 和 dispatch_count
dispatch_count: 派单数量默认为1与 dispatch_amount 同时提供时生效
claimed_amount: 抢单金额Decimal如果提供则累加到 claimed_amount 和 claimed_count
claimed_count: 抢单数量默认为1与 claimed_amount 同时提供时生效
success_amount: 成交金额Decimal如果提供则累加到 success_amount 和 success_count
success_count: 成交数量默认为1与 success_amount 同时提供时生效
"""
today = date.today()
year, month, day = today.year, today.month, today.day
with transaction.atomic():
# 使用 select_for_update 锁定行,避免并发累加冲突
stat, created = DailyDispatchStat.objects.select_for_update().get_or_create(
date=today,
year=year,
month=month,
day=day,
direction=direction,
partner_club_id=partner_club_id,
defaults={
'dispatch_count': dispatch_count if dispatch_amount is not None else 0,
'dispatch_amount': dispatch_amount or Decimal('0.00'),
'claimed_count': claimed_count if claimed_amount is not None else 0,
'claimed_amount': claimed_amount or Decimal('0.00'),
'success_count': success_count if success_amount is not None else 0,
'success_amount': success_amount or Decimal('0.00'),
}
)
if not created:
update_fields = []
if dispatch_amount is not None:
stat.dispatch_count = F('dispatch_count') + dispatch_count
stat.dispatch_amount = F('dispatch_amount') + dispatch_amount
update_fields.extend(['dispatch_count', 'dispatch_amount'])
if claimed_amount is not None:
stat.claimed_count = F('claimed_count') + claimed_count
stat.claimed_amount = F('claimed_amount') + claimed_amount
update_fields.extend(['claimed_count', 'claimed_amount'])
if success_amount is not None:
stat.success_count = F('success_count') + success_count
stat.success_amount = F('success_amount') + success_amount
update_fields.extend(['success_count', 'success_amount'])
if update_fields:
stat.save(update_fields=update_fields)
logger.info(f"更新派单统计: 日期={today}, 方向={direction}, 俱乐部={partner_club_id}, "
f"派单+{dispatch_count if dispatch_amount else 0}单/{dispatch_amount or 0}, "
f"抢单+{claimed_count if claimed_amount else 0}单/{claimed_amount or 0}, "
f"成交+{success_count if success_amount else 0}单/{success_amount or 0}")
from peizhi.models import DailyIncomeStat
logger = logging.getLogger(__name__)
def update_daily_income(amount):
"""
原子更新当日收入统计金额累加笔数加1
用于微信支付成功回调。
参数:
amount: Decimal 本次收入金额
"""
today = date.today()
year, month, day = today.year, today.month, today.day
with transaction.atomic():
stat, created = DailyIncomeStat.objects.select_for_update().get_or_create(
date=today,
defaults={
'year': year,
'month': month,
'day': day,
'total_amount': amount,
'total_count': 1
}
)
if not created:
# 原子累加
stat.total_amount = F('total_amount') + amount
stat.total_count = F('total_count') + 1
stat.save(update_fields=['total_amount', 'total_count'])
logger.info(
f"每日收入更新: {today}, +{amount}元, 总金额={stat.total_amount if created else stat.total_amount + amount}, 总笔数={stat.total_count if created else stat.total_count + 1}")
from peizhi.models import DailyPayoutStat
logger = logging.getLogger(__name__)
def update_daily_payout(amount):
"""
原子更新当日出款统计金额累加笔数加1
用于提现成功、结算打款等场景。
参数:
amount: Decimal 本次出款金额
"""
today = date.today()
year, month, day = today.year, today.month, today.day
with transaction.atomic():
stat, created = DailyPayoutStat.objects.select_for_update().get_or_create(
date=today,
defaults={
'year': year,
'month': month,
'day': day,
'total_amount': amount,
'total_count': 1
}
)
if not created:
# 原子累加
stat.total_amount = F('total_amount') + amount
stat.total_count = F('total_count') + 1
stat.save(update_fields=['total_amount', 'total_count'])
#logger.info(f"每日出款更新: {today}, +{amount}元, 总金额={stat.total_amount if created else stat.total_amount + amount}, 总笔数={stat.total_count if created else stat.total_count + 1}")
from decimal import Decimal
from dingdan.models import Lilubiao
def calc_shangjia_order_fencheng(jine):
"""
商家发单时计算打手/管事分成。
打手优先:打手+管事 > 订单金额时,管事分成为 0。
"""
jine = Decimal(str(jine))
try:
lilu_obj = Lilubiao.objects.get(fadanpingtai='3')
rate_dashou = Decimal(str(lilu_obj.lilu)) if lilu_obj.lilu and lilu_obj.lilu > 0 else Decimal('1')
except Lilubiao.DoesNotExist:
rate_dashou = Decimal('1')
lilu_guanshi_obj = Lilubiao.objects.filter(fadanpingtai='13').first()
rate_guanshi = (
Decimal(str(lilu_guanshi_obj.lilu))
if lilu_guanshi_obj and lilu_guanshi_obj.lilu is not None
else Decimal('0')
)
dashou_fencheng = (jine * rate_dashou).quantize(Decimal('0.01'))
guanshi_raw = (jine * rate_guanshi).quantize(Decimal('0.01'))
if dashou_fencheng + guanshi_raw > jine:
guanshi_fencheng = Decimal('0.00')
else:
guanshi_fencheng = guanshi_raw
return dashou_fencheng, guanshi_fencheng
def settle_shangjia_order_guanshi_fenhong(order, dashou_id):
"""
商家订单结单时结算管事分红(幂等,失败不抛异常阻断主流程)。
"""
from shangpin.models import Gsfenhong
from yonghu.models import UserDashou, UserGuanshi
if getattr(order, 'fadan_pingtai', None) != 2:
return
guanshi_fencheng = order.guanshi_fencheng or Decimal('0')
if guanshi_fencheng <= 0:
return
if Gsfenhong.objects.filter(dingdan_id=order.dingdan_id).exists():
logger.info(f"商家订单管事分红已处理: {order.dingdan_id}")
return
if not dashou_id:
return
try:
dashou = UserDashou.objects.select_related('user').get(user__yonghuid=dashou_id)
except UserDashou.DoesNotExist:
logger.info(f"商家订单管事分红跳过: 打手{dashou_id}不存在")
return
guanshi_id = dashou.yaoqingren
if not guanshi_id:
logger.info(f"商家订单管事分红跳过: 打手{dashou_id}无邀请管事")
return
try:
with transaction.atomic():
guanshi = UserGuanshi.objects.select_for_update().get(user__yonghuid=guanshi_id)
UserGuanshi.objects.filter(id=guanshi.id).update(
yue=F('yue') + guanshi_fencheng,
chongzhifenrun=F('chongzhifenrun') + guanshi_fencheng,
)
user_main = dashou.user
Gsfenhong.objects.create(
dingdan_id=order.dingdan_id,
guanshi=guanshi_id,
dashouid=dashou_id,
shuoming='商家订单分红',
fenhong=guanshi_fencheng,
avatar=user_main.avatar if user_main else None,
nicheng=dashou.nicheng or '未知打手',
fenhong_leixing=3,
)
logger.info(
f"商家订单管事分红成功: 订单{order.dingdan_id}, 管事{guanshi_id}, "
f"打手{dashou_id}, 金额{guanshi_fencheng}"
)
except UserGuanshi.DoesNotExist:
logger.warning(f"商家订单管事分红跳过: 管事{guanshi_id}不存在")
return
except Exception as e:
logger.error(f"商家订单管事分红失败: {e}", exc_info=True)
return
try:
from houtai.utils import update_guanshi_daily_by_action
update_guanshi_daily_by_action(
yonghuid=guanshi_id,
action=3,
amount=guanshi_fencheng,
)
except Exception as e:
logger.error(f"商家订单管事日统计更新失败: {e}")

11602
dingdan/views.py Normal file

File diff suppressed because it is too large Load Diff