@@ -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"),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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").
|
||||||
|
|||||||
Reference in New Issue
Block a user