From 0a897419a51db012d4ba9d2d642673cf26c23721 Mon Sep 17 00:00:00 2001 From: shanshanzhong Date: Wed, 27 May 2026 01:44:11 -0700 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D(#86):=20=E6=8E=92=E9=99=A4?= =?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 | 22 +++++++- .../logic/common/promoEligibility_test.go | 51 +++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 internal/logic/common/promoEligibility_test.go diff --git a/internal/logic/common/promoEligibility.go b/internal/logic/common/promoEligibility.go index f50a4bb..dfbbe3d 100644 --- a/internal/logic/common/promoEligibility.go +++ b/internal/logic/common/promoEligibility.go @@ -11,6 +11,7 @@ import ( "github.com/perfect-panel/server/pkg/xerr" "github.com/pkg/errors" "gorm.io/gorm" + "gorm.io/gorm/clause" ) type PromoResult struct { @@ -148,6 +149,12 @@ func evaluateInactiveUserPromo( err := db.WithContext(ctx). Model(&user.Subscribe{}). Where("user_id = ?", userID). + Order(clause.OrderBy{ + Expression: clause.Expr{ + SQL: "CASE WHEN expire_time = ? THEN 0 ELSE 1 END", + Vars: []interface{}{permanentSubscribeExpireTime()}, + }, + }). Order("expire_time DESC"). Limit(1). Take(&lastSub).Error @@ -158,8 +165,19 @@ func evaluateInactiveUserPromo( return false, time.Time{}, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query promo inactive user subscription failed") } - threshold := now.AddDate(0, -params.InactiveMonths, 0) - return lastSub.ExpireTime.Before(threshold) || lastSub.ExpireTime.Equal(threshold), ruleExpiresAt, nil + return isInactivePromoEligible(lastSub.ExpireTime, now, params.InactiveMonths), ruleExpiresAt, nil +} + +func isInactivePromoEligible(expireTime time.Time, now time.Time, inactiveMonths int) bool { + if expireTime.Equal(permanentSubscribeExpireTime()) { + return false + } + threshold := now.AddDate(0, -inactiveMonths, 0) + return expireTime.Before(threshold) || expireTime.Equal(threshold) +} + +func permanentSubscribeExpireTime() time.Time { + return time.UnixMilli(0) } func promoRuleExpiresAt(rule *promo.RuleWithPrice) time.Time { diff --git a/internal/logic/common/promoEligibility_test.go b/internal/logic/common/promoEligibility_test.go new file mode 100644 index 0000000..2be614b --- /dev/null +++ b/internal/logic/common/promoEligibility_test.go @@ -0,0 +1,51 @@ +package common + +import ( + "testing" + "time" +) + +func TestIsInactivePromoEligible(t *testing.T) { + now := time.Date(2026, time.May, 27, 12, 0, 0, 0, time.UTC) + + tests := []struct { + name string + expireAt time.Time + want bool + }{ + { + name: "expired before inactive threshold is eligible", + expireAt: now.AddDate(0, -4, 0), + want: true, + }, + { + name: "expired exactly at inactive threshold is eligible", + expireAt: now.AddDate(0, -3, 0), + want: true, + }, + { + name: "recently expired subscription is not eligible", + expireAt: now.AddDate(0, -2, 0), + want: false, + }, + { + name: "active future subscription is not eligible", + expireAt: now.Add(time.Hour), + want: false, + }, + { + name: "permanent subscription marker is not eligible", + expireAt: time.UnixMilli(0), + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := isInactivePromoEligible(tt.expireAt, now, 3) + if got != tt.want { + t.Fatalf("isInactivePromoEligible() = %v, want %v", got, tt.want) + } + }) + } +}