"""充值订单 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')