From 25811526bdbd4a15eea9d1f8833f3c146d1e1639 Mon Sep 17 00:00:00 2001 From: shanshanzhong Date: Tue, 26 May 2026 22:18:19 -0700 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D(#81):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=B0=B8=E4=B9=85=E8=AE=A2=E9=98=85=E5=9B=9E=E5=BD=92=E4=BF=83?= =?UTF-8?q?=E9=94=80=E8=AF=AF=E5=88=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: multica-agent --- internal/logic/common/promoEligibility.go | 19 +++++++++---- .../logic/common/promoEligibility_test.go | 28 +++++++++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/internal/logic/common/promoEligibility.go b/internal/logic/common/promoEligibility.go index 6512a07..85ef01f 100644 --- a/internal/logic/common/promoEligibility.go +++ b/internal/logic/common/promoEligibility.go @@ -12,6 +12,7 @@ import ( "github.com/perfect-panel/server/pkg/xerr" "github.com/pkg/errors" "gorm.io/gorm" + "gorm.io/gorm/clause" ) const ( @@ -250,12 +251,7 @@ func (s *gormPromoEligibilitySource) UserCreatedAt(ctx context.Context, userID i func (s *gormPromoEligibilitySource) LastSubscribeExpireAt(ctx context.Context, userID int64) (time.Time, error) { var item user.Subscribe - err := s.db.WithContext(ctx). - Model(&user.Subscribe{}). - Where("user_id = ?", userID). - Where("expire_time != ?", time.UnixMilli(0)). - Order("expire_time DESC"). - Limit(1). + err := lastSubscribeExpireQuery(s.db.WithContext(ctx), userID). Take(&item).Error if err != nil { if stderrors.Is(err, gorm.ErrRecordNotFound) { @@ -266,6 +262,17 @@ func (s *gormPromoEligibilitySource) LastSubscribeExpireAt(ctx context.Context, return item.ExpireTime, nil } +func lastSubscribeExpireQuery(db *gorm.DB, userID int64) *gorm.DB { + return db. + Model(&user.Subscribe{}). + Where("user_id = ?", userID). + Order(clause.OrderBy{Expression: clause.Expr{ + SQL: "CASE WHEN expire_time = ? THEN 0 ELSE 1 END ASC, expire_time DESC", + Vars: []interface{}{time.UnixMilli(0)}, + }}). + Limit(1) +} + func loadEnabledPromoRules(ctx context.Context, svcCtx *svc.ServiceContext) ([]promoRule, error) { if cached, ok := getPromoCache[[]promoRule](ctx, svcCtx, promoRulesEnabledCacheKey); ok { return cached, nil diff --git a/internal/logic/common/promoEligibility_test.go b/internal/logic/common/promoEligibility_test.go index 9b69654..b01f273 100644 --- a/internal/logic/common/promoEligibility_test.go +++ b/internal/logic/common/promoEligibility_test.go @@ -2,8 +2,13 @@ package common import ( "context" + "strings" "testing" "time" + + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" ) type fakePromoEligibilitySource struct { @@ -148,3 +153,26 @@ func TestEvaluatePromoInactiveUser(t *testing.T) { t.Fatal("inactive promo should require positive inactive_months") } } + +func TestLastSubscribeExpireAtIncludesUnlimitedSubscription(t *testing.T) { + db, err := gorm.Open(mysql.New(mysql.Config{ + DSN: "gorm:password@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local", + SkipInitializeWithVersion: true, + }), &gorm.Config{ + DryRun: true, + DisableAutomaticPing: true, + Logger: logger.Default.LogMode(logger.Silent), + }) + if err != nil { + t.Fatalf("open gorm db: %v", err) + } + + stmt := lastSubscribeExpireQuery(db, 10).Take(nil).Statement + sql := strings.ToLower(stmt.SQL.String()) + if strings.Contains(sql, "expire_time <>") || strings.Contains(sql, "expire_time !=") { + t.Fatalf("last subscribe query should include unlimited subscription, sql: %s", stmt.SQL.String()) + } + if !strings.Contains(sql, "case when expire_time = ? then 0 else 1 end asc") { + t.Fatalf("last subscribe query should prioritize unlimited subscription, sql: %s", stmt.SQL.String()) + } +}