Files
Django/orders/services/merchant_refund_approve.py
XingQue 73f0ce36f2 feat: 星阙商家退款审核批量同意脚本
新增 batch_approve_xq_merchant_refunds 管理命令,复用 kefusjtk 同意退款逻辑;
默认要求待审>=762单且 dry-run,--execute 才真正处理。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-10 23:18:56 +08:00

130 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""商家发单退款审核通过(与 KefuMerchantRefundView / 后台同意退款业务一致)。"""
import logging
from decimal import Decimal
from django.core.exceptions import ObjectDoesNotExist
from django.db import transaction
from backend.utils import update_dashou_daily_by_action, update_shangjia_daily
from orders.models import Order, RefundRecord
from users.business_models import User
logger = logging.getLogger(__name__)
class MerchantRefundApproveError(Exception):
"""单笔商家退款同意失败。"""
def approve_merchant_refund_order(
order,
*,
processor_id='batch_script',
tuikuan_liyou='',
require_status_4=True,
):
"""
同意商家发单退款Platform=2
与 POST /yonghu/kefusjtk 核心事务一致:
- 订单 Status → 5已退款
- 打手 tuikuanliang+1、zhuangtai=1
- 商家 tuikuan+1、余额+订单金额(子客服额度单除外)
- RefundRecord ApplyStatus → 1
- update_shangjia_daily(action=3)、update_dashou_daily_by_action(action=3)
返回 (True, 'ok');失败抛 MerchantRefundApproveError。
"""
dingdan_id = order.OrderID
if order.Platform != 2:
raise MerchantRefundApproveError(f'订单 {dingdan_id} 不是商家发单(Platform=2)')
if require_status_4 and order.Status != 4:
raise MerchantRefundApproveError(
f'订单 {dingdan_id} 状态={order.Status},非退款审核中(4)',
)
if not require_status_4 and order.Status not in (1, 7, 2, 8, 4):
raise MerchantRefundApproveError(
f'订单 {dingdan_id} 状态={order.Status},不允许退款',
)
jine = order.Amount
if jine is None:
raise MerchantRefundApproveError(f'订单 {dingdan_id} 缺少订单金额')
try:
shangjia_id = order.shangjia_kuozhan.MerchantID
except ObjectDoesNotExist as exc:
raise MerchantRefundApproveError(f'订单 {dingdan_id} 缺少商家扩展信息') from exc
jiedan_dashou_id = order.PlayerID
with transaction.atomic():
locked = Order.objects.select_for_update().select_related('shangjia_kuozhan').get(
pk=order.pk,
)
if require_status_4 and locked.Status != 4:
raise MerchantRefundApproveError(
f'订单 {dingdan_id} 并发冲突:当前状态={locked.Status}',
)
if locked.Platform != 2:
raise MerchantRefundApproveError(f'订单 {dingdan_id} 不是商家发单')
locked.Status = 5
if tuikuan_liyou:
locked.RefundReason = tuikuan_liyou
if processor_id:
locked.AssignedCS = processor_id
locked.save(update_fields=['Status', 'RefundReason', 'AssignedCS', 'UpdateTime'])
if jiedan_dashou_id:
try:
dashou_user = User.objects.select_for_update().get(UserUID=jiedan_dashou_id)
dashou_profile = dashou_user.DashouProfile
dashou_profile.tuikuanliang += 1
dashou_profile.zhuangtai = 1
dashou_profile.save(update_fields=['tuikuanliang', 'zhuangtai'])
except (User.DoesNotExist, ObjectDoesNotExist):
logger.warning('商家退款:打手 %s 不存在,跳过', jiedan_dashou_id)
from merchant_ops.services.wallet import is_staff_quota_order
try:
shangjia_user = User.objects.select_for_update().get(UserUID=shangjia_id)
shangjia_profile = shangjia_user.ShopProfile
shangjia_profile.tuikuan += 1
if not is_staff_quota_order(dingdan_id):
shangjia_profile.yue += jine
shangjia_profile.save(update_fields=['tuikuan', 'yue'])
except (User.DoesNotExist, ObjectDoesNotExist):
logger.warning('商家退款:商家 %s 不存在,跳过', shangjia_id)
try:
refund_record = RefundRecord.objects.select_for_update().get(OrderID=dingdan_id)
refund_record.ApplyStatus = 1
refund_record.ProcessorID = processor_id
refund_record.save(update_fields=['ApplyStatus', 'ProcessorID', 'UpdateTime'])
except RefundRecord.DoesNotExist:
logger.warning('商家退款:订单 %s 无 RefundRecord跳过更新', dingdan_id)
update_shangjia_daily(
yonghuid=shangjia_id,
amount=Decimal(str(jine)),
action=3,
order_id=dingdan_id,
)
if jiedan_dashou_id and order.PlayerCommission is not None:
try:
update_dashou_daily_by_action(
yonghuid=jiedan_dashou_id,
amount=order.PlayerCommission,
action=3,
)
except Exception as exc:
logger.error('打手每日统计更新失败 order=%s err=%s', dingdan_id, exc)
logger.info('同意商家退款成功 order=%s processor=%s', dingdan_id, processor_id)
return True, 'ok'