feat: 单订阅模式下赠送订阅自动合并,保持token不变
Some checks failed
Build docker and publish / build (20.15.1) (push) Failing after 7m38s
Some checks failed
Build docker and publish / build (20.15.1) (push) Failing after 7m38s
- purchaseLogic: 允许只有赠送订阅(order_id=0)的用户在SingleModel下新购 - activateOrderLogic: NewPurchase激活时检测赠送订阅,在原有订阅上延长到期时间而非创建新记录
This commit is contained in:
parent
a81f28f304
commit
9638cc11fa
@ -66,7 +66,15 @@ func (l *PurchaseLogic) Purchase(req *types.PurchaseOrderRequest) (resp *types.P
|
|||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find user subscription error: %v", err.Error())
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find user subscription error: %v", err.Error())
|
||||||
}
|
}
|
||||||
if l.svcCtx.Config.Subscribe.SingleModel {
|
if l.svcCtx.Config.Subscribe.SingleModel {
|
||||||
if len(userSub) > 0 {
|
// 检查是否有非赠送的活跃订阅(order_id > 0 表示付费订阅)
|
||||||
|
hasPaidSubscription := false
|
||||||
|
for _, s := range userSub {
|
||||||
|
if s.OrderId > 0 {
|
||||||
|
hasPaidSubscription = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hasPaidSubscription {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserSubscribeExist), "user has subscription")
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserSubscribeExist), "user has subscription")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +111,7 @@ func (l *PurchaseLogic) Purchase(req *types.PurchaseOrderRequest) (resp *types.P
|
|||||||
}
|
}
|
||||||
price := sub.UnitPrice * req.Quantity
|
price := sub.UnitPrice * req.Quantity
|
||||||
// discount amount
|
// discount amount
|
||||||
amount := int64(math.Round(float64(price) * discount))
|
amount := int64(math.Round(float64(price) * discount))
|
||||||
discountAmount := price - amount
|
discountAmount := price - amount
|
||||||
var coupon int64 = 0
|
var coupon int64 = 0
|
||||||
// Calculate the coupon deduction
|
// Calculate the coupon deduction
|
||||||
|
|||||||
@ -206,9 +206,31 @@ func (l *ActivateOrderLogic) NewPurchase(ctx context.Context, orderInfo *order.O
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
userSub, err := l.createUserSubscription(ctx, orderInfo, sub)
|
var userSub *user.Subscribe
|
||||||
if err != nil {
|
|
||||||
return err
|
// 单订阅模式下,检查用户是否已有赠送订阅(order_id=0)
|
||||||
|
if l.svc.Config.Subscribe.SingleModel {
|
||||||
|
giftSub, err := l.findGiftSubscription(ctx, orderInfo.UserId, orderInfo.SubscribeId)
|
||||||
|
if err == nil && giftSub != nil {
|
||||||
|
// 在赠送订阅上延长时间,保持 token 不变
|
||||||
|
userSub, err = l.extendGiftSubscription(ctx, giftSub, orderInfo, sub)
|
||||||
|
if err != nil {
|
||||||
|
logger.WithContext(ctx).Error("Extend gift subscription failed",
|
||||||
|
logger.Field("error", err.Error()),
|
||||||
|
logger.Field("gift_subscribe_id", giftSub.Id),
|
||||||
|
)
|
||||||
|
// 合并失败时回退到创建新订阅
|
||||||
|
userSub = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有合并赠送订阅,则正常创建新订阅
|
||||||
|
if userSub == nil {
|
||||||
|
userSub, err = l.createUserSubscription(ctx, orderInfo, sub)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle commission in separate goroutine to avoid blocking
|
// Handle commission in separate goroutine to avoid blocking
|
||||||
@ -382,6 +404,63 @@ func (l *ActivateOrderLogic) createUserSubscription(ctx context.Context, orderIn
|
|||||||
return userSub, nil
|
return userSub, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findGiftSubscription 查找用户指定套餐的赠送订阅(order_id=0 且状态活跃)
|
||||||
|
// 返回找到的赠送订阅记录,如果没有则返回 nil
|
||||||
|
func (l *ActivateOrderLogic) findGiftSubscription(ctx context.Context, userId int64, subscribeId int64) (*user.Subscribe, error) {
|
||||||
|
// 查询用户所有活跃订阅
|
||||||
|
subs, err := l.svc.UserModel.QueryUserSubscribe(ctx, userId, 0, 1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// 查找 order_id=0(赠送)且同套餐的订阅
|
||||||
|
for _, s := range subs {
|
||||||
|
if s.OrderId == 0 && s.SubscribeId == subscribeId {
|
||||||
|
// 通过 ID 获取完整的 Subscribe 记录
|
||||||
|
sub, err := l.svc.UserModel.FindOneSubscribe(ctx, s.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sub, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// extendGiftSubscription 在现有赠送订阅上延长到期时间,保持 token 不变
|
||||||
|
// 将购买的天数叠加到赠送订阅的到期时间上,并更新 order_id 为新订单 ID
|
||||||
|
func (l *ActivateOrderLogic) extendGiftSubscription(ctx context.Context, giftSub *user.Subscribe, orderInfo *order.Order, sub *subscribe.Subscribe) (*user.Subscribe, error) {
|
||||||
|
now := time.Now()
|
||||||
|
// 计算基准时间:取赠送订阅到期时间和当前时间的较大值
|
||||||
|
baseTime := giftSub.ExpireTime
|
||||||
|
if baseTime.Before(now) {
|
||||||
|
baseTime = now
|
||||||
|
}
|
||||||
|
// 在基准时间上增加购买的天数
|
||||||
|
newExpireTime := tool.AddTime(sub.UnitTime, orderInfo.Quantity, baseTime)
|
||||||
|
|
||||||
|
// 更新赠送订阅的信息
|
||||||
|
giftSub.OrderId = orderInfo.Id
|
||||||
|
giftSub.ExpireTime = newExpireTime
|
||||||
|
giftSub.Status = 1
|
||||||
|
|
||||||
|
if err := l.svc.UserModel.UpdateSubscribe(ctx, giftSub); err != nil {
|
||||||
|
logger.WithContext(ctx).Error("Update gift subscription failed",
|
||||||
|
logger.Field("error", err.Error()),
|
||||||
|
logger.Field("subscribe_id", giftSub.Id),
|
||||||
|
)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.WithContext(ctx).Info("Extended gift subscription successfully",
|
||||||
|
logger.Field("subscribe_id", giftSub.Id),
|
||||||
|
logger.Field("old_expire_time", baseTime),
|
||||||
|
logger.Field("new_expire_time", newExpireTime),
|
||||||
|
logger.Field("order_id", orderInfo.Id),
|
||||||
|
)
|
||||||
|
|
||||||
|
return giftSub, nil
|
||||||
|
}
|
||||||
|
|
||||||
// handleCommission processes referral commission for the referrer if applicable.
|
// handleCommission processes referral commission for the referrer if applicable.
|
||||||
// This runs asynchronously to avoid blocking the main order processing flow.
|
// This runs asynchronously to avoid blocking the main order processing flow.
|
||||||
func (l *ActivateOrderLogic) handleCommission(ctx context.Context, userInfo *user.User, orderInfo *order.Order) {
|
func (l *ActivateOrderLogic) handleCommission(ctx context.Context, userInfo *user.User, orderInfo *order.Order) {
|
||||||
|
|||||||
41
scripts/version-history.sh
Executable file
41
scripts/version-history.sh
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 版本历史查询工具
|
||||||
|
# 用法: ./scripts/version-history.sh [数量]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
COUNT=${1:-20}
|
||||||
|
BRANCH=${2:-dev}
|
||||||
|
|
||||||
|
echo "========================================"
|
||||||
|
echo "📦 PPanel Server 版本历史"
|
||||||
|
echo "========================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "🔹 分支: $BRANCH"
|
||||||
|
echo "🔹 显示最近 $COUNT 个版本"
|
||||||
|
echo ""
|
||||||
|
echo "----------------------------------------"
|
||||||
|
printf "%-10s | %-16s | %-50s\n" "版本SHA" "提交时间" "提交信息"
|
||||||
|
echo "----------------------------------------"
|
||||||
|
|
||||||
|
git log --oneline --date=format:'%Y-%m-%d %H:%M' \
|
||||||
|
--pretty=format:'%h | %ad | %s' \
|
||||||
|
-$COUNT $BRANCH
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "----------------------------------------"
|
||||||
|
echo ""
|
||||||
|
echo "💡 回滚方法:"
|
||||||
|
echo ""
|
||||||
|
echo "1️⃣ SSH 到服务器修改 .env 文件:"
|
||||||
|
echo " cd /root/bindbox"
|
||||||
|
echo " echo 'PPANEL_SERVER_TAG=<版本SHA>' > .env"
|
||||||
|
echo " docker-compose -f docker-compose.cloud.yml pull ppanel-server"
|
||||||
|
echo " docker-compose -f docker-compose.cloud.yml up -d ppanel-server"
|
||||||
|
echo ""
|
||||||
|
echo "2️⃣ 或者本地 Git 回滚后重新推送:"
|
||||||
|
echo " git reset --hard <版本SHA>"
|
||||||
|
echo " git push -f origin $BRANCH"
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
Loading…
x
Reference in New Issue
Block a user