fix: 会员充值先开通再分红防回滚,补开通与注册时间字段

This commit is contained in:
XingQue
2026-06-24 18:08:11 +08:00
parent 83519ac67b
commit 9cd7b6920a
4 changed files with 75 additions and 14 deletions

View File

@@ -911,6 +911,7 @@ class KefuGetDashouDetailView(APIView):
'wechat': '', 'wechat': '',
'phone': user_main.Phone or '', 'phone': user_main.Phone or '',
'CreateTime': user_main.UserCreateTime, 'CreateTime': user_main.UserCreateTime,
'create_time': user_main.UserCreateTime.strftime('%Y-%m-%d %H:%M:%S') if getattr(user_main, 'UserCreateTime', None) else '',
'ip': user_main.IP or '', 'ip': user_main.IP or '',
'jiedanzongliang': 0, 'jiedanzongliang': 0,
'chengjiaozongliang': 0, 'chengjiaozongliang': 0,
@@ -951,6 +952,7 @@ class KefuGetDashouDetailView(APIView):
'wechat': dashou.wechat or '', 'wechat': dashou.wechat or '',
'phone': user_main.Phone or '', 'phone': user_main.Phone or '',
'CreateTime': user_main.UserCreateTime, 'CreateTime': user_main.UserCreateTime,
'create_time': user_main.UserCreateTime.strftime('%Y-%m-%d %H:%M:%S') if getattr(user_main, 'UserCreateTime', None) else '',
'ip': user_main.IP or '', 'ip': user_main.IP or '',
'jiedanzongliang': dashou.jiedanzongliang, 'jiedanzongliang': dashou.jiedanzongliang,
'chengjiaozongliang': dashou.chengjiaozongliang, 'chengjiaozongliang': dashou.chengjiaozongliang,
@@ -1554,6 +1556,7 @@ class KefuGetShangjiaDetailView(APIView):
'jinyuedingdan': shangjia.jinyuedingdan, 'jinyuedingdan': shangjia.jinyuedingdan,
'jinyueliushui': str(shangjia.jinyueliushui), 'jinyueliushui': str(shangjia.jinyueliushui),
'CreateTime': shangjia.CreateTime.isoformat() if shangjia.CreateTime else None, 'CreateTime': shangjia.CreateTime.isoformat() if shangjia.CreateTime else None,
'create_time': user.UserCreateTime.strftime('%Y-%m-%d %H:%M:%S') if getattr(user, 'UserCreateTime', None) else '',
} }
return Response({'code': 0, 'data': data}) return Response({'code': 0, 'data': data})
@@ -3824,6 +3827,7 @@ class GetZuzhangDetailView(APIView):
'jinyue_fenyong': str(zuzhang.jinyue_fenyong), 'jinyue_fenyong': str(zuzhang.jinyue_fenyong),
'fenyong_zonge': str(zuzhang.fenyong_zonge), 'fenyong_zonge': str(zuzhang.fenyong_zonge),
'CreateTime': zuzhang.CreateTime.isoformat() if zuzhang.CreateTime else None, 'CreateTime': zuzhang.CreateTime.isoformat() if zuzhang.CreateTime else None,
'create_time': user.UserCreateTime.strftime('%Y-%m-%d %H:%M:%S') if getattr(user, 'UserCreateTime', None) else '',
} }
# 余额提现相关 # 余额提现相关

View File

@@ -127,7 +127,12 @@ def _apply_member_fenhong(order, huiyuan, purchase_cishu):
logger.info('会员分红已处理,跳过: %s', order.dingdan_id) logger.info('会员分红已处理,跳过: %s', order.dingdan_id)
return return
dashou = UserDashou.query.get(user__UserUID=order.yonghuid) try:
dashou = UserDashou.query.get(user__UserUID=order.yonghuid)
except UserDashou.DoesNotExist:
logger.warning('打手不存在,跳过会员分红: %s', order.yonghuid)
return
guanshi_id = dashou.yaoqingren guanshi_id = dashou.yaoqingren
if not guanshi_id: if not guanshi_id:
logger.info('打手%s无邀请管事,跳过会员分红', order.yonghuid) logger.info('打手%s无邀请管事,跳过会员分红', order.yonghuid)
@@ -282,6 +287,35 @@ def _add_first_buy_jifen(yonghuid):
logger.warning('首次购会员加积分失败,打手不存在: %s', yonghuid) logger.warning('首次购会员加积分失败,打手不存在: %s', yonghuid)
def repair_member_entitlement_if_paid(dingdan_id):
"""
订单已标为已支付(zhuangtai=3)但 huiyuangoumai 缺失时补开通会员。
用于修复历史脏数据或履约中断。
"""
order = Czjilu.query.filter(dingdan_id=dingdan_id, leixing=1).first()
if not order or order.zhuangtai != 3 or not order.huiyuan_id:
return False
exists = Huiyuangoumai.query.filter(
yonghu_id=order.yonghuid,
huiyuan_id=order.huiyuan_id,
).exists()
if exists:
return True
try:
huiyuan = Huiyuan.query.get(huiyuan_id=order.huiyuan_id)
except Huiyuan.DoesNotExist:
logger.error('补开通会员失败,会员不存在 dingdan=%s huiyuan=%s', dingdan_id, order.huiyuan_id)
return False
club_id = getattr(order, 'club_id', None) or CLUB_ID_DEFAULT
with transaction.atomic():
_apply_huiyuan_goumai(order.yonghuid, order.huiyuan_id, huiyuan, club_id)
logger.warning('已补开通会员 dingdan=%s yonghu=%s huiyuan=%s', dingdan_id, order.yonghuid, order.huiyuan_id)
return True
@transaction.atomic @transaction.atomic
def fulfill_member_recharge(dingdan_id, paid_amount_yuan=None, source='callback'): def fulfill_member_recharge(dingdan_id, paid_amount_yuan=None, source='callback'):
""" """
@@ -315,7 +349,10 @@ def fulfill_member_recharge(dingdan_id, paid_amount_yuan=None, source='callback'
f'支付金额{paid}与订单{order_amt}不一致', 'amount_mismatch', f'支付金额{paid}与订单{order_amt}不一致', 'amount_mismatch',
) )
huiyuan = Huiyuan.query.get(huiyuan_id=order.huiyuan_id) huiyuan = Huiyuan.query.filter(huiyuan_id=order.huiyuan_id).first()
if not huiyuan:
raise MemberRechargeError(f'会员不存在: {order.huiyuan_id}', 'huiyuan_not_found')
club_id = getattr(order, 'club_id', None) or CLUB_ID_DEFAULT club_id = getattr(order, 'club_id', None) or CLUB_ID_DEFAULT
is_first_purchase_of_member = not Huiyuangoumai.query.filter( is_first_purchase_of_member = not Huiyuangoumai.query.filter(
@@ -328,12 +365,12 @@ def fulfill_member_recharge(dingdan_id, paid_amount_yuan=None, source='callback'
order.yonghuid, order.huiyuan_id, order.dingdan_id, club_id, order.yonghuid, order.huiyuan_id, order.dingdan_id, club_id,
) )
jine_decimal = Decimal(str(order.jine)) # 先开通会员,再记账;分红失败不得回滚会员
try: goumai_record, _, is_first_buy_any = _apply_huiyuan_goumai(
from jituan.services.szjilu_accounting import record_wechat_income_once order.yonghuid, order.huiyuan_id, huiyuan, club_id,
record_wechat_income_once(jine_decimal, club_id, biz_ref=f'cz:{dingdan_id}') )
except Exception as exc: if is_first_buy_any:
logger.error('会员收支统计更新失败: %s', exc, exc_info=True) _add_first_buy_jifen(order.yonghuid)
order.zhuangtai = 3 order.zhuangtai = 3
order.save(update_fields=['zhuangtai']) order.save(update_fields=['zhuangtai'])
@@ -341,13 +378,20 @@ def fulfill_member_recharge(dingdan_id, paid_amount_yuan=None, source='callback'
huiyuan.goumai_cishu = (huiyuan.goumai_cishu or 0) + 1 huiyuan.goumai_cishu = (huiyuan.goumai_cishu or 0) + 1
huiyuan.save(update_fields=['goumai_cishu']) huiyuan.save(update_fields=['goumai_cishu'])
goumai_record, _, is_first_buy_any = _apply_huiyuan_goumai( jine_decimal = Decimal(str(order.jine))
order.yonghuid, order.huiyuan_id, huiyuan, club_id, try:
) from jituan.services.szjilu_accounting import record_wechat_income_once
if is_first_buy_any: record_wechat_income_once(jine_decimal, club_id, biz_ref=f'cz:{dingdan_id}')
_add_first_buy_jifen(order.yonghuid) except Exception as exc:
logger.error('会员收支统计更新失败: %s', exc, exc_info=True)
_apply_member_fenhong(order, huiyuan, purchase_cishu) try:
_apply_member_fenhong(order, huiyuan, purchase_cishu)
except Exception as exc:
logger.error(
'会员分红失败但会员已开通 dingdan=%s err=%s',
dingdan_id, exc, exc_info=True,
)
logger.info( logger.info(
'会员充值履约完成 dingdan=%s source=%s club=%s purchase_cishu=%s first_member=%s', '会员充值履约完成 dingdan=%s source=%s club=%s purchase_cishu=%s first_member=%s',
@@ -419,6 +463,11 @@ def confirm_member_recharge_paid(dingdan_id, yonghuid):
goumai = Huiyuangoumai.query.filter( goumai = Huiyuangoumai.query.filter(
yonghu_id=yonghuid, huiyuan_id=order.huiyuan_id, yonghu_id=yonghuid, huiyuan_id=order.huiyuan_id,
).first() ).first()
if not goumai:
repair_member_entitlement_if_paid(dingdan_id)
goumai = Huiyuangoumai.query.filter(
yonghu_id=yonghuid, huiyuan_id=order.huiyuan_id,
).first()
if not goumai: if not goumai:
raise MemberRechargeError('订单已付但会员记录缺失,请联系客服', 'data_error') raise MemberRechargeError('订单已付但会员记录缺失,请联系客服', 'data_error')
return { return {

View File

@@ -1767,6 +1767,9 @@ class HuiyuanHuitiao(View):
if order.zhuangtai != 9: if order.zhuangtai != 9:
logger.warning(f"会员订单状态不是未支付: {order.dingdan_id}, 当前状态: {order.zhuangtai}") logger.warning(f"会员订单状态不是未支付: {order.dingdan_id}, 当前状态: {order.zhuangtai}")
if order.zhuangtai == 3:
from jituan.services.member_recharge import repair_member_entitlement_if_paid
repair_member_entitlement_if_paid(out_trade_no)
return self.wechat_response('SUCCESS', 'OK') return self.wechat_response('SUCCESS', 'OK')
wechat_amount_yuan = total_fee / 100 wechat_amount_yuan = total_fee / 100
@@ -1968,6 +1971,9 @@ class HuiyuanLunxun(APIView):
'message': '会员订单尚未支付完成' 'message': '会员订单尚未支付完成'
}, status=status.HTTP_400_BAD_REQUEST) }, status=status.HTTP_400_BAD_REQUEST)
from jituan.services.member_recharge import repair_member_entitlement_if_paid
repair_member_entitlement_if_paid(dingdanid)
# 5. 查询会员信息 # 5. 查询会员信息
try: try:
huiyuan = Huiyuan.query.get(huiyuan_id=order.huiyuan_id) huiyuan = Huiyuan.query.get(huiyuan_id=order.huiyuan_id)

View File

@@ -8585,6 +8585,7 @@ class KefuGetDashouDetailView(APIView):
'wechat': '', 'wechat': '',
'phone': user_main.Phone or '', 'phone': user_main.Phone or '',
'CreateTime': user_main.UserCreateTime, 'CreateTime': user_main.UserCreateTime,
'create_time': user_main.UserCreateTime.strftime('%Y-%m-%d %H:%M:%S') if getattr(user_main, 'UserCreateTime', None) else '',
'ip': user_main.IP or '', 'ip': user_main.IP or '',
'jiedanzongliang': 0, 'jiedanzongliang': 0,
'chengjiaozongliang': 0, 'chengjiaozongliang': 0,
@@ -8621,6 +8622,7 @@ class KefuGetDashouDetailView(APIView):
'wechat': dashou.wechat or '', 'wechat': dashou.wechat or '',
'phone': user_main.Phone or '', 'phone': user_main.Phone or '',
'CreateTime': user_main.UserCreateTime, 'CreateTime': user_main.UserCreateTime,
'create_time': user_main.UserCreateTime.strftime('%Y-%m-%d %H:%M:%S') if getattr(user_main, 'UserCreateTime', None) else '',
'ip': user_main.IP or '', 'ip': user_main.IP or '',
'jiedanzongliang': dashou.jiedanzongliang, 'jiedanzongliang': dashou.jiedanzongliang,
'chengjiaozongliang': dashou.chengjiaozongliang, 'chengjiaozongliang': dashou.chengjiaozongliang,