207 lines
8.5 KiB
Python
207 lines
8.5 KiB
Python
"""充值订单 czjilu 履约(押金/积分),与 member_recharge 同一套幂等原则。"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from decimal import Decimal
|
|
|
|
from django.db import transaction
|
|
|
|
from backend.utils import update_guanshi_daily_by_action
|
|
from jituan.constants import CLUB_ID_DEFAULT
|
|
from jituan.services.club_penalty import resolve_gsfenhong_club_id
|
|
from orders.models import CommissionRate
|
|
from products.models import Czjilu, Gsfenhong
|
|
from users.models import UserDashou, UserGuanshi
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
INTEGRAL_PRICE = Decimal('5.00')
|
|
MAX_INTEGRAL = 10
|
|
INTEGRAL_INCREMENT = 5
|
|
|
|
|
|
class CzjiluFulfillError(Exception):
|
|
def __init__(self, message, code='error'):
|
|
super().__init__(message)
|
|
self.code = code
|
|
|
|
|
|
@transaction.atomic
|
|
def fulfill_yajin_recharge(dingdan_id, paid_amount_yuan=None):
|
|
"""押金充值幂等履约:仅 zhuangtai==9 时入账。"""
|
|
order = Czjilu.query.filter(dingdan_id=dingdan_id).select_for_update().first()
|
|
if not order:
|
|
raise CzjiluFulfillError('订单不存在', 'not_found')
|
|
if order.leixing != 2:
|
|
raise CzjiluFulfillError('订单类型不是押金', 'type_error')
|
|
if order.zhuangtai != 9:
|
|
try:
|
|
from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
|
|
repair_wechat_income_if_missing(
|
|
Decimal(str(order.jine)),
|
|
getattr(order, 'club_id', None) or CLUB_ID_DEFAULT,
|
|
biz_ref=f'cz:{dingdan_id}',
|
|
)
|
|
except Exception as exc:
|
|
logger.error('押金补记收支失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
|
|
return {'already_done': True, 'dingdan_id': dingdan_id, 'zhuangtai': order.zhuangtai}
|
|
|
|
amount = Decimal(str(order.jine))
|
|
if paid_amount_yuan is not None:
|
|
paid = Decimal(str(paid_amount_yuan))
|
|
if abs(paid - amount) > Decimal('0.01'):
|
|
raise CzjiluFulfillError(f'支付金额{paid}与订单{amount}不一致', 'amount_mismatch')
|
|
|
|
try:
|
|
dashou = UserDashou.objects.select_for_update().get(user__UserUID=order.yonghuid)
|
|
except UserDashou.DoesNotExist:
|
|
raise CzjiluFulfillError('打手不存在,无法充值押金', 'dashou_not_found')
|
|
|
|
dashou.yajin += amount
|
|
dashou.save(update_fields=['yajin'])
|
|
|
|
order.zhuangtai = 3
|
|
order.save(update_fields=['zhuangtai'])
|
|
|
|
club_id = getattr(order, 'club_id', None) or CLUB_ID_DEFAULT
|
|
try:
|
|
from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
|
|
repair_wechat_income_if_missing(amount, club_id, biz_ref=f'cz:{dingdan_id}')
|
|
except Exception as exc:
|
|
logger.error('押金收支统计失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
|
|
|
|
try:
|
|
_apply_yajin_fenhong(order, dashou, amount)
|
|
except Exception as exc:
|
|
logger.error('押金分红失败但押金已到账 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
|
|
|
|
logger.info('押金履约完成 dingdan=%s amount=%s', dingdan_id, amount)
|
|
return {'already_done': False, 'dingdan_id': dingdan_id, 'zhuangtai': 3, 'yajin': dashou.yajin}
|
|
|
|
|
|
def _apply_yajin_fenhong(order, dashou, amount):
|
|
if Gsfenhong.query.filter(dingdan_id=order.dingdan_id).exists():
|
|
return
|
|
guanshi_id = dashou.yaoqingren
|
|
if not guanshi_id:
|
|
return
|
|
lilu_obj = CommissionRate.query.filter(Platform='12').first()
|
|
rate = lilu_obj.Rate if lilu_obj and lilu_obj.Rate is not None else Decimal('0')
|
|
if rate <= 0:
|
|
return
|
|
fenhong_jine = (amount * rate).quantize(Decimal('0.01'))
|
|
if fenhong_jine <= 0:
|
|
return
|
|
guanshi = UserGuanshi.objects.select_for_update().get(user__UserUID=guanshi_id)
|
|
guanshi.yue += fenhong_jine
|
|
guanshi.chongzhifenrun += fenhong_jine
|
|
guanshi.save(update_fields=['yue', 'chongzhifenrun'])
|
|
user_main = dashou.user
|
|
Gsfenhong.query.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,
|
|
club_id=resolve_gsfenhong_club_id(
|
|
dingdan_id=order.dingdan_id, dashouid=order.yonghuid, czjilu=order,
|
|
),
|
|
)
|
|
try:
|
|
update_guanshi_daily_by_action(yonghuid=guanshi_id, action=4, amount=fenhong_jine)
|
|
except Exception as exc:
|
|
logger.error('押金分红日统计失败: %s', exc)
|
|
|
|
|
|
@transaction.atomic
|
|
def fulfill_jifen_recharge(dingdan_id, paid_amount_yuan=None):
|
|
"""积分补充幂等履约:仅 zhuangtai==9 时入账。"""
|
|
order = Czjilu.query.filter(dingdan_id=dingdan_id).select_for_update().first()
|
|
if not order:
|
|
raise CzjiluFulfillError('订单不存在', 'not_found')
|
|
if order.leixing != 3:
|
|
raise CzjiluFulfillError('订单类型不是积分', 'type_error')
|
|
if order.zhuangtai != 9:
|
|
try:
|
|
from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
|
|
repair_wechat_income_if_missing(
|
|
Decimal(str(order.jine)),
|
|
getattr(order, 'club_id', None) or CLUB_ID_DEFAULT,
|
|
biz_ref=f'cz:{dingdan_id}',
|
|
)
|
|
except Exception as exc:
|
|
logger.error('积分补记收支失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
|
|
return {'already_done': True, 'dingdan_id': dingdan_id, 'zhuangtai': order.zhuangtai}
|
|
|
|
amount = Decimal(str(order.jine))
|
|
if paid_amount_yuan is not None:
|
|
paid = Decimal(str(paid_amount_yuan))
|
|
if abs(paid - amount) > Decimal('0.01'):
|
|
raise CzjiluFulfillError(f'支付金额{paid}与订单{amount}不一致', 'amount_mismatch')
|
|
if abs(paid - INTEGRAL_PRICE) > Decimal('0.01'):
|
|
raise CzjiluFulfillError('积分订单金额必须为5元', 'amount_mismatch')
|
|
|
|
try:
|
|
dashou = UserDashou.objects.select_for_update().get(user__UserUID=order.yonghuid)
|
|
except UserDashou.DoesNotExist:
|
|
raise CzjiluFulfillError('打手不存在,无法补充积分', 'dashou_not_found')
|
|
|
|
if dashou.jifen < MAX_INTEGRAL:
|
|
dashou.jifen = min(dashou.jifen + INTEGRAL_INCREMENT, MAX_INTEGRAL)
|
|
dashou.save(update_fields=['jifen'])
|
|
|
|
order.zhuangtai = 3
|
|
order.save(update_fields=['zhuangtai'])
|
|
|
|
club_id = getattr(order, 'club_id', None) or CLUB_ID_DEFAULT
|
|
try:
|
|
from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
|
|
repair_wechat_income_if_missing(amount, club_id, biz_ref=f'cz:{dingdan_id}')
|
|
except Exception as exc:
|
|
logger.error('积分收支统计失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
|
|
|
|
logger.info('积分履约完成 dingdan=%s jifen=%s', dingdan_id, dashou.jifen)
|
|
return {'already_done': False, 'dingdan_id': dingdan_id, 'zhuangtai': 3, 'jifen': dashou.jifen}
|
|
|
|
|
|
def confirm_czjilu_paid(dingdan_id, yonghuid, expected_leixing):
|
|
"""轮询确认:查微信 SUCCESS 后幂等履约。"""
|
|
from jituan.services.member_recharge import query_wechat_v2_trade_state
|
|
|
|
try:
|
|
order = Czjilu.query.get(dingdan_id=dingdan_id, yonghuid=yonghuid)
|
|
except Czjilu.DoesNotExist:
|
|
raise CzjiluFulfillError('订单不存在或不属于当前用户', 'not_found')
|
|
|
|
if order.leixing != expected_leixing:
|
|
raise CzjiluFulfillError('订单类型不匹配', 'type_error')
|
|
|
|
if order.zhuangtai == 3:
|
|
try:
|
|
from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
|
|
repair_wechat_income_if_missing(
|
|
Decimal(str(order.jine)),
|
|
getattr(order, 'club_id', None) or CLUB_ID_DEFAULT,
|
|
biz_ref=f'cz:{dingdan_id}',
|
|
)
|
|
except Exception as exc:
|
|
logger.error('充值单补记收支失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
|
|
return {'already_done': True, 'zhuangtai': 3}
|
|
|
|
if order.zhuangtai != 9:
|
|
raise CzjiluFulfillError(f'订单状态异常: {order.zhuangtai}', 'status_error')
|
|
|
|
trade_state, _ = query_wechat_v2_trade_state(dingdan_id, getattr(order, 'club_id', None))
|
|
if trade_state != 'SUCCESS':
|
|
raise CzjiluFulfillError('微信侧尚未支付成功,请稍后再试', 'not_paid')
|
|
|
|
paid = Decimal(str(order.jine))
|
|
if expected_leixing == 2:
|
|
return fulfill_yajin_recharge(dingdan_id, paid_amount_yuan=paid)
|
|
if expected_leixing == 3:
|
|
return fulfill_jifen_recharge(dingdan_id, paid_amount_yuan=paid)
|
|
raise CzjiluFulfillError('不支持的充值类型', 'type_error')
|