diff --git a/internal/logic/public/subscribe/promo.go b/internal/logic/public/subscribe/promo.go index ae4d711..1c0865c 100644 --- a/internal/logic/public/subscribe/promo.go +++ b/internal/logic/public/subscribe/promo.go @@ -90,7 +90,16 @@ func loadSubscribePromoMap(ctx context.Context, svcCtx *svc.ServiceContext, subs func querySubscribePromoCandidates(ctx context.Context, svcCtx *svc.ServiceContext, subscribeIDs []int64, loggedIn bool) ([]subscribePromoCandidate, error) { var candidates []subscribePromoCandidate - query := svcCtx.DB.WithContext(ctx). + err := subscribePromoCandidatesQuery(ctx, svcCtx.DB, subscribeIDs, loggedIn). + Scan(&candidates).Error + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query subscribe promo candidates failed: %v", err) + } + return candidates, nil +} + +func subscribePromoCandidatesQuery(ctx context.Context, db *gorm.DB, subscribeIDs []int64, loggedIn bool) *gorm.DB { + query := db.WithContext(ctx). Table("subscribe_promo AS sp"). Select("sp.subscribe_id, sp.quantity, sp.promo_price, pr.name AS rule_name, pr.type AS rule_type, pr.params, pr.start_time, pr.end_time"). Joins("JOIN promo_rule AS pr ON pr.id = sp.promo_rule_id AND pr.deleted_at IS NULL"). @@ -98,16 +107,11 @@ func querySubscribePromoCandidates(ctx context.Context, svcCtx *svc.ServiceConte if !loggedIn { query = query.Where("pr.type = ?", promoRuleTypeCampaign) } - err := query. + return query. Order("sp.subscribe_id ASC"). Order("sp.quantity ASC"). Order("pr.priority DESC"). - Order("pr.id ASC"). - Scan(&candidates).Error - if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query subscribe promo candidates failed: %v", err) - } - return candidates, nil + Order("pr.id ASC") } func (c subscribePromoCandidate) isActive(now time.Time) bool { diff --git a/internal/logic/public/subscribe/promo_test.go b/internal/logic/public/subscribe/promo_test.go index 1f432b9..4a20e0c 100644 --- a/internal/logic/public/subscribe/promo_test.go +++ b/internal/logic/public/subscribe/promo_test.go @@ -1,11 +1,13 @@ package subscribe import ( + "context" "strings" "testing" "time" "github.com/perfect-panel/server/internal/model/user" + "github.com/perfect-panel/server/internal/types" "gorm.io/driver/mysql" "gorm.io/gorm" ) @@ -107,3 +109,52 @@ func TestLastSubscribeExpireAtPrioritizesPermanentSubscription(t *testing.T) { t.Fatalf("permanent subscription order var = %v, want %v; vars=%v", got, want, tx.Statement.Vars) } } + +func TestQuerySubscribePromoCandidatesIncludesQuantity(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 candidates []subscribePromoCandidate + tx := subscribePromoCandidatesQuery(context.Background(), db, []int64{11, 12}, true).Scan(&candidates) + stmt := tx.Statement + sql := stmt.SQL.String() + if !strings.Contains(sql, "sp.subscribe_id, sp.quantity, sp.promo_price") { + t.Fatalf("SQL missing quantity select: %s", sql) + } + if !strings.Contains(sql, "ORDER BY sp.subscribe_id ASC,sp.quantity ASC,pr.priority DESC,pr.id ASC") { + t.Fatalf("SQL missing quantity order: %s", sql) + } +} + +func TestApplySubscribeDiscountPromosMatchesQuantity(t *testing.T) { + subscribe := types.Subscribe{Discount: []types.SubscribeDiscount{ + {Quantity: 1}, + {Quantity: 12}, + }} + promos := map[int64]*types.SubscribePromo{ + 3: {RuleName: "季度优惠", PromoPrice: 2900}, + 12: {RuleName: "年度优惠", PromoPrice: 9900}, + } + + applySubscribeDiscountPromos(&subscribe, promos) + if subscribe.Discount[0].Promo != nil { + t.Fatalf("quantity 1 promo should be nil, got %+v", subscribe.Discount[0].Promo) + } + if subscribe.Discount[1].Promo == nil { + t.Fatal("quantity 12 promo should match") + } + if got, want := subscribe.Discount[1].Promo.RuleName, "年度优惠"; got != want { + t.Fatalf("promo rule name = %q, want %q", got, want) + } + + subscribe = types.Subscribe{Discount: []types.SubscribeDiscount{{Quantity: 6}}} + applySubscribeDiscountPromos(&subscribe, promos) + if subscribe.Discount[0].Promo != nil { + t.Fatalf("promo should be nil when quantity does not match, got %+v", subscribe.Discount[0].Promo) + } +} diff --git a/internal/model/promo/promo.go b/internal/model/promo/promo.go index 053878f..5218d29 100644 --- a/internal/model/promo/promo.go +++ b/internal/model/promo/promo.go @@ -35,7 +35,6 @@ type SubscribePromo struct { SubscribeId int64 `gorm:"type:bigint unsigned;not null;comment:Subscribe ID"` Quantity int64 `gorm:"type:bigint;not null;default:1;comment:Quantity"` PromoRuleId int64 `gorm:"type:bigint unsigned;not null;comment:Promo Rule ID"` - Quantity int64 `gorm:"type:bigint;not null;default:1;comment:Subscribe Quantity"` PromoPrice int64 `gorm:"type:bigint;not null;default:0;comment:Promo Price"` CreatedAt time.Time `gorm:"<-:create;comment:Create Time"` UpdatedAt time.Time `gorm:"comment:Update Time"`