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

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

View File

@@ -1,12 +1,6 @@
""" """
阿龙电竞 - Celery定时任务主配置 阿龙电竞 - Celery定时任务主配置
生产环境就绪版本,支持精确延时任务和周期性任务
""" """
import os import os
from celery import Celery from celery import Celery
from celery.schedules import crontab from celery.schedules import crontab
@@ -26,14 +20,9 @@ app.config_from_object('django.conf:settings', namespace='CELERY')
#app.autodiscover_tasks() #app.autodiscover_tasks()
app.autodiscover_tasks(['yonghu.tasks', 'yonghu.ranking_tasks', 'dingdan.tasks', 'peizhi.tasks']) app.autodiscover_tasks(['yonghu.tasks', 'yonghu.ranking_tasks', 'dingdan.tasks', 'peizhi.tasks'])
# 配置周期性任务Celery Beat Schedule # 配置周期性任务Celery Beat Schedule
app.conf.beat_schedule = { app.conf.beat_schedule = {
# 月榜数据转移每月最后一天23:50执行 # 月榜数据转移每月最后一天23:50执行
'yuebang_zhuanyi': { 'yuebang_zhuanyi': {
'task': 'yonghu.ranking_tasks.zhuanyi_yuebang', 'task': 'yonghu.ranking_tasks.zhuanyi_yuebang',
@@ -41,8 +30,6 @@ app.conf.beat_schedule = {
'options': {'queue': 'periodic_tasks', 'priority': 7}, 'options': {'queue': 'periodic_tasks', 'priority': 7},
}, },
# 日榜数据转移每天23:55执行在清零前 # 日榜数据转移每天23:55执行在清零前
'ribang_zhuanyi': { 'ribang_zhuanyi': {
'task': 'yonghu.ranking_tasks.zhuanyi_ribang', 'task': 'yonghu.ranking_tasks.zhuanyi_ribang',
@@ -51,8 +38,6 @@ app.conf.beat_schedule = {
'options': {'queue': 'periodic_tasks', 'priority': 7}, # 优先级高于清零任务 'options': {'queue': 'periodic_tasks', 'priority': 7}, # 优先级高于清零任务
}, },
# 清理旧历史数据每月1号凌晨1点执行 # 清理旧历史数据每月1号凌晨1点执行
'qingli_jiulishuju': { 'qingli_jiulishuju': {
'task': 'yonghu.ranking_tasks.qingli_jiulishuju', 'task': 'yonghu.ranking_tasks.qingli_jiulishuju',
@@ -60,21 +45,13 @@ app.conf.beat_schedule = {
'options': {'queue': 'periodic_tasks', 'priority': 4}, 'options': {'queue': 'periodic_tasks', 'priority': 4},
}, },
# 补偿检查任务每5分钟运行
# 🔥 补偿检查任务每5分钟运行
'check_order_expire_task': { 'check_order_expire_task': {
'task': 'dingdan.tasks.check_order_expire_task', 'task': 'dingdan.tasks.check_order_expire_task',
'schedule': crontab(minute='*/5000'), 'schedule': crontab(minute='*/5000'),
'options': {'queue': 'order_tasks', 'priority': 3}, 'options': {'queue': 'order_tasks', 'priority': 3},
}, },
# 1. 每日凌晨0点执行 - 清零任务 # 1. 每日凌晨0点执行 - 清零任务
'daily_reset_task': { 'daily_reset_task': {
'task': 'yonghu.tasks.daily_reset_task', 'task': 'yonghu.tasks.daily_reset_task',
@@ -99,10 +76,6 @@ app.conf.beat_schedule = {
'kwargs': {} 'kwargs': {}
}, },
# 4. 每天凌晨0点5分清理收支记录 # 4. 每天凌晨0点5分清理收支记录
'daily_sz_reset_task': { 'daily_sz_reset_task': {
'task': 'peizhi.tasks.daily_sz_reset_task', 'task': 'peizhi.tasks.daily_sz_reset_task',

View File

@@ -7,4 +7,4 @@ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'a_long_dianjing.settings')
app = Celery('a_long_dianjing') app = Celery('a_long_dianjing')
app.config_from_object('django.conf:settings', namespace='CELERY') app.config_from_object('django.conf:settings', namespace='CELERY')
# 手动导入任务模块,避免与已有 tasks.py 冲突 # 手动导入任务模块,避免与已有 tasks.py 冲突
app.autodiscover_tasks(['dingdan.tongzhi_tasks']) app.autodiscover_tasks(['dingdan.notice_tasks'])

View File

@@ -279,7 +279,7 @@ CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Asia/Shanghai' CELERY_TIMEZONE = 'Asia/Shanghai'
CELERY_ENABLE_UTC = True CELERY_ENABLE_UTC = True
CELERY_IMPORTS = ('dingdan.tongzhi_tasks',) CELERY_IMPORTS = ('dingdan.notice_tasks',)
CELERY_TASK_TIME_LIMIT = 300 CELERY_TASK_TIME_LIMIT = 300
CELERY_TASK_SOFT_TIME_LIMIT = 240 CELERY_TASK_SOFT_TIME_LIMIT = 240
CELERY_WORKER_CONCURRENCY = 4 CELERY_WORKER_CONCURRENCY = 4

View File

@@ -2,7 +2,7 @@
#小程序弹窗序列化器 #小程序弹窗序列化器
from rest_framework import serializers from rest_framework import serializers
from peizhi.models import PopupPage, PopupConfig, PopupImage from config.models import PopupPage, PopupConfig, PopupImage
class PopupImageSerializer(serializers.ModelSerializer): class PopupImageSerializer(serializers.ModelSerializer):
"""图片序列化器(输出用)""" """图片序列化器(输出用)"""

View File

@@ -11,6 +11,8 @@ from django.db.models import F
from datetime import date from datetime import date
from decimal import Decimal from decimal import Decimal
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -217,10 +219,6 @@ def update_guanshi_daily_by_action(yonghuid, action, amount=Decimal('0.00')):
根据操作行为更新管事每日统计邀请打手充值会员订单/押金分红 根据操作行为更新管事每日统计邀请打手充值会员订单/押金分红
action: 1 = 邀请打手, 2 = 充值会员, 3 = 商家订单分红, 4 = 押金分红 action: 1 = 邀请打手, 2 = 充值会员, 3 = 商家订单分红, 4 = 押金分红
""" """
from django.db import transaction
from django.db.models import F
from datetime import date
today = date.today() today = date.today()
with transaction.atomic(): with transaction.atomic():

View File

@@ -1,74 +1,94 @@
import hmac import hmac
import threading import threading
import traceback import traceback
from calendar import monthrange
from django.contrib.auth.hashers import make_password
from django.db.models import Q, Count
from django.core.paginator import Paginator
from houtai.utils import update_dashou_daily_by_action
from dingdan.models import Lilubiao
import uuid import uuid
from rest_framework.parsers import JSONParser, MultiPartParser, FormParser
from utils.oss_utils import upload_to_oss, validate_image, delete_from_oss
from shangpin.models import Shangpin, ShangpinLeixing, ShangpinZhuanqu,Huiyuan
import hashlib import hashlib
from django.db.models import Q, Sum, F
from rest_framework import status
from houtai.models import Role, Permission, RolePermission, UserRole, TixianRiTongji
from houtai.utils import verify_kefu_permission
import xmltodict import xmltodict
from django.conf import settings
from dingdan.models import Dashoutupian, Chufajilu, PreSettlement, OrderDashouHistory
# apps/houtai/views.py 继续添加
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from houtai.models import Role
from yonghu.models import UserMain, UserGuanshi, UserBoss, UserZuzhang
from shangpin.models import DuociFenhong
import time import time
import logging import logging
import requests import requests
import os
import secrets
import random
import string
from calendar import monthrange
from decimal import Decimal
from datetime import date, datetime, timedelta from datetime import date, datetime, timedelta
from django.utils import timezone from django.utils import timezone
from django.conf import settings
from decimal import Decimal
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.paginator import Paginator
from django.db import models, transaction, IntegrityError
from django.db.models import Q, Count, Sum, F, Exists, OuterRef
from django.db.models.functions import TruncDate, TruncMonth, TruncYear
from django.contrib.auth.hashers import make_password
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.parsers import JSONParser from rest_framework import status
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.parsers import JSONParser, MultiPartParser, FormParser
from dingdan.utils import update_daily_dispatch_stat # 工具类导入
from shangpin.models import ShangpinLeixing, Huiyuangoumai, Huiyuan from utils.oss_utils import validate_image, upload_to_oss, delete_from_oss
from yonghu.models import UserMain, UserKefu, UserDashou, Xiugaijilu, UserShangjia from backend.utils import (
from peizhi.models import AccountPermission, ClubConfig, Club, DailyDispatchStat, OrderTypeMapping, TixianQuotaDefault update_dashou_daily_by_action,
from dingdan.models import Dingdan, Tuikuanjilu, CrossPlatformOrderData update_shangjia_daily
)
from orders.utils import update_daily_dispatch_stat, update_daily_payout, sync_order_to_partners
from shop.utils import update_dianpu_daily_stat
from products.utils import update_shangpin_daily_stat
from orders.notice_tasks import dingdan_guangbo
from .utils import verify_kefu_permission
# views.py # models 集中导入
import os ## backend
from peizhi.models import PopupPage, PopupConfig, PopupImage from backend.models import Role, Permission, RolePermission, UserRole, TixianRiTongji
## users
from users.models import (
UserMain, UserGuanshi, UserBoss, UserZuzhang,
UserKefu, UserDashou, Xiugaijilu, UserShangjia, UserShenheguan
)
## orders
from orders.models import (
Lilubiao, Dashoutupian, Chufajilu, PreSettlement,
OrderDashouHistory, Dingdan, Tuikuanjilu, CrossPlatformOrderData,
DingdanShangjia, Fadan, FadanShensuTupian, FadanFenhong
)
## shop
from shop.models import Dianpu, DianpuMorenPeizhi, YonghuPingzheng, ShangpinLeixingDianpu, DianpuShangpinShenheShezhi
## products
from products.models import (
Shangpin, ShangpinLeixing, ShangpinZhuanqu, Huiyuan,
DuociFenhong, Czjilu, Huiyuangoumai, Gsfenhong
)
## config
from config.models import (
WithdrawConfig, ShangjiaLianjie, AccountPermission, ClubConfig,
Club, DailyDispatchStat, OrderTypeMapping, TixianQuotaDefault,
DailyIncomeStat, DailyPayoutStat, PopupPage, PopupConfig, PopupImage
)
## rank
from rank.models import (
KaoheguanBankuai, Bankuai, Chenghao, KaoheCishuFeiyong,
YonghuChenghao, ShenheJilu
)
# 序列化器
from .serializers import PopupPageSerializer, PopupConfigSerializer, PopupImageSerializer from .serializers import PopupPageSerializer, PopupConfigSerializer, PopupImageSerializer
# 全局常量、日志对象
logger = logging.getLogger('houtai')
SHOP_PRODUCT_PERMS = ['1199ab', '1199abc', '1199abd']
logger = logging.getLogger(__name__)
class GetClubConfigView(APIView): class GetClubConfigView(APIView):
@@ -611,7 +631,6 @@ class GetCrossOrderDetailView(APIView):
# ========== 新增:查询更换打手生成的新订单信息 ========== # ========== 新增:查询更换打手生成的新订单信息 ==========
try: try:
from peizhi.models import ShangjiaLianjie
new_lianjie = ShangjiaLianjie.objects.filter( new_lianjie = ShangjiaLianjie.objects.filter(
previous_dingdan_id=dingdan_id previous_dingdan_id=dingdan_id
).order_by('-create_time').first() ).order_by('-create_time').first()
@@ -763,7 +782,6 @@ class CrossPlatformRefundView(APIView):
# ✅ 新方法(行为驱动,更安全) # ✅ 新方法(行为驱动,更安全)
from houtai.utils import update_shangjia_daily
shangjia_ext = order.shangjia_kuozhan shangjia_ext = order.shangjia_kuozhan
shangjia_id = shangjia_ext.shangjia_id shangjia_id = shangjia_ext.shangjia_id
update_shangjia_daily( update_shangjia_daily(
@@ -1034,7 +1052,6 @@ class CrossPlatformRefundView(APIView):
pass pass
# ✅ 新方法(行为驱动,更安全) # ✅ 新方法(行为驱动,更安全)
from houtai.utils import update_shangjia_daily
update_shangjia_daily( update_shangjia_daily(
yonghuid=shangjia_id, yonghuid=shangjia_id,
amount=Decimal(str(order.jine)), amount=Decimal(str(order.jine)),
@@ -1133,13 +1150,10 @@ class CrossPlatformRefundView(APIView):
try: try:
#更新支出记录 #更新支出记录
if order.fadan_pingtai == 1: if order.fadan_pingtai == 1:
from dingdan.utils import update_daily_payout
update_daily_payout(order.jine) update_daily_payout(order.jine)
# ========== 新增:商品和店铺每日统计 ========== # ========== 新增:商品和店铺每日统计 ==========
try: try:
from shangdian.utils import update_dianpu_daily_stat
from shangpin.utils import update_shangpin_daily_stat
# 商品每日统计只要有商品ID就执行 # 商品每日统计只要有商品ID就执行
if order.shangpin_id: if order.shangpin_id:
# 从扩展表获取已计算好的收益值(下单时已存储) # 从扩展表获取已计算好的收益值(下单时已存储)
@@ -1500,9 +1514,6 @@ class PartnerRefundNotifyView(APIView):
from houtai.utils import update_dashou_daily_by_action
class CrossPlatformForceCompleteView(APIView): class CrossPlatformForceCompleteView(APIView):
""" """
跨平台订单强制结单接口 跨平台订单强制结单接口
@@ -1646,8 +1657,6 @@ class CrossPlatformForceCompleteView(APIView):
# ========== 新增:商品和店铺每日统计 ========== # ========== 新增:商品和店铺每日统计 ==========
if order.fadan_pingtai == 1: if order.fadan_pingtai == 1:
try: try:
from shangdian import update_dianpu_daily_stat
from shangpin import update_shangpin_daily_stat
# 商品每日统计只要有商品ID就执行 # 商品每日统计只要有商品ID就执行
if order.shangpin_id: if order.shangpin_id:
# 从扩展表获取已计算好的收益值(下单时已存储) # 从扩展表获取已计算好的收益值(下单时已存储)
@@ -1705,8 +1714,6 @@ class CrossPlatformForceCompleteView(APIView):
# ========== 新增:商品和店铺每日统计 ========== # ========== 新增:商品和店铺每日统计 ==========
if order.fadan_pingtai == 1: if order.fadan_pingtai == 1:
try: try:
from shangdian.utils import update_dianpu_daily_stat
from shangpin.utils import update_shangpin_daily_stat
# 商品每日统计只要有商品ID就执行 # 商品每日统计只要有商品ID就执行
if order.shangpin_id: if order.shangpin_id:
# 从扩展表获取已计算好的收益值(下单时已存储) # 从扩展表获取已计算好的收益值(下单时已存储)
@@ -1803,7 +1810,6 @@ class CrossPlatformForceCompleteView(APIView):
if order.fadan_pingtai == 2: if order.fadan_pingtai == 2:
shangjia_ext = order.shangjia_kuozhan shangjia_ext = order.shangjia_kuozhan
# ✅ 新方法(行为驱动,更安全) # ✅ 新方法(行为驱动,更安全)
from houtai.utils import update_shangjia_daily
update_shangjia_daily( update_shangjia_daily(
yonghuid=shangjia_ext.shangjia_id, yonghuid=shangjia_ext.shangjia_id,
amount=Decimal(str(order.jine)), amount=Decimal(str(order.jine)),
@@ -3531,10 +3537,6 @@ class PreSettlementActionView(APIView):
# ---------- 打手每日统计(成交) ---------- # ---------- 打手每日统计(成交) ----------
try: try:
from houtai.utils import update_dashou_daily_by_action
update_dashou_daily_by_action( update_dashou_daily_by_action(
yonghuid=dashou_id, yonghuid=dashou_id,
amount=amount, amount=amount,
@@ -3562,7 +3564,6 @@ class PreSettlementActionView(APIView):
try: try:
dashou_id = pre_settlement.dashou_id dashou_id = pre_settlement.dashou_id
amount = pre_settlement.amount amount = pre_settlement.amount
from houtai.utils import update_dashou_daily_by_action
update_dashou_daily_by_action( update_dashou_daily_by_action(
yonghuid=dashou_id, yonghuid=dashou_id,
amount=amount, amount=amount,
@@ -3727,10 +3728,6 @@ class ModifyRolePermissionView(APIView):
return Response({'code': 400, 'msg': '无效的 action'}) return Response({'code': 400, 'msg': '无效的 action'})
import random
import string
from django.db import transaction
class AddRoleView(APIView): class AddRoleView(APIView):
""" """
添加角色自动生成角色编码前端提供角色名称描述选择的权限列表 添加角色自动生成角色编码前端提供角色名称描述选择的权限列表
@@ -3788,18 +3785,6 @@ class AddRoleView(APIView):
return Response({'code': 0, 'msg': '角色添加成功', 'data': {'role_code': role_code}}) return Response({'code': 0, 'msg': '角色添加成功', 'data': {'role_code': role_code}})
import logging
import traceback
from django.core.paginator import Paginator
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from django.db.models import Exists, OuterRef # 若不再使用可移除,此处保留以备后用
# 请根据项目实际路径导入 verify_kefu_permission
from .utils import verify_kefu_permission
logger = logging.getLogger(__name__)
class GetAdminUserListView(APIView): class GetAdminUserListView(APIView):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
@@ -5840,7 +5825,6 @@ class DeleteProductView(APIView):
tupian_url = product.tupian_url tupian_url = product.tupian_url
if tupian_url: if tupian_url:
# 调用已有的 delete_from_oss 函数 # 调用已有的 delete_from_oss 函数
from utils.oss_utils import delete_from_oss
delete_success = delete_from_oss(tupian_url) delete_success = delete_from_oss(tupian_url)
if not delete_success: if not delete_success:
logger.warning(f"删除商品图片失败: {tupian_url}, 商品ID: {product_id}") logger.warning(f"删除商品图片失败: {tupian_url}, 商品ID: {product_id}")
@@ -6230,10 +6214,6 @@ class UpdateMemberView(APIView):
import random
import string
class AddMemberView(APIView): class AddMemberView(APIView):
""" """
添加会员 添加会员
@@ -6836,7 +6816,6 @@ class GetWithdrawSettingsView(APIView):
today_totals[rec.leixing] = float(rec.total_amount) today_totals[rec.leixing] = float(rec.total_amount)
# 🆕 获取提现模式自动1 / 手动2默认2 # 🆕 获取提现模式自动1 / 手动2默认2
from peizhi.models import WithdrawConfig # 确保导入模型
config = WithdrawConfig.objects.filter(id=1).first() config = WithdrawConfig.objects.filter(id=1).first()
withdraw_mode = config.mode if config else 2 withdraw_mode = config.mode if config else 2
@@ -6983,7 +6962,6 @@ class UpdateWithdrawSettingsView(APIView):
obj.save() obj.save()
# 🆕 修改提现模式有5500a/b/c任一即可 # 🆕 修改提现模式有5500a/b/c任一即可
from peizhi.models import WithdrawConfig
withdraw_mode = request.data.get('withdraw_mode') withdraw_mode = request.data.get('withdraw_mode')
if withdraw_mode is not None: if withdraw_mode is not None:
if not any(p in permissions for p in ('5500a', '5500b', '5500c')): if not any(p in permissions for p in ('5500a', '5500b', '5500c')):
@@ -7545,343 +7523,6 @@ class GetProductTypeZoneView(APIView):
'''class ModifyProductTypeZoneView(APIView):
"""
统一修改商品类型/专区
权限需要拥有 7007a
请求POST /houtai/htxgsplx
支持 multipart/form-data application/json
参数通用
username: 客服账号
type: 'leixing' 'zhuanqu'
action: 'add' / 'update' / 'delete'
类型专用参数
- add/update: id (update时必填), jieshao, tupian_url(可不上传), file(图片文件), yaoqiuleixing, huiyuan_id, yongjin, shenhezhuangtai, is_cross_enabled, sync_to_products (可选)
- delete: id, delete_products (布尔)
专区专用参数
- add/update: id (update时必填), mingzi, leixing_id, shenhezhuangtai
- delete: id, delete_products (布尔)
"""
permission_classes = [IsAuthenticated]
parser_classes = [JSONParser, MultiPartParser, FormParser]
def post(self, request):
username = request.data.get('username', '').strip()
obj_type = request.data.get('type') # 'leixing' 或 'zhuanqu'
action = request.data.get('action') # 'add', 'update', 'delete'
if not username or not obj_type or not action:
return Response({'code': 400, 'msg': '缺少必要参数'})
# 权限校验
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return permissions
if '7007a' not in permissions:
return Response({'code': 403, 'msg': '您无权进行此操作'})
if obj_type == 'leixing':
return self.handle_leixing(request, action)
elif obj_type == 'zhuanqu':
return self.handle_zhuanqu(request, action)
else:
return Response({'code': 400, 'msg': '无效的type参数'})
# ---------- 商品类型处理 ----------
def handle_leixing(self, request, action):
if action == 'add':
return self.add_leixing(request)
elif action == 'update':
return self.update_leixing(request)
elif action == 'delete':
return self.delete_leixing(request)
else:
return Response({'code': 400, 'msg': '无效的action'})
def add_leixing(self, request):
# 获取表单字段
jieshao = request.data.get('jieshao', '').strip()
yaoqiuleixing = request.data.get('yaoqiuleixing')
huiyuan_id = request.data.get('huiyuan_id', '').strip() or None
yongjin = request.data.get('yongjin')
shenhezhuangtai = request.data.get('shenhezhuangtai', 1)
is_cross_enabled = request.data.get('is_cross_enabled', False)
image_file = request.FILES.get('file') # 上传的图片文件
if not jieshao:
return Response({'code': 400, 'msg': '类型名称不能为空'})
if yaoqiuleixing not in [1, 2]:
return Response({'code': 400, 'msg': '抢单要求类型无效'})
if not image_file:
return Response({'code': 400, 'msg': '请上传类型图片'})
# 校验图片
try:
from utils.oss_utils import validate_image
valid, msg = validate_image(image_file)
if not valid:
return Response({'code': 400, 'msg': msg})
except ImportError:
pass
# 上传图片到OSS
ext = image_file.name.split('.')[-1].lower()
new_filename = f"{uuid.uuid4().hex}.{ext}"
oss_path = f"a_long/shangpin/shangpinleixing/{new_filename}"
url = upload_to_oss(image_file, oss_path)
if not url:
return Response({'code': 500, 'msg': '图片上传失败'})
# 创建类型
try:
with transaction.atomic():
leixing = ShangpinLeixing.objects.create(
jieshao=jieshao,
tupian_url=oss_path, # 存储相对路径
yaoqiuleixing=yaoqiuleixing,
huiyuan_id=huiyuan_id,
yongjin=Decimal(yongjin) if yongjin else None,
shenhezhuangtai=int(shenhezhuangtai),
is_cross_enabled=is_cross_enabled in [True, 'true', '1'],
paixu=0 # 新添加的类型排序默认0前端可再调整
)
return Response({'code': 0, 'msg': '添加成功', 'data': {'id': leixing.id}})
except Exception as e:
logger.error(f"添加类型失败: {e}", exc_info=True)
# 删除已上传的图片
delete_from_oss(oss_path)
return Response({'code': 500, 'msg': '数据库错误'})
def update_leixing(self, request):
type_id = request.data.get('id')
if not type_id:
return Response({'code': 400, 'msg': '缺少类型ID'})
try:
leixing = ShangpinLeixing.objects.get(id=type_id)
except ShangpinLeixing.DoesNotExist:
return Response({'code': 404, 'msg': '类型不存在'})
# 收集修改字段
changes = {}
jieshao = request.data.get('jieshao')
if jieshao is not None and jieshao != leixing.jieshao:
changes['jieshao'] = jieshao.strip()
yaoqiuleixing = request.data.get('yaoqiuleixing')
if yaoqiuleixing is not None and int(yaoqiuleixing) != leixing.yaoqiuleixing:
changes['yaoqiuleixing'] = int(yaoqiuleixing)
huiyuan_id = request.data.get('huiyuan_id', '').strip() or None
if huiyuan_id != leixing.huiyuan_id:
changes['huiyuan_id'] = huiyuan_id
yongjin = request.data.get('yongjin')
if yongjin is not None:
new_yongjin = Decimal(yongjin) if yongjin else None
if new_yongjin != leixing.yongjin:
changes['yongjin'] = new_yongjin
shenhezhuangtai = request.data.get('shenhezhuangtai')
if shenhezhuangtai is not None and int(shenhezhuangtai) != leixing.shenhezhuangtai:
changes['shenhezhuangtai'] = int(shenhezhuangtai)
is_cross = request.data.get('is_cross_enabled')
if is_cross is not None:
new_cross = is_cross in [True, 'true', '1']
if new_cross != leixing.is_cross_enabled:
changes['is_cross_enabled'] = new_cross
# 图片处理
image_file = request.FILES.get('file')
old_image_path = leixing.tupian_url
if image_file:
# 验证并上传新图片
try:
from utils.oss_utils import validate_image
valid, msg = validate_image(image_file)
if not valid:
return Response({'code': 400, 'msg': msg})
except ImportError:
pass
ext = image_file.name.split('.')[-1].lower()
new_filename = f"{uuid.uuid4().hex}.{ext}"
oss_path = f"a_long/shangpin/shangpinleixing/{new_filename}"
url = upload_to_oss(image_file, oss_path)
if not url:
return Response({'code': 500, 'msg': '图片上传失败'})
changes['tupian_url'] = oss_path
if not changes and not image_file:
return Response({'code': 400, 'msg': '未做任何修改'})
# 执行更新
sync_to_products = request.data.get('sync_to_products') in [True, 'true', '1']
try:
with transaction.atomic():
# 更新类型
for field, value in changes.items():
setattr(leixing, field, value)
leixing.save()
# 如果修改了抢单要求相关字段且需要同步商品
if sync_to_products and ('yaoqiuleixing' in changes or 'huiyuan_id' in changes or 'yongjin' in changes):
# 更新所有关联此类型的商品
product_updates = {}
if 'yaoqiuleixing' in changes:
product_updates['yaoqiuleixing'] = changes['yaoqiuleixing']
if 'huiyuan_id' in changes:
product_updates['huiyuan_id'] = changes['huiyuan_id']
if 'yongjin' in changes:
product_updates['yongjin'] = changes['yongjin']
Shangpin.objects.filter(leixing_id=type_id).update(**product_updates)
# 删除旧图片(新图片上传成功后才删除)
if image_file and old_image_path:
delete_from_oss(old_image_path)
return Response({'code': 0, 'msg': '修改成功'})
except Exception as e:
logger.error(f"更新类型失败: {e}", exc_info=True)
# 如果上传了新图片,尝试删除
if image_file and 'oss_path' in locals():
delete_from_oss(oss_path)
return Response({'code': 500, 'msg': '修改失败'})
def delete_leixing(self, request):
type_id = request.data.get('id')
if not type_id:
return Response({'code': 400, 'msg': '缺少类型ID'})
delete_products = request.data.get('delete_products') in [True, 'true', '1']
try:
leixing = ShangpinLeixing.objects.get(id=type_id)
except ShangpinLeixing.DoesNotExist:
return Response({'code': 404, 'msg': '类型不存在'})
with transaction.atomic():
# 删除该类型下的所有专区
zones = ShangpinZhuanqu.objects.filter(leixing_id=type_id)
if delete_products:
# 删除专区下的所有商品
for zone in zones:
Shangpin.objects.filter(zhuanqu_id=zone.id).delete()
# 删除所有专区
zones.delete()
# 删除该类型下的所有商品(可能还有直接关联类型但无专区的商品)
Shangpin.objects.filter(leixing_id=type_id).delete()
else:
# 只删除专区,不删除商品
zones.delete()
# 商品保持原样leixing_id 保留,但类型已删除,可能导致数据不一致,按需求不处理)
# 也可以将商品的 leixing_id 置空
Shangpin.objects.filter(leixing_id=type_id).update(leixing_id=None)
# 删除类型本身
leixing.delete()
# 删除类型图片
if leixing.tupian_url:
delete_from_oss(leixing.tupian_url)
return Response({'code': 0, 'msg': '删除成功'})
# ---------- 商品专区处理 ----------
def handle_zhuanqu(self, request, action):
if action == 'add':
return self.add_zhuanqu(request)
elif action == 'update':
return self.update_zhuanqu(request)
elif action == 'delete':
return self.delete_zhuanqu(request)
else:
return Response({'code': 400, 'msg': '无效的action'})
def add_zhuanqu(self, request):
mingzi = request.data.get('mingzi', '').strip()
leixing_id = request.data.get('leixing_id')
shenhezhuangtai = request.data.get('shenhezhuangtai', 1)
if not mingzi:
return Response({'code': 400, 'msg': '专区名称不能为空'})
if not leixing_id:
return Response({'code': 400, 'msg': '请选择所属类型'})
# 校验类型是否存在
if not ShangpinLeixing.objects.filter(id=leixing_id).exists():
return Response({'code': 404, 'msg': '所属类型不存在'})
try:
with transaction.atomic():
zhuanqu = ShangpinZhuanqu.objects.create(
mingzi=mingzi,
leixing_id=int(leixing_id),
shenhezhuangtai=int(shenhezhuangtai),
paixu=0
)
return Response({'code': 0, 'msg': '添加成功', 'data': {'id': zhuanqu.id}})
except Exception as e:
logger.error(f"添加专区失败: {e}", exc_info=True)
return Response({'code': 500, 'msg': '数据库错误'})
def update_zhuanqu(self, request):
zone_id = request.data.get('id')
if not zone_id:
return Response({'code': 400, 'msg': '缺少专区ID'})
try:
zhuanqu = ShangpinZhuanqu.objects.get(id=zone_id)
except ShangpinZhuanqu.DoesNotExist:
return Response({'code': 404, 'msg': '专区不存在'})
changes = {}
mingzi = request.data.get('mingzi')
if mingzi is not None and mingzi != zhuanqu.mingzi:
changes['mingzi'] = mingzi.strip()
leixing_id = request.data.get('leixing_id')
if leixing_id is not None and int(leixing_id) != zhuanqu.leixing_id:
# 校验新类型存在
if not ShangpinLeixing.objects.filter(id=leixing_id).exists():
return Response({'code': 404, 'msg': '新所属类型不存在'})
changes['leixing_id'] = int(leixing_id)
shenhezhuangtai = request.data.get('shenhezhuangtai')
if shenhezhuangtai is not None and int(shenhezhuangtai) != zhuanqu.shenhezhuangtai:
changes['shenhezhuangtai'] = int(shenhezhuangtai)
if not changes:
return Response({'code': 400, 'msg': '未做任何修改'})
try:
with transaction.atomic():
for field, value in changes.items():
setattr(zhuanqu, field, value)
zhuanqu.save()
return Response({'code': 0, 'msg': '修改成功'})
except Exception as e:
logger.error(f"更新专区失败: {e}", exc_info=True)
return Response({'code': 500, 'msg': '修改失败'})
def delete_zhuanqu(self, request):
zone_id = request.data.get('id')
if not zone_id:
return Response({'code': 400, 'msg': '缺少专区ID'})
delete_products = request.data.get('delete_products') in [True, 'true', '1']
try:
zhuanqu = ShangpinZhuanqu.objects.get(id=zone_id)
except ShangpinZhuanqu.DoesNotExist:
return Response({'code': 404, 'msg': '专区不存在'})
with transaction.atomic():
if delete_products:
Shangpin.objects.filter(zhuanqu_id=zone_id).delete()
else:
Shangpin.objects.filter(zhuanqu_id=zone_id).update(zhuanqu_id=None)
zhuanqu.delete()
return Response({'code': 0, 'msg': '删除成功'})'''
class ModifyProductTypeZoneView(APIView): class ModifyProductTypeZoneView(APIView):
""" """
统一修改商品类型/专区 统一修改商品类型/专区
@@ -7964,7 +7605,6 @@ class ModifyProductTypeZoneView(APIView):
# 验证图片 # 验证图片
try: try:
from utils.oss_utils import validate_image
valid, msg = validate_image(image_file) valid, msg = validate_image(image_file)
if not valid: if not valid:
return Response({'code': 400, 'msg': msg}) return Response({'code': 400, 'msg': msg})
@@ -8067,7 +7707,6 @@ class ModifyProductTypeZoneView(APIView):
old_image_path = leixing.tupian_url old_image_path = leixing.tupian_url
if image_file: if image_file:
try: try:
from utils.oss_utils import validate_image
valid, msg = validate_image(image_file) valid, msg = validate_image(image_file)
if not valid: if not valid:
return Response({'code': 400, 'msg': msg}) return Response({'code': 400, 'msg': msg})
@@ -8286,7 +7925,6 @@ class GetRateView(APIView):
if '7007b' not in permissions: if '7007b' not in permissions:
return Response({'code': 403, 'msg': '您无权查看费率'}) return Response({'code': 403, 'msg': '您无权查看费率'})
from dingdan.models import Lilubiao
try: try:
platform_rate_obj = Lilubiao.objects.get(fadanpingtai='1') platform_rate_obj = Lilubiao.objects.get(fadanpingtai='1')
merchant_rate_obj = Lilubiao.objects.get(fadanpingtai='3') merchant_rate_obj = Lilubiao.objects.get(fadanpingtai='3')
@@ -8438,7 +8076,6 @@ class PopupNoticeModifyAPIView(APIView):
return Response({'code': 400, 'msg': f'未知的action: {action}'}) return Response({'code': 400, 'msg': f'未知的action: {action}'})
except Exception as e: except Exception as e:
# 捕获未预期的异常,返回服务器错误 # 捕获未预期的异常,返回服务器错误
import traceback
traceback.print_exc() traceback.print_exc()
return Response({'code': 500, 'msg': f'服务器错误: {str(e)}'}) return Response({'code': 500, 'msg': f'服务器错误: {str(e)}'})
@@ -8695,8 +8332,6 @@ class PopupNoticeModifyAPIView(APIView):
from shangdian.models import Dianpu, DianpuMorenPeizhi, YonghuPingzheng
class ShopListView(APIView): class ShopListView(APIView):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
@@ -8792,7 +8427,7 @@ class ShopListView(APIView):
} }
}) })
from django.db import transaction, IntegrityError
class ShopModifyView(APIView): class ShopModifyView(APIView):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
@@ -8958,25 +8593,7 @@ class ShopModifyView(APIView):
# houtai/views.py (完整新增接口,可直接追加到文件末尾)
import logging
from django.db import models # Q 查询必须导入
from django.core.paginator import Paginator
from django.db import transaction
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from shangpin.models import ShangpinLeixing # 公共商品类型
from shangdian.models import (
Dianpu, ShangpinLeixingDianpu,
DianpuShangpinShenheShezhi, YonghuPingzheng
)
from .utils import verify_kefu_permission # 已提供的公共身份校验方法
logger = logging.getLogger(__name__)
# 店铺商品管理所需权限组(拥有任意一个即可访问列表类接口)
SHOP_PRODUCT_PERMS = ['1199ab', '1199abc', '1199abd']
# ==================== 1. 公共商品类型 + 审核模式 ==================== # ==================== 1. 公共商品类型 + 审核模式 ====================
@@ -9574,11 +9191,7 @@ class KefuChatPermissionsView(APIView):
from rest_framework.permissions import AllowAny
from dingdan.models import Dingdan, Fadan, CrossPlatformOrderData # 按实际路径
class FineApplyView(APIView): class FineApplyView(APIView):
""" """
客服申请罚款接口金额罚款非积分 客服申请罚款接口金额罚款非积分
@@ -9799,7 +9412,7 @@ class PartnerFineNotifyView(APIView):
from dingdan.models import Fadan, FadanShensuTupian
# 权限码到身份的映射 # 权限码到身份的映射
PERMISSION_TO_SHENFEN = { PERMISSION_TO_SHENFEN = {
'66693a': 1, # 打手 '66693a': 1, # 打手
@@ -10156,7 +9769,7 @@ class FaKuanChuangJianView(APIView):
from dengji.models import Bankuai
@@ -10366,19 +9979,7 @@ class BkxgView(APIView):
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework.response import Response
from django.db.models import Sum
from django.utils import timezone
from datetime import date, timedelta
from peizhi.models import (
DailyIncomeStat, DailyPayoutStat
)
from shangpin.models import (
Czjilu,Huiyuangoumai
)
from .utils import verify_kefu_permission
@@ -10558,8 +10159,7 @@ class CwhybkhqView(APIView):
logger.exception("CwhybkhqView 接口错误") logger.exception("CwhybkhqView 接口错误")
return Response({'code': 1, 'msg': f'服务器内部错误: {str(e)}'}, status=500) return Response({'code': 1, 'msg': f'服务器内部错误: {str(e)}'}, status=500)
from django.db.models.functions import TruncDate, TruncMonth, TruncYear
from shangpin.models import Czjilu, Gsfenhong # 请根据实际路径调整
class HybkjtsjView(APIView): class HybkjtsjView(APIView):
""" """
会员详细统计数据 会员详细统计数据
@@ -10599,7 +10199,6 @@ class HybkjtsjView(APIView):
return Response({'code': 1, 'msg': '缺少会员ID'}, status=400) return Response({'code': 1, 'msg': '缺少会员ID'}, status=400)
# 🔧 直接使用 date.today() 避免模块名冲突 # 🔧 直接使用 date.today() 避免模块名冲突
from datetime import date, datetime, timedelta
now = date.today() now = date.today()
# ---------- 构造时间范围 ---------- # ---------- 构造时间范围 ----------
@@ -10789,8 +10388,6 @@ class SzxxView(APIView):
def post(self, request): def post(self, request):
try: try:
# 避免模块名冲突,局部导入 # 避免模块名冲突,局部导入
from datetime import date, datetime
username_from_frontend = request.data.get('phone', None) username_from_frontend = request.data.get('phone', None)
kefu_obj, permissions = verify_kefu_permission(request, username_from_frontend) kefu_obj, permissions = verify_kefu_permission(request, username_from_frontend)
if kefu_obj is None: if kefu_obj is None:
@@ -11148,16 +10745,6 @@ class CwhqjtddsjView(APIView):
from dingdan.models import Fadan, FadanFenhong # 罚单和罚款分红表,根据实际路径调整
import logging
from datetime import date, datetime
from django.db.models import Count, Sum, Q
from django.db.models.functions import TruncDate, TruncMonth, TruncYear
class CwqtczhqView(APIView): class CwqtczhqView(APIView):
""" """
其他充值/罚款数据统计非会员充值 其他充值/罚款数据统计非会员充值
@@ -11660,11 +11247,6 @@ class CwqtczhqView(APIView):
# ==================== 后端接口 ==================== # ==================== 后端接口 ====================
# 文件views.py (添加以下两个视图类) # 文件views.py (添加以下两个视图类)
from dengji.models import (
Chenghao, KaoheCishuFeiyong, Bankuai,
YonghuChenghao, ShenheJilu, # 可能不需要用到但导入无妨
)
class KhpzhqView(APIView): class KhpzhqView(APIView):
@@ -11895,13 +11477,7 @@ class ChzsgcView(APIView):
# ==================== 后端 views.py ==================== # ==================== 后端 views.py ====================
# 请确保已有以下导入,如果没有请在文件头部添加 # 请确保已有以下导入,如果没有请在文件头部添加
from yonghu.models import (
UserMain, UserShenheguan
)
from dengji.models import (
KaoheguanBankuai, Bankuai
)
class KhgglView(APIView): class KhgglView(APIView):
@@ -12128,13 +11704,6 @@ class ShgxgsjView(APIView):
import secrets
from dingdan.models import Dingdan, DingdanShangjia
from dingdan.tongzhi_tasks import dingdan_guangbo
from peizhi.models import ShangjiaLianjie
class ZxkfghdsView(APIView): class ZxkfghdsView(APIView):
""" """
客服更换打手接口 客服更换打手接口
@@ -12294,7 +11863,6 @@ class ZxkfghdsView(APIView):
return Response({'code': 500, 'msg': '通知对方平台失败'}) return Response({'code': 500, 'msg': '通知对方平台失败'})
# 更新跨平台扩展表(如果存在) # 更新跨平台扩展表(如果存在)
from dingdan.models import CrossPlatformOrderData
CrossPlatformOrderData.objects.update_or_create( CrossPlatformOrderData.objects.update_or_create(
dingdan_id=dingdan_id, dingdan_id=dingdan_id,
defaults={ defaults={
@@ -12391,7 +11959,6 @@ class ZxkfghdsView(APIView):
# 跨平台同步(与客户填写接口保持一致) # 跨平台同步(与客户填写接口保持一致)
try: try:
from dingdan.utils import sync_order_to_partners
sync_order_to_partners(new_dingdan_id) sync_order_to_partners(new_dingdan_id)
except Exception as e: except Exception as e:
logger.error(f"跨平台同步失败: {e}") logger.error(f"跨平台同步失败: {e}")
@@ -12416,7 +11983,6 @@ class ZxkfghdsView(APIView):
def _get_game_type_name(self, leixing_id): def _get_game_type_name(self, leixing_id):
"""根据类型ID获取游戏类型名称""" """根据类型ID获取游戏类型名称"""
try: try:
from shangpin.models import ShangpinLeixing
gt = ShangpinLeixing.objects.filter(id=leixing_id).first() gt = ShangpinLeixing.objects.filter(id=leixing_id).first()
return gt.jieshao if gt else '游戏订单' return gt.jieshao if gt else '游戏订单'
except Exception: except Exception:
@@ -12427,18 +11993,7 @@ class ZxkfghdsView(APIView):
import logging
from django.conf import settings
from django.db.models import ObjectDoesNotExist
from rest_framework import status
from rest_framework.parsers import JSONParser
from rest_framework.response import Response
from rest_framework.views import APIView
from dingdan.models import Dingdan
logger = logging.getLogger('houtai')
class KptxwztjbView(APIView): class KptxwztjbView(APIView):

View File

@@ -1,5 +1,7 @@
from django.db import models from django.db import models
class Lunbo(models.Model): class Lunbo(models.Model):
tupian_url = models.CharField(max_length=500, null=True, blank=True, verbose_name='轮播图片URL') tupian_url = models.CharField(max_length=500, null=True, blank=True, verbose_name='轮播图片URL')
leixing = models.IntegerField(null=True, blank=True, verbose_name='图片类型:1轮播2个人中心背景') leixing = models.IntegerField(null=True, blank=True, verbose_name='图片类型:1轮播2个人中心背景')
@@ -66,9 +68,6 @@ class Szjilu(models.Model):
# models/shangjia_moban.py
from django.db import models
class ShangjiaMoban(models.Model): class ShangjiaMoban(models.Model):
"""商家订单模板表 - 存储商家创建的订单模板""" """商家订单模板表 - 存储商家创建的订单模板"""
@@ -119,8 +118,6 @@ class ShangjiaMoban(models.Model):
return f"模板{self.id}-{self.yonghu_id}" return f"模板{self.id}-{self.yonghu_id}"
# models/shangjia_lianjie.py
from django.db import models
class ShangjiaLianjie(models.Model): class ShangjiaLianjie(models.Model):
@@ -274,10 +271,6 @@ class WithdrawConfig(models.Model):
# apps/club/models.py
from django.db import models
class Club(models.Model): class Club(models.Model):
"""合作俱乐部/平台信息表""" """合作俱乐部/平台信息表"""
# 自己的俱乐部ID6位字符 # 自己的俱乐部ID6位字符
@@ -384,8 +377,6 @@ class ClubConfig(models.Model):
return f"{self.club_nickname or self.club_id} ({'我方' if self.is_self else '对方'})" return f"{self.club_nickname or self.club_id} ({'我方' if self.is_self else '对方'})"
from django.db import models
class AccountPermission(models.Model): class AccountPermission(models.Model):
"""账号权限表:记录每个账号拥有的权限及开关状态""" """账号权限表:记录每个账号拥有的权限及开关状态"""
account_id = models.CharField(max_length=11, db_index=True, verbose_name='账号ID') account_id = models.CharField(max_length=11, db_index=True, verbose_name='账号ID')
@@ -521,10 +512,6 @@ class DailyPayoutStat(models.Model):
from django.db import models
class PopupPage(models.Model): class PopupPage(models.Model):
page_key = models.CharField(max_length=50, unique=True, db_index=True, verbose_name='页面标识') page_key = models.CharField(max_length=50, unique=True, db_index=True, verbose_name='页面标识')
name = models.CharField(max_length=100, verbose_name='页面名称') name = models.CharField(max_length=100, verbose_name='页面名称')

View File

@@ -3,7 +3,6 @@
处理收支记录表的每日清零 处理收支记录表的每日清零
固定使用ID=1的记录 固定使用ID=1的记录
""" """
from celery import shared_task from celery import shared_task
from django.db import transaction from django.db import transaction
from django.utils import timezone from django.utils import timezone
@@ -12,7 +11,7 @@ from decimal import Decimal
import logging import logging
# 导入模型 # 导入模型
from peizhi.models import Szjilu from config.models import Szjilu
from utils.celery_utils import log_task_execution, rollback_on_failure from utils.celery_utils import log_task_execution, rollback_on_failure
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

File diff suppressed because it is too large Load Diff

View File

@@ -1,614 +0,0 @@
"""
阿龙电竞 - 信号处理器修复Redis导入问题
"""
# 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}")
# 🔥 关键修复在最开始强制正确导入redis模块
'''import sys
# 1. 如果redis模块已加载但有问题先移除
if 'redis' in sys.modules:
print(f"🔧 移除已加载的redis模块: {sys.modules['redis']}")
del sys.modules['redis']
# 同时移除相关模块
for key in list(sys.modules.keys()):
if key.startswith('redis.') or key == 'redis':
del sys.modules[key]
# 2. 重新导入redis模块
try:
import importlib
redis = importlib.import_module('redis')
print(f"✅ 重新导入redis成功: {redis}")
print(f" Redis类: {redis.Redis}")
print(f" Redis类是否为None: {redis.Redis is None}")
# 验证Redis类是否可用
if redis.Redis is None:
print("❌ Redis类为None尝试从源码级别修复")
# 强制重新加载
import importlib.util
import os
# 找到redis包路径
import pkgutil
redis_spec = pkgutil.find_loader('redis')
if redis_spec:
print(f" Redis包路径: {redis_spec.origin}")
# 强制重新加载
importlib.invalidate_caches()
redis = importlib.import_module('redis')
importlib.reload(redis)
print(f" Redis类重载后: {redis.Redis}")
except Exception as e:
print(f"❌ 导入redis失败: {e}")
import traceback
traceback.print_exc()
# 尝试直接安装
print("尝试直接安装redis包...")
import subprocess
subprocess.call([sys.executable, "-m", "pip", "install", "redis==4.5.4", "-q"])
# 3. 确保kombu.transport.redis也能正确导入
try:
import kombu.transport.redis
print(f"✅ kombu.transport.redis导入成功")
except Exception as e:
print(f"❌ kombu.transport.redis导入失败: {e}")
# 修复先导入redis再导入kombu.transport.redis
if 'redis' not in sys.modules:
import redis
# 重新导入
import importlib
if 'kombu.transport.redis' in sys.modules:
del sys.modules['kombu.transport.redis']
kombu_transport_redis = importlib.import_module('kombu.transport.redis')
print(f"✅ 重新导入kombu.transport.redis成功")
# 4. 继续原来的信号处理器代码
import logging
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils import timezone
logger = logging.getLogger(__name__)
@receiver(pre_save, sender='dingdan.Dingdan')
def order_status_8_handler_fixed(sender, instance, **kwargs):
"""
修复Redis导入问题后的信号处理器
"""
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 = 60 # 测试用60秒
# 🔥 再次验证redis模块
try:
import redis
logger.info(f"🔍 信号处理器内redis模块状态: {redis.Redis}")
if redis.Redis is None:
logger.error("❌ 信号处理器内redis.Redis为None!")
# 使用备用方案
instance.auto_task_id = f"redis_none_{timezone.now().timestamp()}"
return
except Exception as e:
logger.error(f"❌ 信号处理器内验证redis失败: {e}")
instance.auto_task_id = f"redis_check_failed_{timezone.now().timestamp()}"
return
# 提交Celery任务
try:
from dingdan.tasks import process_expired_order
logger.info(f"🔧 开始提交任务,订单: {instance.dingdan_id}")
# 使用更安全的方式提交任务
from celery import current_app
# 验证current_app的backend
if hasattr(current_app, 'backend'):
logger.info(f"✅ current_app.backend存在: {current_app.backend}")
else:
logger.error("❌ current_app没有backend属性")
task_result = process_expired_order.apply_async(
args=[instance.dingdan_id],
countdown=expire_seconds,
queue='order_tasks',
priority=9
)
logger.info(f"✅ Celery任务提交成功订单: {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"❌ Celery任务提交失败: {type(e).__name__}: {e}")
# 特别检查Redis相关错误
if "'NoneType' object has no attribute 'Redis'" in str(e):
logger.error("💥 检测到Redis类为None错误")
logger.error("💥 尝试从当前环境重新导入redis模块")
# 尝试修复
try:
import importlib
import redis as redis_module
importlib.reload(redis_module)
logger.info(f"💥 重新加载redis模块后: {redis_module.Redis}")
except Exception as reload_e:
logger.error(f"💥 重新加载失败: {reload_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"订单状态变更处理失败订单ID: {getattr(instance, 'dingdan_id', '未知')}, 错误: {e}")'''
'''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
logger = logging.getLogger(__name__)
# 在你的 dingdan/tasks.py 文件末尾添加以下代码
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils import timezone
import logging
from dingdan.models import Dingdan
logger = logging.getLogger(__name__)
@receiver(pre_save, sender=Dingdan)
def handle_order_status_change(sender, instance, **kwargs):
"""
订单状态变化监听器(纯净标记版)
严格遵循原始逻辑,仅更新数据库标记。
"""
# 1. 跳过新建的订单
if instance.pk is None:
return
# 2. 获取数据库中此订单的旧状态
try:
old_status = Dingdan.objects.filter(pk=instance.pk).values_list('zhuangtai', flat=True).first()
except Dingdan.DoesNotExist:
return # 旧订单不存在,不处理
except Exception as e:
logger.error(f"获取订单 {instance.pk} 旧状态失败: {e},跳过信号处理")
return # 出错时静默退出,绝不阻塞订单保存
old_status = old_status or 0 # 防None
# 3. 【核心逻辑】状态从 非8 变为 8
if old_status != 8 and instance.zhuangtai == 8:
instance.pending_dispatch = True # 标记为待处理
instance.status_8_time = timezone.now() # 记录变8时间
# 清空可能存在的旧任务信息(安全起见)
instance.auto_task_id = ''
instance.auto_expire_at = None
logger.info(f"📝 订单 {instance.dingdan_id} 状态变8已标记为待调度。")
# 4. 【核心逻辑】状态从 8 变为 非8
elif old_status == 8 and instance.zhuangtai != 8:
# 清除所有调度标记和任务信息
instance.pending_dispatch = False
instance.status_8_time = None
instance.auto_task_id = ''
instance.auto_expire_at = None
logger.info(f"📝 订单 {instance.dingdan_id} 状态离开8已清除所有调度标记。")'''
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils import timezone
from datetime import timedelta
from celery.result import AsyncResult
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):
"""
监听订单状态变化(稳定生产版)- 修复配置问题
"""
# 1. 跳过新建的订单(无旧状态可比)
if instance.pk is None:
return
try:
old = sender.objects.get(pk=instance.pk)
except sender.DoesNotExist:
return
# dingdan/signals.py - 修改状态变8的逻辑部分
# 2. 状态变成 8提交任务
if old.zhuangtai != 8 and instance.zhuangtai == 8:
from django.conf import settings
from celery import current_app
# 🔥【核心修复】确保信号上下文中的Celery应用有正确配置
# 直接从你的Django settings中获取Redis连接字符串并设置给current_app
if hasattr(settings, 'CELERY_BROKER_URL'):
current_app.conf.update(broker_url=settings.CELERY_BROKER_URL)
else:
# 如果settings里没有就按你的配置拼接和celery.py里一致
redis_password = getattr(settings, 'REDIS_PASSWORD', 'Dujieduze5.3')
redis_host = getattr(settings, 'REDIS_HOST', '172.19.0.3')
redis_port = getattr(settings, 'REDIS_PORT', 6379)
redis_db_celery = getattr(settings, 'REDIS_DB_CELERY', 0)
broker_url = f'redis://:{redis_password}@{redis_host}:{redis_port}/{redis_db_celery}'
current_app.conf.update(broker_url=broker_url)
countdown_seconds = getattr(settings, 'ORDER_EXPIRE_SECONDS', 172800) # 默认48小时
try:
# 现在再提交任务
job = process_expired_order.apply_async(
args=[instance.dingdan_id],
countdown=countdown_seconds,
queue='order_tasks',
priority=9
)
# ✅ 核心将任务ID和计划时间记录到当前实例
instance.auto_task_id = job.id
instance.auto_expire_at = timezone.now() + timedelta(seconds=countdown_seconds)
logger.info(f"✅ 订单 {instance.dingdan_id} 状态变8已提交延时任务ID: {job.id}")
except Exception as e:
logger.error(f"❌ 提交订单 {instance.dingdan_id} 的延时任务失败: {str(e)}")
return # 处理完毕,直接返回
# 3. 状态从 8 被改成别的 -> 尝试撤销任务 (这部分代码不变)
if old.zhuangtai == 8 and instance.zhuangtai != 8:
if old.auto_task_id:
try:
AsyncResult(old.auto_task_id).revoke()
logger.info(f"订单 {instance.dingdan_id} 状态离开8已撤销原任务: {old.auto_task_id}")
except Exception as e:
logger.warning(f"撤销订单 {instance.dingdan_id} 的任务失败: {str(e)}")
instance.auto_task_id = ''
instance.auto_expire_at = None'''
'''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):
print(f"🚨【信号触发】订单ID: {getattr(instance, 'dingdan_id', 'N/A')}, 新状态: {instance.zhuangtai}")
"""
轻量级订单状态变化监听器
设计原则:
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)}")
# 不抛出异常,避免影响订单保存的主流程
# 信号处理失败不影响订单保存,有周期性检查作为备用方案'''

View File

@@ -1,28 +1,28 @@
# /opt/1panel/apps/openresty/nginx/www/sites/43.142.166.152.443/django/gunicorn.conf.py # /opt/1panel/apps/openresty/nginx/www/sites/43.142.166.152.443/django/gunicorn.conf.py
# 🔥 绑定的IP和端口根据你的Nginx配置后端在8001端口 # 绑定的IP和端口根据你的Nginx配置后端在8001端口
bind = "127.0.0.1:8001" bind = "127.0.0.1:8001"
# 🔥 工作进程数根据CPU核心数调整建议CPU核心数*2+1 # 工作进程数根据CPU核心数调整建议CPU核心数*2+1
# 查看CPU核心数cat /proc/cpuinfo | grep "processor" | wc -l # 查看CPU核心数cat /proc/cpuinfo | grep "processor" | wc -l
workers = 4 workers = 4
# 🔥 工作模式使用gevent支持异步更高效 # 工作模式使用gevent支持异步更高效
#worker_class = "gevent" #worker_class = "gevent"
worker_class = "sync" # 改成这个 worker_class = "sync" # 改成这个
# 🔥 每个worker的最大请求数防止内存泄漏 # 每个worker的最大请求数防止内存泄漏
max_requests = 1000 max_requests = 1000
max_requests_jitter = 50 # 随机抖动避免所有worker同时重启 max_requests_jitter = 50 # 随机抖动避免所有worker同时重启
# 🔥 超时时间(秒) # 超时时间(秒)
timeout = 30 timeout = 30
keepalive = 2 keepalive = 2
# 🔥 预加载应用(减少内存占用,加快请求处理) # 预加载应用(减少内存占用,加快请求处理)
preload_app = True preload_app = True
# 🔥 日志配置 # 日志配置
# 访问日志输出到标准输出便于OnePanel查看 # 访问日志输出到标准输出便于OnePanel查看
accesslog = "-" accesslog = "-"
# 错误日志:输出到标准错误 # 错误日志:输出到标准错误
@@ -30,23 +30,23 @@ errorlog = "-"
# 日志级别 # 日志级别
loglevel = "info" loglevel = "info"
# 🔥 进程名称(便于识别) # 进程名称(便于识别)
proc_name = "a_long_dianjing_gunicorn" proc_name = "a_long_dianjing_gunicorn"
# 🔥 工作进程的用户/组安全考虑用非root用户运行 # 工作进程的用户/组安全考虑用非root用户运行
# user = "www-data" # user = "www-data"
# group = "www-data" # group = "www-data"
# 🔥 工作进程临时目录(避免权限问题) # 工作进程临时目录(避免权限问题)
worker_tmp_dir = "/dev/shm" worker_tmp_dir = "/dev/shm"
# 🔥 限制请求数据大小(防止大文件攻击) # 限制请求数据大小(防止大文件攻击)
limit_request_line = 4094 limit_request_line = 4094
limit_request_fields = 100 limit_request_fields = 100
limit_request_field_size = 8190 limit_request_field_size = 8190
# 🔥 优雅退出时间 # 优雅退出时间
graceful_timeout = 30 graceful_timeout = 30
# 🔥 进程守护如果用systemd管理这里设为False # 进程守护如果用systemd管理这里设为False
daemon = False daemon = False

View File

@@ -7,4 +7,4 @@ class DingdanConfig(AppConfig):
def ready(self): def ready(self):
# 导入信号处理,确保信号被注册 # 导入信号处理,确保信号被注册
import dingdan.signals import orders.signals

View File

@@ -1,5 +1,6 @@
from django.db import models from django.db import models
class Dingdan(models.Model): class Dingdan(models.Model):
"""订单主表""" """订单主表"""
# 核心编号:唯一且不可为空[citation:3][citation:9] # 核心编号:唯一且不可为空[citation:3][citation:9]

View File

@@ -1,8 +1,8 @@
# dingdan/tongzhi_tasks.py # dingdan/notice_tasks.py
import logging import logging
from concurrent.futures import ThreadPoolExecutor, as_completed from concurrent.futures import ThreadPoolExecutor, as_completed
from a_long_dianjing.celery_app import app from a_long_dianjing.celery_app import app
from yonghu.models import OfficialAccountUser from users.models import OfficialAccountUser
from utils.weixin_broadcast import WeixinBroadcastSender from utils.weixin_broadcast import WeixinBroadcastSender
logger = logging.getLogger('weixin_broadcast') logger = logging.getLogger('weixin_broadcast')

102
orders/signals.py Normal file
View File

@@ -0,0 +1,102 @@
"""
阿龙电竞 - 订单信号处理器(生产稳定版)
经过验证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 orders.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 orders.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}")

View File

@@ -14,8 +14,8 @@ from decimal import Decimal
import logging import logging
# 导入模型 - 确保路径正确 # 导入模型 - 确保路径正确
from dingdan.models import Dingdan, DingdanShangjia from orders.models import Dingdan
from yonghu.models import UserMain, UserDashou, UserShangjia from users.models import UserMain, UserDashou, UserShangjia
from utils.celery_utils import safe_decimal_operation, log_task_execution, rollback_on_failure from utils.celery_utils import safe_decimal_operation, log_task_execution, rollback_on_failure
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -25,9 +25,6 @@ logger = logging.getLogger(__name__)
@shared_task(bind=True, max_retries=3, default_retry_delay=60) @shared_task(bind=True, max_retries=3, default_retry_delay=60)
def process_expired_order(self, dingdan_id): def process_expired_order(self, dingdan_id):
""" """
@@ -44,13 +41,6 @@ def process_expired_order(self, dingdan_id):
dingdan_id=dingdan_id dingdan_id=dingdan_id
) )
# 其余代码不变...
# ... 保持原有代码
# 再次检查订单状态是否为8 # 再次检查订单状态是否为8
if order.zhuangtai != 8: if order.zhuangtai != 8:
logger.info(f"订单{dingdan_id}状态已不是8当前:{order.zhuangtai}),跳过处理") logger.info(f"订单{dingdan_id}状态已不是8当前:{order.zhuangtai}),跳过处理")
@@ -124,7 +114,7 @@ def process_expired_order(self, dingdan_id):
if fadan_pingtai == 1: if fadan_pingtai == 1:
try: try:
from dingdan.utils import update_platform_profit # 导入函数 from orders.utils import update_platform_profit # 导入函数
update_platform_profit(dingdan_id) update_platform_profit(dingdan_id)
except Exception as e: except Exception as e:
# 平台收益更新失败不影响主流程,但记录日志 # 平台收益更新失败不影响主流程,但记录日志

View File

@@ -65,8 +65,6 @@ urlpatterns = [
path('partner_sync_order', csrf_exempt(PartnerSyncOrderView.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_check_claim', csrf_exempt(PartnerCheckClaimView.as_view()), name='对方抢单询问接口'),
@@ -86,6 +84,4 @@ urlpatterns = [
] ]

View File

@@ -1,21 +1,25 @@
import threading import threading
import requests import requests
import time
from django.conf import settings
from peizhi.models import Club
from shangpin.models import ShangpinLeixing
from dingdan.models import Dingdan # 根据实际路径导入
import logging import logging
from datetime import date from datetime import date
from decimal import Decimal
from django.conf import settings
from django.db import transaction from django.db import transaction
from django.db.models import F from django.db.models import F
from peizhi.models import DailyDispatchStat # 假设模型在 peizhi 应用中
from config.models import Club, DailyDispatchStat, DailyIncomeStat, DailyPayoutStat
from orders.models import Dingdan, DingdanPingtai, DingdanShangjia, Lilubiao
from products.models import ShangpinLeixing, Gsfenhong
from users.models import UserDashou, UserGuanshi
from backend.utils import update_guanshi_daily_by_action
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# 可选:线程池限制(避免大量线程同时创建,建议使用 Celery这里简单用线程 # 可选:线程池限制(避免大量线程同时创建,建议使用 Celery这里简单用线程
THREAD_POOL_SIZE = 10 THREAD_POOL_SIZE = 10
_semaphore = threading.Semaphore(THREAD_POOL_SIZE) _semaphore = threading.Semaphore(THREAD_POOL_SIZE)
@@ -149,7 +153,6 @@ def _send_to_single_partner(dingdan, club):
logger.error(f"同步订单异常(尝试 {attempt}/3: {e}") logger.error(f"同步订单异常(尝试 {attempt}/3: {e}")
if attempt < 3: if attempt < 3:
import time
time.sleep(2 ** attempt) # 指数退避 time.sleep(2 ** attempt) # 指数退避
else: else:
logger.error(f"同步订单 {dingdan.dingdan_id} 到俱乐部 {club.club_id} 最终失败") logger.error(f"同步订单 {dingdan.dingdan_id} 到俱乐部 {club.club_id} 最终失败")
@@ -158,7 +161,6 @@ def _send_to_single_partner(dingdan, club):
# 获取下单方ID根据订单类型 # 获取下单方ID根据订单类型
def get_order_user_id(order): def get_order_user_id(order):
from dingdan.models import DingdanPingtai, DingdanShangjia
if order.fadan_pingtai == 1: if order.fadan_pingtai == 1:
try: try:
ext = DingdanPingtai.objects.get(dingdan=order) ext = DingdanPingtai.objects.get(dingdan=order)
@@ -176,18 +178,6 @@ def get_order_user_id(order):
# 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, def update_daily_dispatch_stat(direction, partner_club_id,
dispatch_amount=None, dispatch_count=1, dispatch_amount=None, dispatch_count=1,
@@ -255,9 +245,6 @@ def update_daily_dispatch_stat(direction, partner_club_id,
from peizhi.models import DailyIncomeStat
logger = logging.getLogger(__name__)
def update_daily_income(amount): def update_daily_income(amount):
@@ -295,9 +282,6 @@ def update_daily_income(amount):
from peizhi.models import DailyPayoutStat
logger = logging.getLogger(__name__)
def update_daily_payout(amount): def update_daily_payout(amount):
""" """
@@ -335,10 +319,6 @@ def update_daily_payout(amount):
from decimal import Decimal
from dingdan.models import Lilubiao
def calc_shangjia_order_fencheng(jine): def calc_shangjia_order_fencheng(jine):
""" """
@@ -374,8 +354,6 @@ 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: if getattr(order, 'fadan_pingtai', None) != 2:
return return
@@ -433,7 +411,6 @@ def settle_shangjia_order_guanshi_fenhong(order, dashou_id):
return return
try: try:
from houtai.utils import update_guanshi_daily_by_action
update_guanshi_daily_by_action( update_guanshi_daily_by_action(
yonghuid=guanshi_id, yonghuid=guanshi_id,
action=3, action=3,

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More