fix: 会员充值先开通再分红防回滚,补开通与注册时间字段
This commit is contained in:
@@ -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 '',
|
||||||
}
|
}
|
||||||
|
|
||||||
# 余额提现相关
|
# 余额提现相关
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user