From 9638cc11fa45fccfaf25e0fbafe0ab2d3ac2fcad Mon Sep 17 00:00:00 2001 From: shanshanzhong Date: Tue, 24 Feb 2026 21:01:36 -0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8D=95=E8=AE=A2=E9=98=85=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E4=B8=8B=E8=B5=A0=E9=80=81=E8=AE=A2=E9=98=85=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=90=88=E5=B9=B6=EF=BC=8C=E4=BF=9D=E6=8C=81token?= =?UTF-8?q?=E4=B8=8D=E5=8F=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - purchaseLogic: 允许只有赠送订阅(order_id=0)的用户在SingleModel下新购 - activateOrderLogic: NewPurchase激活时检测赠送订阅,在原有订阅上延长到期时间而非创建新记录 --- internal/logic/public/order/purchaseLogic.go | 12 ++- queue/logic/order/activateOrderLogic.go | 85 +++++++++++++++++++- scripts/version-history.sh | 41 ++++++++++ 3 files changed, 133 insertions(+), 5 deletions(-) create mode 100755 scripts/version-history.sh diff --git a/internal/logic/public/order/purchaseLogic.go b/internal/logic/public/order/purchaseLogic.go index 3950f57..8b0665e 100644 --- a/internal/logic/public/order/purchaseLogic.go +++ b/internal/logic/public/order/purchaseLogic.go @@ -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()) } 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") } } @@ -103,7 +111,7 @@ func (l *PurchaseLogic) Purchase(req *types.PurchaseOrderRequest) (resp *types.P } price := sub.UnitPrice * req.Quantity // discount amount - amount := int64(math.Round(float64(price) * discount)) + amount := int64(math.Round(float64(price) * discount)) discountAmount := price - amount var coupon int64 = 0 // Calculate the coupon deduction diff --git a/queue/logic/order/activateOrderLogic.go b/queue/logic/order/activateOrderLogic.go index 4924c63..b7e63e8 100644 --- a/queue/logic/order/activateOrderLogic.go +++ b/queue/logic/order/activateOrderLogic.go @@ -206,9 +206,31 @@ func (l *ActivateOrderLogic) NewPurchase(ctx context.Context, orderInfo *order.O return err } - userSub, err := l.createUserSubscription(ctx, orderInfo, sub) - if err != nil { - return err + var userSub *user.Subscribe + + // 单订阅模式下,检查用户是否已有赠送订阅(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 @@ -382,6 +404,63 @@ func (l *ActivateOrderLogic) createUserSubscription(ctx context.Context, orderIn 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. // This runs asynchronously to avoid blocking the main order processing flow. func (l *ActivateOrderLogic) handleCommission(ctx context.Context, userInfo *user.User, orderInfo *order.Order) { diff --git a/scripts/version-history.sh b/scripts/version-history.sh new file mode 100755 index 0000000..a586940 --- /dev/null +++ b/scripts/version-history.sh @@ -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 "========================================"