Compare commits
11 Commits
98972401c2
...
06e4698888
| Author | SHA1 | Date | |
|---|---|---|---|
| 06e4698888 | |||
| e51dbea7c7 | |||
| c8f172dc0e | |||
| 52aaaf4de5 | |||
| 877a471e24 | |||
| 40cfe46ba8 | |||
| 5733ebe40d | |||
| 0b1e6ce3c3 | |||
| 3167465865 | |||
| 8838fc51f8 | |||
| ea34718a0b |
@ -21,7 +21,7 @@ env:
|
|||||||
SSH_PASSWORD: ${{ github.ref_name == 'main' && vars.SSH_PASSWORD || vars.DEV_SSH_PASSWORD }}
|
SSH_PASSWORD: ${{ github.ref_name == 'main' && vars.SSH_PASSWORD || vars.DEV_SSH_PASSWORD }}
|
||||||
# TG通知
|
# TG通知
|
||||||
TG_BOT_TOKEN: 8114337882:AAHkEx03HSu7RxN4IHBJJEnsK9aPPzNLIk0
|
TG_BOT_TOKEN: 8114337882:AAHkEx03HSu7RxN4IHBJJEnsK9aPPzNLIk0
|
||||||
TG_CHAT_ID: "-4940243803"
|
TG_CHAT_ID: "-49402438031"
|
||||||
# Go构建变量
|
# Go构建变量
|
||||||
SERVICE: vpn
|
SERVICE: vpn
|
||||||
SERVICE_STYLE: vpn
|
SERVICE_STYLE: vpn
|
||||||
|
|||||||
@ -76,7 +76,7 @@ func CountScopedSubscribePurchaseOrders(
|
|||||||
var count int64
|
var count int64
|
||||||
query := db.WithContext(ctx).
|
query := db.WithContext(ctx).
|
||||||
Model(&modelOrder.Order{}).
|
Model(&modelOrder.Order{}).
|
||||||
Where("user_id IN ? AND subscribe_id = ? AND type = 1", scopeUserIDs, subscribeID)
|
Where("user_id IN ? AND subscribe_id = ? AND type IN ? AND amount > 0", scopeUserIDs, subscribeID, []int64{1, 2})
|
||||||
if len(statuses) > 0 {
|
if len(statuses) > 0 {
|
||||||
query = query.Where("status IN ?", statuses)
|
query = query.Where("status IN ?", statuses)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,18 +2,33 @@ package order
|
|||||||
|
|
||||||
import "github.com/perfect-panel/server/internal/types"
|
import "github.com/perfect-panel/server/internal/types"
|
||||||
|
|
||||||
|
// getDiscount returns the discount factor for the given quantity.
|
||||||
|
//
|
||||||
|
// - New user: pick the tier with the lowest discount value (biggest saving).
|
||||||
|
// - Non-new user: pick the tier with the highest discount value (least saving / closest to full price).
|
||||||
func getDiscount(discounts []types.SubscribeDiscount, inputMonths int64, isNewUser bool) float64 {
|
func getDiscount(discounts []types.SubscribeDiscount, inputMonths int64, isNewUser bool) float64 {
|
||||||
for _, discount := range discounts {
|
best := float64(-1)
|
||||||
if discount.NewUserOnly && !isNewUser {
|
for _, d := range discounts {
|
||||||
|
if d.Quantity != inputMonths || d.Discount <= 0 || d.Discount >= 100 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if inputMonths == discount.Quantity && discount.Discount > 0 && discount.Discount < 100 {
|
if isNewUser {
|
||||||
return discount.Discount / float64(100)
|
// lowest discount value = biggest saving
|
||||||
|
if best < 0 || d.Discount < best {
|
||||||
|
best = d.Discount
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// highest discount value = least saving (closest to original price)
|
||||||
|
if best < 0 || d.Discount > best {
|
||||||
|
best = d.Discount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if best < 0 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
return best / float64(100)
|
||||||
|
}
|
||||||
|
|
||||||
// isNewUserOnlyForQuantity checks whether the matched discount tier has new_user_only enabled.
|
// isNewUserOnlyForQuantity checks whether the matched discount tier has new_user_only enabled.
|
||||||
// Returns true only when all matching tiers for the given quantity require new-user status
|
// Returns true only when all matching tiers for the given quantity require new-user status
|
||||||
|
|||||||
@ -82,7 +82,7 @@ func (l *PurchaseLogic) Purchase(req *types.PurchaseOrderRequest) (resp *types.P
|
|||||||
decision, routeErr := commonLogic.ResolvePurchaseRoute(
|
decision, routeErr := commonLogic.ResolvePurchaseRoute(
|
||||||
l.ctx,
|
l.ctx,
|
||||||
l.svcCtx.Config.Subscribe.SingleModel,
|
l.svcCtx.Config.Subscribe.SingleModel,
|
||||||
u.Id,
|
entitlement.EffectiveUserID,
|
||||||
req.SubscribeId,
|
req.SubscribeId,
|
||||||
l.svcCtx.UserModel.FindSingleModeAnchorSubscribe,
|
l.svcCtx.UserModel.FindSingleModeAnchorSubscribe,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -81,11 +81,17 @@ func (l *RenewalLogic) Renewal(req *types.RenewalOrderRequest) (resp *types.Rene
|
|||||||
if !*sub.Sell {
|
if !*sub.Sell {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "subscribe not sell")
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "subscribe not sell")
|
||||||
}
|
}
|
||||||
|
newUserDiscount, err := resolveNewUserDiscountEligibility(l.ctx, l.svcCtx.DB, u.Id, sub.Id, req.Quantity, sub.Discount)
|
||||||
|
if err != nil {
|
||||||
|
l.Errorw("[Renewal] Database query error resolving new user eligibility",
|
||||||
|
logger.Field("error", err.Error()),
|
||||||
|
logger.Field("user_id", u.Id),
|
||||||
|
)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
var discount float64 = 1
|
var discount float64 = 1
|
||||||
if sub.Discount != "" {
|
if len(newUserDiscount.Discounts) > 0 {
|
||||||
var dis []types.SubscribeDiscount
|
discount = getDiscount(newUserDiscount.Discounts, req.Quantity, newUserDiscount.EligibleForDiscount)
|
||||||
_ = json.Unmarshal([]byte(sub.Discount), &dis)
|
|
||||||
discount = getDiscount(dis, req.Quantity, false)
|
|
||||||
}
|
}
|
||||||
price := sub.UnitPrice * req.Quantity
|
price := sub.UnitPrice * req.Quantity
|
||||||
amount := int64(math.Round(float64(price) * discount))
|
amount := int64(math.Round(float64(price) * discount))
|
||||||
|
|||||||
@ -229,9 +229,14 @@ func (l *ActivateOrderLogic) NewPurchase(ctx context.Context, orderInfo *order.O
|
|||||||
|
|
||||||
var userSub *user.Subscribe
|
var userSub *user.Subscribe
|
||||||
|
|
||||||
// 单订阅模式下,优先兜底为“续费语义”:延长已购订阅,避免并发下重复创建 user_subscribe
|
// 单订阅模式下,优先兜底为”续费语义”:延长已购订阅,避免并发下重复创建 user_subscribe
|
||||||
if l.svc.Config.Subscribe.SingleModel {
|
if l.svc.Config.Subscribe.SingleModel {
|
||||||
anchorSub, anchorErr := l.svc.UserModel.FindSingleModeAnchorSubscribe(ctx, orderInfo.UserId)
|
// 家庭组场景:订阅实际属于 SubscriptionUserId(成员/主),而非付款人 UserId
|
||||||
|
singleModeUserId := orderInfo.UserId
|
||||||
|
if orderInfo.SubscriptionUserId > 0 {
|
||||||
|
singleModeUserId = orderInfo.SubscriptionUserId
|
||||||
|
}
|
||||||
|
anchorSub, anchorErr := l.svc.UserModel.FindSingleModeAnchorSubscribe(ctx, singleModeUserId)
|
||||||
switch {
|
switch {
|
||||||
case anchorErr == nil && anchorSub != nil:
|
case anchorErr == nil && anchorSub != nil:
|
||||||
if orderInfo.ParentId == 0 && anchorSub.OrderId > 0 && anchorSub.OrderId != orderInfo.Id {
|
if orderInfo.ParentId == 0 && anchorSub.OrderId > 0 && anchorSub.OrderId != orderInfo.Id {
|
||||||
@ -271,7 +276,7 @@ func (l *ActivateOrderLogic) NewPurchase(ctx context.Context, orderInfo *order.O
|
|||||||
|
|
||||||
// 如果没有合并已购订阅,再尝试合并赠送订阅(order_id=0)
|
// 如果没有合并已购订阅,再尝试合并赠送订阅(order_id=0)
|
||||||
if userSub == nil {
|
if userSub == nil {
|
||||||
giftSub, giftErr := l.findGiftSubscription(ctx, orderInfo.UserId, orderInfo.SubscribeId)
|
giftSub, giftErr := l.findGiftSubscription(ctx, singleModeUserId, orderInfo.SubscribeId)
|
||||||
if giftErr == nil && giftSub != nil {
|
if giftErr == nil && giftSub != nil {
|
||||||
// 在赠送订阅上延长时间,保持 token 不变
|
// 在赠送订阅上延长时间,保持 token 不变
|
||||||
userSub, err = l.extendGiftSubscription(ctx, giftSub, orderInfo, sub)
|
userSub, err = l.extendGiftSubscription(ctx, giftSub, orderInfo, sub)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user