x
Build docker and publish / build (20.15.1) (push) Successful in 7m36s

This commit is contained in:
2026-03-30 21:05:21 -07:00
parent d351d9e436
commit e0b2be2058
35 changed files with 18692 additions and 116 deletions
@@ -0,0 +1,63 @@
package order
import (
"context"
"encoding/json"
"time"
commonLogic "github.com/perfect-panel/server/internal/logic/common"
"github.com/perfect-panel/server/internal/types"
"gorm.io/gorm"
)
type newUserDiscountEligibility struct {
Eligibility *commonLogic.NewUserEligibilityContext
Discounts []types.SubscribeDiscount
NewUserOnly bool
WithinWindow bool
EligibleForDiscount bool
}
func resolveNewUserDiscountEligibility(
ctx context.Context,
db *gorm.DB,
currentUserID int64,
subscribeID int64,
quantity int64,
discountJSON string,
) (*newUserDiscountEligibility, error) {
state := &newUserDiscountEligibility{}
if discountJSON == "" {
return state, nil
}
_ = json.Unmarshal([]byte(discountJSON), &state.Discounts)
state.NewUserOnly = isNewUserOnlyForQuantity(state.Discounts, quantity)
eligibility, err := commonLogic.ResolveNewUserEligibility(ctx, db, currentUserID)
if err != nil {
return nil, err
}
state.Eligibility = eligibility
state.WithinWindow = eligibility.IsNewUserAt(time.Now())
state.EligibleForDiscount = state.WithinWindow
if state.EligibleForDiscount && state.NewUserOnly {
historyCount, countErr := commonLogic.CountScopedSubscribePurchaseOrders(
ctx,
db,
eligibility.ScopeUserIDs,
subscribeID,
[]int64{2, 5},
"",
)
if countErr != nil {
return nil, countErr
}
if historyCount >= 1 {
state.EligibleForDiscount = false
}
}
return state, nil
}
@@ -2,9 +2,7 @@ package order
import (
"context"
"encoding/json"
"math"
"time"
commonLogic "github.com/perfect-panel/server/internal/logic/common"
"github.com/perfect-panel/server/internal/model/order"
@@ -105,47 +103,21 @@ func (l *PreCreateOrderLogic) PreCreateOrder(req *types.PurchaseOrderRequest) (r
}
}
// check new user only restriction (tier-level only)
// Only block users who registered more than 24h ago; users within 24h who already purchased
// are allowed to preview (they just won't receive the new-user discount).
var newUserOnly bool
if !isSingleModeRenewal && sub.Discount != "" {
var dis []types.SubscribeDiscount
_ = json.Unmarshal([]byte(sub.Discount), &dis)
newUserOnly = isNewUserOnlyForQuantity(dis, req.Quantity)
newUserDiscount, err := resolveNewUserDiscountEligibility(l.ctx, l.svcCtx.DB, u.Id, targetSubscribeID, req.Quantity, sub.Discount)
if err != nil {
l.Errorw("[PreCreateOrder] Database query error resolving new user eligibility",
logger.Field("error", err.Error()),
logger.Field("user_id", u.Id),
)
return nil, err
}
if newUserOnly {
if time.Since(u.CreatedAt) > 24*time.Hour {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SubscribeNewUserOnly), "not a new user")
}
if !isSingleModeRenewal && newUserDiscount.NewUserOnly && !newUserDiscount.WithinWindow {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SubscribeNewUserOnly), "not a new user")
}
var discount float64 = 1
isNewUserForDiscount := time.Since(u.CreatedAt) <= 24*time.Hour
if isNewUserForDiscount && sub.Discount != "" {
// If the matched tier is new_user_only, check whether the user has already purchased this plan.
// If so, they are not eligible for the new-user discount (but the order is still allowed).
var dis []types.SubscribeDiscount
_ = json.Unmarshal([]byte(sub.Discount), &dis)
if isNewUserOnlyForQuantity(dis, req.Quantity) {
var historyCount int64
if e := l.svcCtx.DB.Model(&order.Order{}).
Where("user_id = ? AND subscribe_id = ? AND type = 1 AND status IN ?",
u.Id, targetSubscribeID, []int64{2, 5}).
Count(&historyCount).Error; e != nil {
l.Errorw("[PreCreateOrder] Database query error checking new user discount eligibility",
logger.Field("error", e.Error()), logger.Field("user_id", u.Id))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "check new user discount history error: %v", e.Error())
}
if historyCount >= 1 {
isNewUserForDiscount = false
}
}
}
if sub.Discount != "" {
var dis []types.SubscribeDiscount
_ = json.Unmarshal([]byte(sub.Discount), &dis)
discount = getDiscount(dis, req.Quantity, isNewUserForDiscount)
if len(newUserDiscount.Discounts) > 0 {
discount = getDiscount(newUserDiscount.Discounts, req.Quantity, newUserDiscount.EligibleForDiscount)
}
price := sub.UnitPrice * req.Quantity
+42 -55
View File
@@ -153,32 +153,18 @@ func (l *PurchaseLogic) Purchase(req *types.PurchaseOrderRequest) (resp *types.P
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SubscribeOutOfStock), "subscribe out of stock")
}
var discount float64 = 1
isNewUserForDiscount := time.Since(u.CreatedAt) <= 24*time.Hour
if isNewUserForDiscount && sub.Discount != "" {
// If the matched tier is new_user_only, check whether the user has already purchased this plan.
// If so, they are not eligible for the new-user discount (but the order is still allowed).
var dis []types.SubscribeDiscount
_ = json.Unmarshal([]byte(sub.Discount), &dis)
if isNewUserOnlyForQuantity(dis, req.Quantity) {
var historyCount int64
if e := l.svcCtx.DB.Model(&order.Order{}).
Where("user_id = ? AND subscribe_id = ? AND type = 1 AND status IN ?",
u.Id, targetSubscribeID, []int{2, 5}).
Count(&historyCount).Error; e != nil {
l.Errorw("[Purchase] Database query error checking new user discount eligibility",
logger.Field("error", e.Error()), logger.Field("user_id", u.Id))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "check new user discount history error: %v", e.Error())
}
if historyCount >= 1 {
isNewUserForDiscount = false
}
}
newUserDiscount, err := resolveNewUserDiscountEligibility(l.ctx, l.svcCtx.DB, u.Id, targetSubscribeID, req.Quantity, sub.Discount)
if err != nil {
l.Errorw("[Purchase] Database query error resolving new user eligibility",
logger.Field("error", err.Error()),
logger.Field("user_id", u.Id),
)
return nil, err
}
if sub.Discount != "" {
var dis []types.SubscribeDiscount
_ = json.Unmarshal([]byte(sub.Discount), &dis)
discount = getDiscount(dis, req.Quantity, isNewUserForDiscount)
var discount float64 = 1
if len(newUserDiscount.Discounts) > 0 {
discount = getDiscount(newUserDiscount.Discounts, req.Quantity, newUserDiscount.EligibleForDiscount)
}
price := sub.UnitPrice * req.Quantity
// discount amount
@@ -272,23 +258,23 @@ func (l *PurchaseLogic) Purchase(req *types.PurchaseOrderRequest) (resp *types.P
UserId: u.Id,
SubscriptionUserId: entitlement.EffectiveUserID,
ParentId: parentOrderID,
OrderNo: tool.GenerateTradeNo(),
Type: orderType,
Quantity: req.Quantity,
Price: price,
Amount: amount,
Discount: discountAmount,
GiftAmount: deductionAmount,
Coupon: req.Coupon,
CouponDiscount: coupon,
PaymentId: payment.Id,
Method: canonicalOrderMethod(payment.Platform),
FeeAmount: feeAmount,
Status: 1,
IsNew: isNew,
SubscribeId: targetSubscribeID,
SubscribeToken: subscribeToken,
AppAccountToken: uuid.New().String(),
OrderNo: tool.GenerateTradeNo(),
Type: orderType,
Quantity: req.Quantity,
Price: price,
Amount: amount,
Discount: discountAmount,
GiftAmount: deductionAmount,
Coupon: req.Coupon,
CouponDiscount: coupon,
PaymentId: payment.Id,
Method: canonicalOrderMethod(payment.Platform),
FeeAmount: feeAmount,
Status: 1,
IsNew: isNew,
SubscribeId: targetSubscribeID,
SubscribeToken: subscribeToken,
AppAccountToken: uuid.New().String(),
}
if isSingleModeRenewal {
l.Infow("[Purchase] single mode purchase order created as renewal",
@@ -320,20 +306,21 @@ func (l *PurchaseLogic) Purchase(req *types.PurchaseOrderRequest) (resp *types.P
}
}
// check new user only restriction inside transaction to prevent race condition (tier-level only)
// Only block users who registered more than 24h ago; users within 24h who already purchased
// are allowed to place another order (they just won't receive the new-user discount).
if orderInfo.Type == 1 {
var txNewUserOnly bool
if sub.Discount != "" {
var dis []types.SubscribeDiscount
_ = json.Unmarshal([]byte(sub.Discount), &dis)
txNewUserOnly = isNewUserOnlyForQuantity(dis, orderInfo.Quantity)
// Re-check new-user-only restriction inside the transaction to prevent race conditions.
if orderInfo.Type == 1 && newUserDiscount.NewUserOnly {
txNewUserDiscount, txErr := resolveNewUserDiscountEligibility(
l.ctx,
db,
u.Id,
targetSubscribeID,
orderInfo.Quantity,
sub.Discount,
)
if txErr != nil {
return txErr
}
if txNewUserOnly {
if time.Since(u.CreatedAt) > 24*time.Hour {
return errors.Wrapf(xerr.NewErrCode(xerr.SubscribeNewUserOnly), "not a new user")
}
if !txNewUserDiscount.WithinWindow {
return errors.Wrapf(xerr.NewErrCode(xerr.SubscribeNewUserOnly), "not a new user")
}
}