feat(payment): 添加邀请奖励功能并重构订单支付处理逻辑

- 引入InviteReward模型用于记录邀请奖励
- 新增_process_success_order内部函数统一处理订单成功逻辑
- 实现邀请奖励机制:被邀请人前3次充值时,邀请人获得10%积分奖励
- 在支付回调、主动查询和异步通知中统一调用新的处理函数
- 改进代码结构,消除重复的订单处理逻辑
```
This commit is contained in:
24024 2026-01-23 21:58:37 +08:00
parent 0164140c39
commit 2ea495721c

View File

@ -1,6 +1,6 @@
from flask import Blueprint, request, redirect, url_for, session, jsonify, render_template
from extensions import db, redis_client
from models import Order, User, to_bj_time, get_bj_now
from models import Order, User, InviteReward, to_bj_time, get_bj_now
from services.alipay_service import AlipayService
from services.logger import system_logger
import uuid
@ -16,6 +16,57 @@ POINTS_PACKAGES = {
'5000': {'points': 5000, 'amount': 500.00},
}
def _process_success_order(order, trade_no):
"""内部处理订单成功逻辑 (需在锁内调用,不包含 commit)"""
if order.status == 'PAID':
return False
order.status = 'PAID'
order.trade_no = trade_no
order.paid_at = get_bj_now()
# 增加用户积分
user = db.session.get(User, order.user_id)
if user:
user.points += order.points
system_logger.info(f"订单支付成功", order_id=order.out_trade_no, points=order.points, user_id=user.id)
# ========== 邀请奖励逻辑 ==========
if user.invited_by:
# 统计该被邀请人之前已经完成多少次充值(不含本次)
paid_count = Order.query.filter(
Order.user_id == user.id,
Order.status == 'PAID',
Order.id != order.id
).count()
# 只有前3次充值才有奖励
if paid_count < 3:
inviter = db.session.get(User, user.invited_by)
if inviter:
# 计算10%奖励积分(四舍五入)
reward_points = round(order.points * 0.1)
if reward_points > 0:
inviter.points += reward_points
# 记录邀请奖励
invite_reward = InviteReward(
inviter_id=inviter.id,
invitee_id=user.id,
order_id=order.id,
reward_points=reward_points,
recharge_count=paid_count + 1
)
db.session.add(invite_reward)
system_logger.info(
f"邀请奖励发放成功",
inviter_id=inviter.id,
invitee_id=user.id,
reward_points=reward_points,
recharge_count=paid_count + 1
)
return True
@payment_bp.route('/create', methods=['POST'])
def create_payment():
if 'user_id' not in session:
@ -88,17 +139,8 @@ def payment_return():
order = Order.query.filter_by(out_trade_no=out_trade_no).with_for_update().first()
# 如果订单存在且状态为PENDING则更新为PAID
if order and order.status == 'PENDING':
order.status = 'PAID'
order.trade_no = trade_no
order.paid_at = get_bj_now()
# 增加用户积分
user = db.session.get(User, order.user_id)
if user:
user.points += order.points
system_logger.info(f"同步回调-订单支付成功", order_id=out_trade_no, points=order.points, user_id=user.id)
if order:
_process_success_order(order, trade_no)
db.session.commit()
elif order:
# 订单已经是完成状态,不做处理
@ -216,36 +258,28 @@ def api_sync_order():
order_locked = Order.query.filter_by(out_trade_no=out_trade_no).with_for_update().first()
# 二次校验状态,防止异步回调/定时任务已经处理
if order_locked and order_locked.status == 'PENDING':
order_locked.status = 'PAID'
order_locked.trade_no = alipay_result.get('trade_no')
if not order_locked.paid_at:
order_locked.paid_at = get_bj_now()
# 增加用户积分
user = db.session.get(User, order_locked.user_id)
if user:
user.points += order_locked.points
system_logger.info(f"主动查询-订单支付成功", order_id=out_trade_no, points=order_locked.points, user_id=user.id)
db.session.commit()
return jsonify({
'code': 200,
'msg': '订单已支付,积分已增加',
'status': 'PAID',
'points': order_locked.points,
'paid_at': order_locked.paid_at_bj.strftime('%Y-%m-%d %H:%M:%S')
})
elif order_locked and order_locked.status == 'PAID':
# 订单已经被处理,直接返回
return jsonify({
'code': 200,
'msg': '订单已支付',
'status': 'PAID',
'points': order_locked.points,
'paid_at': order_locked.paid_at_bj.strftime('%Y-%m-%d %H:%M:%S') if order_locked.paid_at else None
})
# 二次校验状态,防止异步回调/定时任务已经处理
if order_locked:
processed = _process_success_order(order_locked, alipay_result.get('trade_no'))
if processed:
db.session.commit()
return jsonify({
'code': 200,
'msg': '订单已支付,积分已增加',
'status': 'PAID',
'points': order_locked.points,
'paid_at': order_locked.paid_at_bj.strftime('%Y-%m-%d %H:%M:%S')
})
else:
# 订单已经是完成状态,不做处理
return jsonify({
'code': 200,
'msg': '订单已支付',
'status': 'PAID',
'points': order_locked.points,
'paid_at': order_locked.paid_at_bj.strftime('%Y-%m-%d %H:%M:%S') if order_locked.paid_at else None
})
except Exception as e:
if "LockError" in str(e) or "BlockingIOError" in str(e):
# 如果获取锁失败,可能是因为正在处理中,返回成功状态(前端会重试或刷新)
@ -301,20 +335,10 @@ def payment_notify():
lock_key = f"lock:order:{out_trade_no}"
with redis_client.lock(lock_key, timeout=10, blocking_timeout=3):
order = Order.query.filter_by(out_trade_no=out_trade_no).with_for_update().first()
if order and order.status == 'PENDING':
order.status = 'PAID'
order.trade_no = trade_no
order.paid_at = get_bj_now()
user = db.session.get(User, order.user_id)
if user:
user.points += order.points
system_logger.info(f"订单支付成功", order_id=out_trade_no, points=order.points, user_id=user.id)
if order:
_process_success_order(order, trade_no)
db.session.commit()
return "success"
elif order:
return "success"
else:
return "fail"
except Exception as e:
@ -327,59 +351,12 @@ def payment_notify():
return "fail"
raise e
order = Order.query.filter_by(out_trade_no=out_trade_no).first()
if order and order.status == 'PENDING':
order.status = 'PAID'
order.trade_no = trade_no
order.paid_at = get_bj_now()
user = db.session.get(User, order.user_id)
if user:
user.points += order.points
system_logger.info(f"订单支付成功", order_id=out_trade_no, points=order.points, user_id=user.id)
# ========== 邀请奖励逻辑 ==========
if user.invited_by:
from models import InviteReward
# 统计该被邀请人之前已经完成多少次充值(不含本次)
paid_count = Order.query.filter(
Order.user_id == user.id,
Order.status == 'PAID',
Order.id != order.id # 排除本次订单
).count()
# 只有前3次充值才有奖励paid_count 是之前的次数0/1/2 表示这是第1/2/3次
if paid_count < 3:
inviter = db.session.get(User, user.invited_by)
if inviter:
# 计算10%奖励积分(四舍五入)
reward_points = round(order.points * 0.1)
if reward_points > 0:
inviter.points += reward_points
# 记录邀请奖励
invite_reward = InviteReward(
inviter_id=inviter.id,
invitee_id=user.id,
order_id=order.id,
reward_points=reward_points,
recharge_count=paid_count + 1 # 第几次充值
)
db.session.add(invite_reward)
system_logger.info(
f"邀请奖励发放成功",
inviter_id=inviter.id,
invitee_id=user.id,
reward_points=reward_points,
recharge_count=paid_count + 1
)
# ========== 邀请奖励逻辑结束 ==========
db.session.commit()
return "success"
elif order:
return "success"
if order:
_process_success_order(order, trade_no)
db.session.commit()
return "success"
else:
return "fail"
return "fail"
else:
return "fail"