Files
Django/jituan/services/czjilu_fulfill.py

426 lines
18 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.
"""充值订单 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}
@transaction.atomic
def fulfill_fakuan(dingdan_id, paid_amount_yuan=None):
"""罚款支付幂等履约:仅 zhuangtai==9 时入账。"""
import re
from orders.models import Penalty
from users.fadan_fenhong_utils import process_fadan_fenhong
order = Czjilu.query.filter(dingdan_id=dingdan_id).select_for_update().first()
if not order:
raise CzjiluFulfillError('订单不存在', 'not_found')
if order.leixing != 6:
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')
order.zhuangtai = 3
order.save(update_fields=['zhuangtai'])
# 更新罚单状态为已缴纳2并触发分红
try:
fadan = None
m = re.search(r'#fadan:(\d+)', order.shuoming or '')
if m:
fadan_id_str = m.group(1)
fadan = Penalty.query.filter(
id=int(fadan_id_str),
PenalizedUserID=order.yonghuid,
Status__in=[1, 3],
).first()
if not fadan:
logger.warning(
'罚款订单 %s shuoming 标记罚单 %s 未找到(被处罚者=%s, 状态需为1/3)',
dingdan_id, fadan_id_str, order.yonghuid,
)
else:
logger.warning(
'罚款订单 %s shuoming 无 #fadan:xxx 标记,回退金额匹配: %s',
dingdan_id, order.shuoming,
)
if not fadan:
fadan = Penalty.query.filter(
PenalizedUserID=order.yonghuid,
FineAmount=order.jine,
Status__in=[1, 3],
).order_by('-CreateTime').first()
if fadan:
logger.warning(
'罚款订单 %s 回退金额匹配到罚单 %s(金额=%s',
dingdan_id, fadan.id, order.jine,
)
if fadan:
fadan.Status = 2
fadan.save(update_fields=['Status'])
success, msg = process_fadan_fenhong(fadan.id)
if not success:
logger.error('罚单 %s 分红处理失败: %s', fadan.id, msg)
else:
logger.info('罚单 %s 分红处理完成: %s', fadan.id, msg)
else:
logger.error(
'罚款订单 %s 未找到对应罚单,罚单状态未更新(被处罚者=%s, 金额=%s)',
dingdan_id, order.yonghuid, order.jine,
)
except Exception as exc:
logger.error('罚款罚单状态更新失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
try:
from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
repair_wechat_income_if_missing(amount, 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)
logger.info('罚款履约完成 dingdan=%s amount=%s', dingdan_id, amount)
return {'already_done': False, 'dingdan_id': dingdan_id, 'zhuangtai': 3}
@transaction.atomic
def fulfill_kaohe(dingdan_id, paid_amount_yuan=None):
"""考核支付幂等履约:仅 zhuangtai==9 时入账。"""
from rank.models import KaohePayTemp
from rank.utils import create_shenhe_jilu_from_temp
from users.business_models import User
order = Czjilu.query.filter(dingdan_id=dingdan_id).select_for_update().first()
if not order:
raise CzjiluFulfillError('订单不存在', 'not_found')
if order.leixing != 5:
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')
order.zhuangtai = 3
order.save(update_fields=['zhuangtai'])
# 从临时表取业务参数并创建审核记录
try:
temp = KaohePayTemp.query.get(dingdan_id=dingdan_id, yonghuid=order.yonghuid)
user = User.query.get(UserUID=order.yonghuid)
success, msg, record = create_shenhe_jilu_from_temp(user, temp)
if not success:
logger.error('创建审核记录失败 dingdan=%s: %s', dingdan_id, msg)
order.zhuangtai = 10 # 支付成功但业务处理失败
order.save(update_fields=['zhuangtai'])
else:
logger.info('考核审核记录创建成功 dingdan=%s jilu_id=%s', dingdan_id, getattr(record, 'jilu_id', None))
except KaohePayTemp.DoesNotExist:
logger.error('考核临时表无记录 dingdan=%s', dingdan_id)
order.zhuangtai = 10
order.save(update_fields=['zhuangtai'])
except Exception as exc:
logger.error('考核审核记录创建异常 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
order.zhuangtai = 10
order.save(update_fields=['zhuangtai'])
try:
from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
repair_wechat_income_if_missing(amount, 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)
logger.info('考核履约完成 dingdan=%s amount=%s', dingdan_id, amount)
return {'already_done': False, 'dingdan_id': dingdan_id, 'zhuangtai': order.zhuangtai}
@transaction.atomic
def fulfill_shangjia(dingdan_id, paid_amount_yuan=None):
"""商家充值幂等履约:仅 zhuangtai==9 时入账。"""
from users.models import UserShangjia
order = Czjilu.query.filter(dingdan_id=dingdan_id).select_for_update().first()
if not order:
raise CzjiluFulfillError('订单不存在', 'not_found')
if order.leixing != 4:
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:
shangjia = UserShangjia.objects.select_for_update().get(user__UserUID=order.yonghuid)
except UserShangjia.DoesNotExist:
raise CzjiluFulfillError('商家不存在,无法充值', 'shangjia_not_found')
shangjia.yue += amount
shangjia.save(update_fields=['yue'])
order.zhuangtai = 3
order.save(update_fields=['zhuangtai'])
try:
from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
repair_wechat_income_if_missing(amount, 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)
logger.info('商家充值履约完成 dingdan=%s amount=%s yue=%s', dingdan_id, amount, shangjia.yue)
return {'already_done': False, 'dingdan_id': dingdan_id, 'zhuangtai': 3, 'yue': shangjia.yue}
def confirm_czjilu_paid(dingdan_id, yonghuid, expected_leixing):
"""轮询确认:支付宝订单直接看本地状态,微信订单查微信 SUCCESS 后幂等履约。"""
from django.core.cache import cache
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')
# 支付宝订单:异步回调会更新状态,轮询时只看本地状态,未支付则提示稍后重试
is_alipay = cache.get(f'alipay_pay_url_{dingdan_id}') is not None
if is_alipay:
raise CzjiluFulfillError('支付宝支付结果尚未同步,请稍后再试', 'not_paid')
# 微信订单:主动查单
from jituan.services.member_recharge import query_wechat_v2_trade_state
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 == 1:
from jituan.services.member_recharge import fulfill_member_recharge
return fulfill_member_recharge(dingdan_id, paid_amount_yuan=paid, source='confirm')
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)
if expected_leixing == 4:
return fulfill_shangjia(dingdan_id, paid_amount_yuan=paid)
if expected_leixing == 5:
return fulfill_kaohe(dingdan_id, paid_amount_yuan=paid)
if expected_leixing == 6:
return fulfill_fakuan(dingdan_id, paid_amount_yuan=paid)
raise CzjiluFulfillError('不支持的充值类型', 'type_error')