From ae126296e31bf6090345dad49d21dd20e050ef51 Mon Sep 17 00:00:00 2001 From: shanshanzhong Date: Sat, 30 May 2026 23:12:39 -0700 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D(#128):=20=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E7=BB=AD=E8=B4=B9=E5=9C=BA=E6=99=AF=E4=BF=83=E9=94=80=E5=88=A4?= =?UTF-8?q?=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: multica-agent --- .../logic/public/order/paidSubscription.go | 17 ++++++++++ .../public/order/paidSubscription_test.go | 32 +++++++++++++++++++ .../logic/public/order/preCreateOrderLogic.go | 9 ++---- internal/logic/public/order/purchaseLogic.go | 9 ++---- internal/model/user/subscribe.go | 2 +- 5 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 internal/logic/public/order/paidSubscription.go create mode 100644 internal/logic/public/order/paidSubscription_test.go diff --git a/internal/logic/public/order/paidSubscription.go b/internal/logic/public/order/paidSubscription.go new file mode 100644 index 0000000..c980082 --- /dev/null +++ b/internal/logic/public/order/paidSubscription.go @@ -0,0 +1,17 @@ +package order + +import ( + "context" + + "github.com/perfect-panel/server/internal/model/user" + "gorm.io/gorm" +) + +func paidSubscriptionQuery(ctx context.Context, db *gorm.DB, userID int64) *gorm.DB { + return db.WithContext(ctx). + Model(&user.Subscribe{}). + Where("user_id = ? AND (order_id > 0 OR token LIKE 'iap:%')", userID). + Order("expire_time DESC"). + Order("updated_at DESC"). + Order("id DESC") +} diff --git a/internal/logic/public/order/paidSubscription_test.go b/internal/logic/public/order/paidSubscription_test.go new file mode 100644 index 0000000..b42b905 --- /dev/null +++ b/internal/logic/public/order/paidSubscription_test.go @@ -0,0 +1,32 @@ +package order + +import ( + "context" + "strings" + "testing" + + "github.com/perfect-panel/server/internal/model/user" + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +func TestPaidSubscriptionQueryIncludesOrderBackedSubscriptionWithoutToken(t *testing.T) { + db, err := gorm.Open(mysql.New(mysql.Config{ + DSN: "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local", + SkipInitializeWithVersion: true, + }), &gorm.Config{DryRun: true, DisableAutomaticPing: true}) + if err != nil { + t.Fatalf("open dry-run db: %v", err) + } + + var sub user.Subscribe + tx := paidSubscriptionQuery(context.Background(), db, 510).First(&sub) + sql := tx.Statement.SQL.String() + + if strings.Contains(sql, "token != ''") { + t.Fatalf("paid subscription query should not require non-empty token: %s", sql) + } + if !strings.Contains(sql, "order_id > 0 OR token LIKE 'iap:%'") { + t.Fatalf("paid subscription query should include order-backed or iap-backed subscriptions: %s", sql) + } +} diff --git a/internal/logic/public/order/preCreateOrderLogic.go b/internal/logic/public/order/preCreateOrderLogic.go index 1759d53..be16561 100644 --- a/internal/logic/public/order/preCreateOrderLogic.go +++ b/internal/logic/public/order/preCreateOrderLogic.go @@ -88,13 +88,8 @@ func (l *PreCreateOrderLogic) PreCreateOrder(req *types.PurchaseOrderRequest) (r // routes the request to renewal semantics, where first-purchase promos are disabled. if !l.svcCtx.Config.Subscribe.SingleModel && orderType == 1 { var existSub user.Subscribe - if e := l.svcCtx.DB.WithContext(l.ctx). - Model(&user.Subscribe{}). - Where("user_id = ? AND token != '' AND (order_id > 0 OR token LIKE 'iap:%')", entitlement.EffectiveUserID). - Order("expire_time DESC"). - Order("updated_at DESC"). - Order("id DESC"). - First(&existSub).Error; e == nil && existSub.Id > 0 && existSub.Token != "" { + if e := paidSubscriptionQuery(l.ctx, l.svcCtx.DB, entitlement.EffectiveUserID). + First(&existSub).Error; e == nil && existSub.Id > 0 { orderType = 2 l.Infow("[PreCreateOrder] purchase preview routed to renewal because an existing subscription was found", logger.Field("route_mode", "global_single_subscription"), diff --git a/internal/logic/public/order/purchaseLogic.go b/internal/logic/public/order/purchaseLogic.go index 3ba1978..f53df65 100644 --- a/internal/logic/public/order/purchaseLogic.go +++ b/internal/logic/public/order/purchaseLogic.go @@ -129,13 +129,8 @@ func (l *PurchaseLogic) Purchase(req *types.PurchaseOrderRequest) (resp *types.P // 防止不同套餐购买创建第二条订阅。 if !l.svcCtx.Config.Subscribe.SingleModel && orderType == 1 { var existSub user.Subscribe - if e := l.svcCtx.DB.WithContext(l.ctx). - Model(&user.Subscribe{}). - Where("user_id = ? AND token != '' AND (order_id > 0 OR token LIKE 'iap:%')", entitlement.EffectiveUserID). - Order("expire_time DESC"). - Order("updated_at DESC"). - Order("id DESC"). - First(&existSub).Error; e == nil && existSub.Id > 0 && existSub.Token != "" { + if e := paidSubscriptionQuery(l.ctx, l.svcCtx.DB, entitlement.EffectiveUserID). + First(&existSub).Error; e == nil && existSub.Id > 0 { orderType = 2 parentOrderID = existSub.OrderId subscribeToken = existSub.Token diff --git a/internal/model/user/subscribe.go b/internal/model/user/subscribe.go index 61613a5..296e124 100644 --- a/internal/model/user/subscribe.go +++ b/internal/model/user/subscribe.go @@ -67,7 +67,7 @@ func (m *defaultUserModel) FindSingleModeAnchorSubscribe(ctx context.Context, us var data Subscribe err := m.QueryNoCacheCtx(ctx, &data, func(conn *gorm.DB, _ interface{}) error { return conn.Model(&Subscribe{}). - Where("user_id = ? AND token != '' AND (order_id > 0 OR token LIKE 'iap:%') AND `status` IN ?", userId, []int64{0, 1, 2, 3, 4, 5}). + Where("user_id = ? AND (order_id > 0 OR token LIKE 'iap:%') AND `status` IN ?", userId, []int64{0, 1, 2, 3, 4, 5}). Order("expire_time DESC"). Order("updated_at DESC"). Order("id DESC").