From 6628ce06db008c91ea75d4149b4d75f6e7552ca2 Mon Sep 17 00:00:00 2001 From: shanshanzhong Date: Wed, 27 May 2026 05:59:57 -0700 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D(#75):=20=E6=8C=89=E6=95=B0?= =?UTF-8?q?=E9=87=8F=E5=8C=B9=E9=85=8D=E5=A5=97=E9=A4=90=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E4=BF=83=E9=94=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: multica-agent --- internal/logic/public/subscribe/promo.go | 20 +++++--- internal/logic/public/subscribe/promo_test.go | 51 +++++++++++++++++++ internal/model/promo/promo.go | 1 - 3 files changed, 63 insertions(+), 9 deletions(-) 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"`