修复(#128): 统一续费场景促销判断
Build docker and publish / build (20.15.1) (push) Failing after 23m3s
Build docker and publish / build (20.15.1) (pull_request) Failing after 19m4s

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
2026-05-30 23:12:39 -07:00
parent 1e99cfb83c
commit ae126296e3
5 changed files with 54 additions and 15 deletions
@@ -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")
}
@@ -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)
}
}
@@ -88,13 +88,8 @@ func (l *PreCreateOrderLogic) PreCreateOrder(req *types.PurchaseOrderRequest) (r
// routes the request to renewal semantics, where first-purchase promos are disabled. // routes the request to renewal semantics, where first-purchase promos are disabled.
if !l.svcCtx.Config.Subscribe.SingleModel && orderType == 1 { if !l.svcCtx.Config.Subscribe.SingleModel && orderType == 1 {
var existSub user.Subscribe var existSub user.Subscribe
if e := l.svcCtx.DB.WithContext(l.ctx). if e := paidSubscriptionQuery(l.ctx, l.svcCtx.DB, entitlement.EffectiveUserID).
Model(&user.Subscribe{}). First(&existSub).Error; e == nil && existSub.Id > 0 {
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 != "" {
orderType = 2 orderType = 2
l.Infow("[PreCreateOrder] purchase preview routed to renewal because an existing subscription was found", l.Infow("[PreCreateOrder] purchase preview routed to renewal because an existing subscription was found",
logger.Field("route_mode", "global_single_subscription"), logger.Field("route_mode", "global_single_subscription"),
+2 -7
View File
@@ -129,13 +129,8 @@ func (l *PurchaseLogic) Purchase(req *types.PurchaseOrderRequest) (resp *types.P
// 防止不同套餐购买创建第二条订阅。 // 防止不同套餐购买创建第二条订阅。
if !l.svcCtx.Config.Subscribe.SingleModel && orderType == 1 { if !l.svcCtx.Config.Subscribe.SingleModel && orderType == 1 {
var existSub user.Subscribe var existSub user.Subscribe
if e := l.svcCtx.DB.WithContext(l.ctx). if e := paidSubscriptionQuery(l.ctx, l.svcCtx.DB, entitlement.EffectiveUserID).
Model(&user.Subscribe{}). First(&existSub).Error; e == nil && existSub.Id > 0 {
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 != "" {
orderType = 2 orderType = 2
parentOrderID = existSub.OrderId parentOrderID = existSub.OrderId
subscribeToken = existSub.Token subscribeToken = existSub.Token
+1 -1
View File
@@ -67,7 +67,7 @@ func (m *defaultUserModel) FindSingleModeAnchorSubscribe(ctx context.Context, us
var data Subscribe var data Subscribe
err := m.QueryNoCacheCtx(ctx, &data, func(conn *gorm.DB, _ interface{}) error { err := m.QueryNoCacheCtx(ctx, &data, func(conn *gorm.DB, _ interface{}) error {
return conn.Model(&Subscribe{}). 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("expire_time DESC").
Order("updated_at DESC"). Order("updated_at DESC").
Order("id DESC"). Order("id DESC").