修复管事双分红与押金分红:移除错误冲突校验、优化分成计算与建聊补偿
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -38,6 +38,7 @@ def process_expired_order(self, dingdan_id):
|
||||
return f'自动结算已禁用,跳过订单{dingdan_id}'
|
||||
|
||||
try:
|
||||
guanshi_settle_ctx = None
|
||||
# 使用select_for_update锁定记录,防止并发修改
|
||||
with transaction.atomic():
|
||||
|
||||
@@ -125,7 +126,19 @@ def process_expired_order(self, dingdan_id):
|
||||
result_msg += ",商家信息已更新"
|
||||
|
||||
log_task_execution(f"自动结算订单 {dingdan_id}", True, result_msg)
|
||||
return result_msg
|
||||
|
||||
if fadan_pingtai == 2 and dashou_id:
|
||||
guanshi_settle_ctx = (dingdan_id, dashou_id)
|
||||
|
||||
if guanshi_settle_ctx:
|
||||
try:
|
||||
from dingdan.utils import settle_shangjia_order_guanshi_fenhong
|
||||
settle_order = Dingdan.objects.get(dingdan_id=guanshi_settle_ctx[0])
|
||||
settle_shangjia_order_guanshi_fenhong(settle_order, guanshi_settle_ctx[1])
|
||||
except Exception as e:
|
||||
logger.error(f"自动结算管事分红失败 订单{dingdan_id}: {e}", exc_info=True)
|
||||
|
||||
return result_msg
|
||||
|
||||
except Dingdan.DoesNotExist:
|
||||
error_msg = f"订单{dingdan_id}不存在"
|
||||
@@ -143,6 +156,27 @@ def process_expired_order(self, dingdan_id):
|
||||
log_task_execution(f"自动结算订单 {dingdan_id}", False, error_msg)
|
||||
return error_msg
|
||||
|
||||
|
||||
@shared_task(bind=True, max_retries=3, default_retry_delay=15)
|
||||
def retry_establish_order_chat_task(self, dingdan_id):
|
||||
"""抢单后建聊失败时的延迟补偿任务"""
|
||||
try:
|
||||
from utils.chat_utils import establish_order_chat_with_retry
|
||||
ok = establish_order_chat_with_retry(dingdan_id, max_rounds=5)
|
||||
if ok:
|
||||
logger.info(f"建聊补偿成功: 订单{dingdan_id}")
|
||||
return f'chat ok {dingdan_id}'
|
||||
logger.warning(f"建聊补偿仍失败: 订单{dingdan_id}")
|
||||
if self.request.retries < self.max_retries:
|
||||
raise self.retry()
|
||||
return f'chat failed {dingdan_id}'
|
||||
except Exception as e:
|
||||
logger.error(f"建聊补偿任务异常 订单{dingdan_id}: {e}", exc_info=True)
|
||||
if self.request.retries < self.max_retries:
|
||||
raise self.retry(exc=e)
|
||||
return str(e)
|
||||
|
||||
|
||||
@shared_task
|
||||
@rollback_on_failure("批量检查超时订单")
|
||||
def check_order_expire_task():
|
||||
|
||||
110
dingdan/utils.py
110
dingdan/utils.py
@@ -356,10 +356,10 @@ def _normalize_lilu_rate(raw):
|
||||
def calc_shangjia_order_fencheng(jine):
|
||||
"""
|
||||
商家发单时计算打手/管事分成。
|
||||
- 打手分成(Lilubiao=3)最高不超过订单金额
|
||||
- 打手分成(Lilubiao=3)
|
||||
- 管事打手接单分红(Lilubiao=13)
|
||||
- 管事商家派单分红(Lilubiao=15)
|
||||
打手优先;三项合计超过订单金额时,优先清零管事商家分红,再清零管事打手分红。
|
||||
三项合计超过订单金额时,优先削减打手分成,再削减商家派单管事分红,最后削减打手接单管事分红。
|
||||
"""
|
||||
jine = Decimal(str(jine))
|
||||
try:
|
||||
@@ -387,14 +387,19 @@ def calc_shangjia_order_fencheng(jine):
|
||||
guanshi_fencheng = (jine * rate_guanshi_dashou).quantize(Decimal('0.01'))
|
||||
guanshi_shangjia_fencheng = (jine * rate_guanshi_shangjia).quantize(Decimal('0.01'))
|
||||
|
||||
if dashou_fencheng + guanshi_fencheng > jine:
|
||||
guanshi_fencheng = Decimal('0.00')
|
||||
|
||||
if dashou_fencheng + guanshi_fencheng + guanshi_shangjia_fencheng > jine:
|
||||
guanshi_shangjia_fencheng = Decimal('0.00')
|
||||
|
||||
if dashou_fencheng + guanshi_fencheng + guanshi_shangjia_fencheng > jine:
|
||||
guanshi_fencheng = Decimal('0.00')
|
||||
total = dashou_fencheng + guanshi_fencheng + guanshi_shangjia_fencheng
|
||||
if total > jine:
|
||||
overflow = total - jine
|
||||
cut = min(overflow, dashou_fencheng)
|
||||
dashou_fencheng -= cut
|
||||
overflow -= cut
|
||||
if overflow > 0:
|
||||
cut = min(overflow, guanshi_shangjia_fencheng)
|
||||
guanshi_shangjia_fencheng -= cut
|
||||
overflow -= cut
|
||||
if overflow > 0:
|
||||
cut = min(overflow, guanshi_fencheng)
|
||||
guanshi_fencheng -= cut
|
||||
|
||||
return dashou_fencheng, guanshi_fencheng, guanshi_shangjia_fencheng
|
||||
|
||||
@@ -413,17 +418,6 @@ def _pay_guanshi_order_fenhong(order, guanshi_id, amount, related_user_id, niche
|
||||
logger.info(f"管事分红已处理: 订单{order.dingdan_id}, 类型{fenhong_leixing}")
|
||||
return False
|
||||
|
||||
# 旧表结构 dingdan_id 全局唯一时,第二笔分红会插入失败
|
||||
legacy_conflict = Gsfenhong.objects.filter(dingdan_id=order.dingdan_id).exclude(
|
||||
fenhong_leixing=fenhong_leixing
|
||||
).exists()
|
||||
if legacy_conflict:
|
||||
logger.error(
|
||||
f"管事分红写入失败: 订单{order.dingdan_id} 已有其他类型分红记录,"
|
||||
f"请执行 python manage.py migrate shangpin 0010 更新 gsfenhong 表唯一约束"
|
||||
)
|
||||
return False
|
||||
|
||||
try:
|
||||
with transaction.atomic():
|
||||
guanshi = UserGuanshi.objects.select_for_update().get(user__yonghuid=guanshi_id)
|
||||
@@ -569,4 +563,76 @@ def settle_shangjia_order_guanshi_fenhong(order, dashou_id):
|
||||
else:
|
||||
logger.info(
|
||||
f"管事商家分红跳过: 商家{shangjia_id}打手身份无邀请管事(yaoqingren为空)"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def pay_deposit_guanshi_fenhong(dashou, order, recharge_amount):
|
||||
"""
|
||||
打手充值押金后向邀请管事发放分红(幂等:dingdan_id + fenhong_leixing=2)。
|
||||
网络重试/微信重复回调时不会重复入账。
|
||||
"""
|
||||
from shangpin.models import Gsfenhong
|
||||
from yonghu.models import UserGuanshi
|
||||
|
||||
guanshi_id = (dashou.yaoqingren or '').strip()
|
||||
if not guanshi_id:
|
||||
logger.info(f"押金分红跳过: 打手{dashou.user.yonghuid}无邀请管事")
|
||||
return False
|
||||
|
||||
if Gsfenhong.objects.filter(dingdan_id=order.dingdan_id, fenhong_leixing=2).exists():
|
||||
logger.info(f"押金分红已处理: 订单{order.dingdan_id}")
|
||||
return True
|
||||
|
||||
lilu_obj = Lilubiao.objects.filter(fadanpingtai='12').first()
|
||||
rate = lilu_obj.lilu if lilu_obj and lilu_obj.lilu is not None else Decimal('0')
|
||||
rate = _normalize_lilu_rate(rate) if rate else Decimal('0')
|
||||
if rate <= 0:
|
||||
logger.info("押金分红跳过: 费率未配置或为0")
|
||||
return False
|
||||
|
||||
recharge_amount = Decimal(str(recharge_amount))
|
||||
fenhong_jine = (recharge_amount * rate).quantize(Decimal('0.01'))
|
||||
if fenhong_jine <= 0:
|
||||
return False
|
||||
|
||||
try:
|
||||
with transaction.atomic():
|
||||
guanshi = UserGuanshi.objects.select_for_update().get(user__yonghuid=guanshi_id)
|
||||
UserGuanshi.objects.filter(pk=guanshi.pk).update(
|
||||
yue=F('yue') + fenhong_jine,
|
||||
chongzhifenrun=F('chongzhifenrun') + fenhong_jine,
|
||||
)
|
||||
user_main = dashou.user
|
||||
Gsfenhong.objects.create(
|
||||
dingdan_id=order.dingdan_id,
|
||||
guanshi=guanshi_id,
|
||||
dashouid=dashou.user.yonghuid,
|
||||
shuoming='押金分红',
|
||||
fenhong=fenhong_jine,
|
||||
avatar=user_main.avatar if user_main else None,
|
||||
nicheng=dashou.nicheng or '未知打手',
|
||||
fenhong_leixing=2,
|
||||
)
|
||||
logger.info(
|
||||
f"押金分红成功: 订单{order.dingdan_id}, 管事{guanshi_id}, "
|
||||
f"打手{dashou.user.yonghuid}, 金额{fenhong_jine}元"
|
||||
)
|
||||
try:
|
||||
from houtai.utils import update_guanshi_daily_by_action
|
||||
update_guanshi_daily_by_action(
|
||||
yonghuid=guanshi_id,
|
||||
action=4,
|
||||
amount=fenhong_jine,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"押金分红管事日统计更新失败: {e}")
|
||||
return True
|
||||
except UserGuanshi.DoesNotExist:
|
||||
logger.warning(f"押金分红跳过: 管事{guanshi_id}不存在")
|
||||
return False
|
||||
except IntegrityError:
|
||||
logger.info(f"押金分红已处理(并发): 订单{order.dingdan_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"押金分红处理失败: {e}", exc_info=True)
|
||||
return False
|
||||
@@ -4235,7 +4235,15 @@ class QiangdanView(APIView):
|
||||
self._execute_qiangdan(order, dashou_profile, request.user.yonghuid)
|
||||
|
||||
# 事务提交后再建群/建聊,避免长耗时 HTTP 拖住行锁,并支持重试
|
||||
chat_success = establish_order_chat_with_retry(dingdan_id)
|
||||
chat_success = establish_order_chat_with_retry(dingdan_id, max_rounds=5)
|
||||
|
||||
if not chat_success:
|
||||
try:
|
||||
from dingdan.tasks import retry_establish_order_chat_task
|
||||
retry_establish_order_chat_task.apply_async(args=[dingdan_id], countdown=8)
|
||||
retry_establish_order_chat_task.apply_async(args=[dingdan_id], countdown=30)
|
||||
except Exception as chat_task_err:
|
||||
logger.error(f"提交建聊补偿任务失败: {chat_task_err}")
|
||||
|
||||
try:
|
||||
update_dashou_daily_by_action(
|
||||
|
||||
@@ -680,7 +680,14 @@ class YajinHuitiao(View):
|
||||
# 5. 检查订单状态是否为未支付(9)
|
||||
if order.zhuangtai != 9:
|
||||
logger.warning(f"订单状态不是未支付: {order.dingdan_id}, 当前状态: {order.zhuangtai}")
|
||||
# 已处理过的订单直接返回成功,避免微信重复回调
|
||||
# 已支付订单:补发可能因网络中断未成功的押金分红(幂等)
|
||||
if order.zhuangtai == 3 and getattr(order, 'leixing', None) == 5:
|
||||
try:
|
||||
dashou = UserDashou.objects.get(user__yonghuid=order.yonghuid)
|
||||
from dingdan.utils import pay_deposit_guanshi_fenhong
|
||||
pay_deposit_guanshi_fenhong(dashou, order, order.jine)
|
||||
except Exception as e:
|
||||
logger.error(f"押金分红补发失败: {order.dingdan_id}, {e}", exc_info=True)
|
||||
return self.wechat_response('SUCCESS', 'OK')
|
||||
|
||||
# 6. 验证金额(微信返回的是分,需要转换)
|
||||
@@ -709,62 +716,10 @@ class YajinHuitiao(View):
|
||||
|
||||
logger.info(f"打手押金更新成功: 用户{order.yonghuid}, 增加{order_amount_yuan}元, 新押金{dashou.yajin}")
|
||||
|
||||
# 8b. 押金分红(仅微信确认到账后;费率 Lilubiao fadanpingtai=12)
|
||||
# 8b. 押金分红(微信确认到账后;幂等、可重试)
|
||||
try:
|
||||
from decimal import Decimal
|
||||
from django.db import transaction
|
||||
from dingdan.models import Lilubiao
|
||||
from shangpin.models import Gsfenhong
|
||||
from yonghu.models import UserGuanshi
|
||||
|
||||
guanshi_id = dashou.yaoqingren
|
||||
if not guanshi_id:
|
||||
logger.info(f"押金分红跳过: 打手{order.yonghuid}无邀请管事")
|
||||
elif Gsfenhong.objects.filter(dingdan_id=order.dingdan_id).exists():
|
||||
logger.info(f"押金分红已处理: 订单{order.dingdan_id}")
|
||||
else:
|
||||
lilu_obj = Lilubiao.objects.filter(fadanpingtai='12').first()
|
||||
rate = lilu_obj.lilu if lilu_obj and lilu_obj.lilu is not None else Decimal('0')
|
||||
if rate <= 0:
|
||||
logger.info(f"押金分红跳过: 费率未配置或为0")
|
||||
else:
|
||||
recharge_amount = Decimal(str(order_amount_yuan))
|
||||
fenhong_jine = (recharge_amount * rate).quantize(Decimal('0.01'))
|
||||
if fenhong_jine > 0:
|
||||
with transaction.atomic():
|
||||
guanshi = UserGuanshi.objects.select_for_update().get(
|
||||
user__yonghuid=guanshi_id
|
||||
)
|
||||
guanshi.yue += fenhong_jine
|
||||
guanshi.chongzhifenrun += fenhong_jine
|
||||
guanshi.save(update_fields=['yue', 'chongzhifenrun'])
|
||||
|
||||
user_main = dashou.user
|
||||
Gsfenhong.objects.create(
|
||||
dingdan_id=order.dingdan_id,
|
||||
guanshi=guanshi_id,
|
||||
dashouid=order.yonghuid,
|
||||
shuoming='押金分红',
|
||||
fenhong=fenhong_jine,
|
||||
avatar=user_main.avatar,
|
||||
nicheng=dashou.nicheng or '未知打手',
|
||||
fenhong_leixing=2,
|
||||
)
|
||||
logger.info(
|
||||
f"押金分红成功: 订单{order.dingdan_id}, 管事{guanshi_id}, "
|
||||
f"打手{order.yonghuid}, 金额{fenhong_jine}元"
|
||||
)
|
||||
try:
|
||||
from houtai.utils import update_guanshi_daily_by_action
|
||||
update_guanshi_daily_by_action(
|
||||
yonghuid=guanshi_id,
|
||||
action=4,
|
||||
amount=fenhong_jine,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"押金分红管事日统计更新失败: {e}")
|
||||
except UserGuanshi.DoesNotExist:
|
||||
logger.warning(f"押金分红跳过: 管事{guanshi_id}不存在")
|
||||
from dingdan.utils import pay_deposit_guanshi_fenhong
|
||||
pay_deposit_guanshi_fenhong(dashou, order, order_amount_yuan)
|
||||
except Exception as e:
|
||||
logger.error(f"押金分红处理失败: {str(e)}", exc_info=True)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user