+1
-1
@@ -229,6 +229,7 @@ type (
|
||||
Quantity int64 `json:"quantity"`
|
||||
Discount float64 `json:"discount"`
|
||||
MapApple string `json:"map_apple"`
|
||||
Promo *SubscribePromo `json:"promo"`
|
||||
}
|
||||
SubscribePromo {
|
||||
RuleName string `json:"rule_name"`
|
||||
@@ -250,7 +251,6 @@ type (
|
||||
UnitPrice int64 `json:"unit_price"`
|
||||
UnitTime string `json:"unit_time"`
|
||||
Discount []SubscribeDiscount `json:"discount"`
|
||||
Promo *SubscribePromo `json:"promo"`
|
||||
NodeCount int64 `json:"node_count"`
|
||||
Replacement int64 `json:"replacement"`
|
||||
Inventory int64 `json:"inventory"`
|
||||
|
||||
+5
-1
@@ -4316,12 +4316,16 @@
|
||||
"discount": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"promo": {
|
||||
"$ref": "#/definitions/SubscribePromo"
|
||||
}
|
||||
},
|
||||
"title": "SubscribeDiscount",
|
||||
"required": [
|
||||
"quantity",
|
||||
"discount"
|
||||
"discount",
|
||||
"promo"
|
||||
]
|
||||
},
|
||||
"SubscribeGroup": {
|
||||
|
||||
@@ -18,15 +18,70 @@ CREATE TABLE IF NOT EXISTS `promo_rule` (
|
||||
CREATE TABLE IF NOT EXISTS `subscribe_promo` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`subscribe_id` BIGINT UNSIGNED NOT NULL COMMENT '套餐规格 ID',
|
||||
`quantity` BIGINT NOT NULL DEFAULT 1 COMMENT '购买数量',
|
||||
`promo_rule_id` BIGINT UNSIGNED NOT NULL COMMENT '促销规则 ID',
|
||||
`promo_price` BIGINT NOT NULL DEFAULT 0 COMMENT '该规格在此规则下的优惠价(分)',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_subscribe_rule` (`subscribe_id`, `promo_rule_id`),
|
||||
UNIQUE KEY `uk_subscribe_quantity_rule` (`subscribe_id`, `quantity`, `promo_rule_id`),
|
||||
KEY `idx_promo_rule_id` (`promo_rule_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='规格促销价表';
|
||||
|
||||
SET @column_exists = (
|
||||
SELECT COUNT(*)
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'subscribe_promo'
|
||||
AND COLUMN_NAME = 'quantity'
|
||||
);
|
||||
|
||||
SET @sql = IF(
|
||||
@column_exists = 0,
|
||||
'ALTER TABLE `subscribe_promo` ADD COLUMN `quantity` BIGINT NOT NULL DEFAULT 1 COMMENT ''购买数量'' AFTER `subscribe_id`',
|
||||
'SELECT ''Column quantity already exists in subscribe_promo table'''
|
||||
);
|
||||
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_exists = (
|
||||
SELECT COUNT(*)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'subscribe_promo'
|
||||
AND INDEX_NAME = 'uk_subscribe_rule'
|
||||
);
|
||||
|
||||
SET @sql = IF(
|
||||
@index_exists = 1,
|
||||
'ALTER TABLE `subscribe_promo` DROP INDEX `uk_subscribe_rule`',
|
||||
'SELECT ''Index uk_subscribe_rule does not exist on subscribe_promo table'''
|
||||
);
|
||||
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_exists = (
|
||||
SELECT COUNT(*)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'subscribe_promo'
|
||||
AND INDEX_NAME = 'uk_subscribe_quantity_rule'
|
||||
);
|
||||
|
||||
SET @sql = IF(
|
||||
@index_exists = 0,
|
||||
'ALTER TABLE `subscribe_promo` ADD UNIQUE KEY `uk_subscribe_quantity_rule` (`subscribe_id`, `quantity`, `promo_rule_id`)',
|
||||
'SELECT ''Index uk_subscribe_quantity_rule already exists on subscribe_promo table'''
|
||||
);
|
||||
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `promo_usage` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户 ID',
|
||||
|
||||
@@ -27,13 +27,13 @@ type promoRuleParams struct {
|
||||
InactiveMonths int `json:"inactive_months"`
|
||||
}
|
||||
|
||||
func EvaluatePromo(ctx context.Context, svcCtx *svc.ServiceContext, userID int64, subscribeID int64) (*PromoResult, error) {
|
||||
func EvaluatePromo(ctx context.Context, svcCtx *svc.ServiceContext, userID int64, subscribeID int64, quantity int64) (*PromoResult, error) {
|
||||
result := &PromoResult{}
|
||||
if svcCtx == nil || svcCtx.PromoModel == nil || svcCtx.DB == nil || userID <= 0 || subscribeID <= 0 {
|
||||
if svcCtx == nil || svcCtx.PromoModel == nil || svcCtx.DB == nil || userID <= 0 || subscribeID <= 0 || quantity <= 0 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
rules, err := svcCtx.PromoModel.QueryEligibleRules(ctx, subscribeID)
|
||||
rules, err := svcCtx.PromoModel.QueryEligibleRules(ctx, subscribeID, quantity)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query promo rules failed: %v", err.Error())
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ func (l *PreCreateOrderLogic) PreCreateOrder(req *types.PurchaseOrderRequest) (r
|
||||
}
|
||||
|
||||
targetSubscribeID := req.SubscribeId
|
||||
orderType := uint8(1)
|
||||
isSingleModeRenewal := false
|
||||
decision, routeErr := commonLogic.ResolvePurchaseRoute(
|
||||
l.ctx,
|
||||
@@ -68,6 +69,7 @@ func (l *PreCreateOrderLogic) PreCreateOrder(req *types.PurchaseOrderRequest) (r
|
||||
targetSubscribeID = decision.ResolvedSubscribeID
|
||||
isSingleModeRenewal = decision.Route == commonLogic.PurchaseRoutePurchaseToRenewal
|
||||
if isSingleModeRenewal && decision.Anchor != nil {
|
||||
orderType = 2
|
||||
l.Infow("[PreCreateOrder] single mode purchase routed to renewal preview",
|
||||
logger.Field("mode", "single"),
|
||||
logger.Field("route", "purchase_to_renewal"),
|
||||
@@ -77,6 +79,32 @@ func (l *PreCreateOrderLogic) PreCreateOrder(req *types.PurchaseOrderRequest) (r
|
||||
}
|
||||
}
|
||||
|
||||
// Keep promo eligibility preview aligned with Purchase: an existing paid subscription
|
||||
// routes the request to renewal semantics, where first-purchase promos are disabled.
|
||||
if !l.svcCtx.Config.Subscribe.SingleModel && orderType == 1 {
|
||||
var existSub user.Subscribe
|
||||
if e := l.svcCtx.DB.WithContext(l.ctx).
|
||||
Model(&user.Subscribe{}).
|
||||
Where("user_id = ? AND token != '' AND (order_id > 0 OR token LIKE 'iap:%')", u.Id).
|
||||
Order("expire_time DESC").
|
||||
Order("updated_at DESC").
|
||||
Order("id DESC").
|
||||
First(&existSub).Error; e == nil && existSub.Id > 0 && existSub.Token != "" {
|
||||
orderType = 2
|
||||
l.Infow("[PreCreateOrder] purchase preview routed to renewal because an existing subscription was found",
|
||||
logger.Field("route_mode", "global_single_subscription"),
|
||||
logger.Field("route", "purchase_to_existing_subscription"),
|
||||
logger.Field("existing_subscribe_id", existSub.Id),
|
||||
logger.Field("existing_status", existSub.Status),
|
||||
logger.Field("user_id", u.Id),
|
||||
logger.Field("resolved_subscribe_id", targetSubscribeID),
|
||||
)
|
||||
} else if e != nil && !errors.Is(e, gorm.ErrRecordNotFound) {
|
||||
l.Errorw("[PreCreateOrder] Database query error", logger.Field("error", e.Error()), logger.Field("user_id", u.Id))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find existing subscription error: %v", e.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// find subscribe plan
|
||||
sub, err := l.svcCtx.SubscribeModel.FindOne(l.ctx, targetSubscribeID)
|
||||
if err != nil {
|
||||
@@ -123,7 +151,7 @@ func (l *PreCreateOrderLogic) PreCreateOrder(req *types.PurchaseOrderRequest) (r
|
||||
req.Quantity,
|
||||
newUserDiscount.Discounts,
|
||||
newUserDiscount.EligibleForDiscount,
|
||||
!isSingleModeRenewal,
|
||||
orderType == 1,
|
||||
)
|
||||
if err != nil {
|
||||
l.Errorw("[PreCreateOrder] Promo price calculation error",
|
||||
|
||||
@@ -36,7 +36,7 @@ func calculatePurchasePrice(
|
||||
}
|
||||
|
||||
if allowPromo {
|
||||
promoResult, err := commonLogic.EvaluatePromo(ctx, svcCtx, userID, subscribeID)
|
||||
promoResult, err := commonLogic.EvaluatePromo(ctx, svcCtx, userID, subscribeID, quantity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -11,21 +11,30 @@ import (
|
||||
)
|
||||
|
||||
type fakePromoModel struct {
|
||||
rules []*promo.RuleWithPrice
|
||||
rules []*promo.RuleWithPrice
|
||||
lastSubscribeID int64
|
||||
lastQuantity int64
|
||||
requireQuantity int64
|
||||
quantityMismatch []*promo.RuleWithPrice
|
||||
}
|
||||
|
||||
func (m fakePromoModel) QueryEligibleRules(context.Context, int64) ([]*promo.RuleWithPrice, error) {
|
||||
func (m *fakePromoModel) QueryEligibleRules(_ context.Context, subscribeID int64, quantity int64) ([]*promo.RuleWithPrice, error) {
|
||||
m.lastSubscribeID = subscribeID
|
||||
m.lastQuantity = quantity
|
||||
if m.requireQuantity > 0 && quantity != m.requireQuantity {
|
||||
return m.quantityMismatch, nil
|
||||
}
|
||||
return m.rules, nil
|
||||
}
|
||||
|
||||
func (m fakePromoModel) InsertUsage(context.Context, *promo.Usage, ...*gorm.DB) error {
|
||||
func (m *fakePromoModel) InsertUsage(context.Context, *promo.Usage, ...*gorm.DB) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestCalculatePurchasePricePromoSkipsPercentDiscount(t *testing.T) {
|
||||
svcCtx := &svc.ServiceContext{
|
||||
DB: &gorm.DB{},
|
||||
PromoModel: fakePromoModel{rules: []*promo.RuleWithPrice{
|
||||
PromoModel: &fakePromoModel{rules: []*promo.RuleWithPrice{
|
||||
{
|
||||
Rule: promo.Rule{
|
||||
Id: 9,
|
||||
@@ -73,7 +82,7 @@ func TestCalculatePurchasePricePromoSkipsPercentDiscount(t *testing.T) {
|
||||
func TestCalculatePurchasePriceIgnoresInvalidPromoPrice(t *testing.T) {
|
||||
svcCtx := &svc.ServiceContext{
|
||||
DB: &gorm.DB{},
|
||||
PromoModel: fakePromoModel{rules: []*promo.RuleWithPrice{
|
||||
PromoModel: &fakePromoModel{rules: []*promo.RuleWithPrice{
|
||||
{
|
||||
Rule: promo.Rule{
|
||||
Id: 10,
|
||||
@@ -111,3 +120,52 @@ func TestCalculatePurchasePriceIgnoresInvalidPromoPrice(t *testing.T) {
|
||||
t.Fatalf("promo fields = (%d, %d), want (0, 0)", result.PromoRuleId, result.PromoDiscount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculatePurchasePricePassesQuantityToPromoEvaluation(t *testing.T) {
|
||||
promoModel := &fakePromoModel{
|
||||
requireQuantity: 6,
|
||||
rules: []*promo.RuleWithPrice{
|
||||
{
|
||||
Rule: promo.Rule{
|
||||
Id: 11,
|
||||
Name: "quantity campaign",
|
||||
Type: promo.RuleTypeCampaign,
|
||||
Enabled: true,
|
||||
},
|
||||
PromoPrice: 500,
|
||||
},
|
||||
},
|
||||
}
|
||||
svcCtx := &svc.ServiceContext{
|
||||
DB: &gorm.DB{},
|
||||
PromoModel: promoModel,
|
||||
}
|
||||
|
||||
result, err := calculatePurchasePrice(
|
||||
context.Background(),
|
||||
svcCtx,
|
||||
1,
|
||||
2,
|
||||
1000,
|
||||
6,
|
||||
[]types.SubscribeDiscount{{Quantity: 6, Discount: 80}},
|
||||
true,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("calculatePurchasePrice returned error: %v", err)
|
||||
}
|
||||
|
||||
if promoModel.lastSubscribeID != 2 {
|
||||
t.Fatalf("lastSubscribeID = %d, want 2", promoModel.lastSubscribeID)
|
||||
}
|
||||
if promoModel.lastQuantity != 6 {
|
||||
t.Fatalf("lastQuantity = %d, want 6", promoModel.lastQuantity)
|
||||
}
|
||||
if result.PayableBase != 3000 {
|
||||
t.Fatalf("PayableBase = %d, want 3000", result.PayableBase)
|
||||
}
|
||||
if result.PromoRuleId != 11 {
|
||||
t.Fatalf("PromoRuleId = %d, want 11", result.PromoRuleId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ const (
|
||||
|
||||
type subscribePromoCandidate struct {
|
||||
SubscribeId int64 `gorm:"column:subscribe_id"`
|
||||
Quantity int64 `gorm:"column:quantity"`
|
||||
RuleName string `gorm:"column:rule_name"`
|
||||
RuleType string `gorm:"column:rule_type"`
|
||||
PromoPrice int64 `gorm:"column:promo_price"`
|
||||
@@ -38,8 +39,8 @@ type promoRuleParams struct {
|
||||
InactiveMonths int `json:"inactive_months"`
|
||||
}
|
||||
|
||||
func loadSubscribePromoMap(ctx context.Context, svcCtx *svc.ServiceContext, subscribeIDs []int64) (map[int64]*types.SubscribePromo, error) {
|
||||
result := make(map[int64]*types.SubscribePromo)
|
||||
func loadSubscribePromoMap(ctx context.Context, svcCtx *svc.ServiceContext, subscribeIDs []int64) (map[int64]map[int64]*types.SubscribePromo, error) {
|
||||
result := make(map[int64]map[int64]*types.SubscribePromo)
|
||||
if len(subscribeIDs) == 0 || svcCtx == nil || svcCtx.DB == nil {
|
||||
return result, nil
|
||||
}
|
||||
@@ -56,7 +57,13 @@ func loadSubscribePromoMap(ctx context.Context, svcCtx *svc.ServiceContext, subs
|
||||
evaluator := promoEligibilityEvaluator{ctx: ctx, db: svcCtx.DB, userInfo: userInfo}
|
||||
now := time.Now()
|
||||
for _, candidate := range candidates {
|
||||
if _, exists := result[candidate.SubscribeId]; exists {
|
||||
if candidate.Quantity <= 0 {
|
||||
continue
|
||||
}
|
||||
if result[candidate.SubscribeId] == nil {
|
||||
result[candidate.SubscribeId] = make(map[int64]*types.SubscribePromo)
|
||||
}
|
||||
if _, exists := result[candidate.SubscribeId][candidate.Quantity]; exists {
|
||||
continue
|
||||
}
|
||||
if !candidate.isActive(now) {
|
||||
@@ -69,7 +76,7 @@ func loadSubscribePromoMap(ctx context.Context, svcCtx *svc.ServiceContext, subs
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
result[candidate.SubscribeId] = &types.SubscribePromo{
|
||||
result[candidate.SubscribeId][candidate.Quantity] = &types.SubscribePromo{
|
||||
RuleName: candidate.RuleName,
|
||||
RuleType: candidate.RuleType,
|
||||
PromoPrice: candidate.PromoPrice,
|
||||
@@ -84,7 +91,7 @@ func querySubscribePromoCandidates(ctx context.Context, svcCtx *svc.ServiceConte
|
||||
var candidates []subscribePromoCandidate
|
||||
query := svcCtx.DB.WithContext(ctx).
|
||||
Table("subscribe_promo AS sp").
|
||||
Select("sp.subscribe_id, sp.promo_price, pr.name AS rule_name, pr.type AS rule_type, pr.params, pr.start_time, pr.end_time").
|
||||
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").
|
||||
Where("sp.subscribe_id IN ? AND sp.promo_price > 0 AND pr.enabled = ?", subscribeIDs, true)
|
||||
if !loggedIn {
|
||||
@@ -92,6 +99,7 @@ func querySubscribePromoCandidates(ctx context.Context, svcCtx *svc.ServiceConte
|
||||
}
|
||||
err := query.
|
||||
Order("sp.subscribe_id ASC").
|
||||
Order("sp.quantity ASC").
|
||||
Order("pr.priority DESC").
|
||||
Order("pr.id ASC").
|
||||
Scan(&candidates).Error
|
||||
|
||||
@@ -77,7 +77,13 @@ func (l *QuerySubscribeListLogic) QuerySubscribeList(req *types.QuerySubscribeLi
|
||||
return nil, err
|
||||
}
|
||||
for i := range list {
|
||||
list[i].Promo = promos[list[i].Id]
|
||||
subscribePromos := promos[list[i].Id]
|
||||
for j := range list[i].Discount {
|
||||
if subscribePromos == nil {
|
||||
continue
|
||||
}
|
||||
list[i].Discount[j].Promo = subscribePromos[list[i].Discount[j].Quantity]
|
||||
}
|
||||
}
|
||||
|
||||
resp.List = list
|
||||
|
||||
@@ -13,7 +13,7 @@ type RuleWithPrice struct {
|
||||
}
|
||||
|
||||
type Model interface {
|
||||
QueryEligibleRules(ctx context.Context, subscribeId int64) ([]*RuleWithPrice, error)
|
||||
QueryEligibleRules(ctx context.Context, subscribeId int64, quantity int64) ([]*RuleWithPrice, error)
|
||||
InsertUsage(ctx context.Context, data *Usage, tx ...*gorm.DB) error
|
||||
}
|
||||
|
||||
@@ -25,13 +25,13 @@ func NewModel(db *gorm.DB, _ *redis.Client) Model {
|
||||
return &defaultPromoModel{db: db}
|
||||
}
|
||||
|
||||
func (m *defaultPromoModel) QueryEligibleRules(ctx context.Context, subscribeId int64) ([]*RuleWithPrice, error) {
|
||||
func (m *defaultPromoModel) QueryEligibleRules(ctx context.Context, subscribeId int64, quantity int64) ([]*RuleWithPrice, error) {
|
||||
var list []*RuleWithPrice
|
||||
err := m.db.WithContext(ctx).
|
||||
Table("promo_rule AS pr").
|
||||
Select("pr.*, sp.promo_price").
|
||||
Joins("JOIN subscribe_promo AS sp ON sp.promo_rule_id = pr.id").
|
||||
Where("sp.subscribe_id = ? AND sp.promo_price > 0 AND pr.enabled = ?", subscribeId, true).
|
||||
Where("sp.subscribe_id = ? AND sp.quantity = ? AND sp.promo_price > 0 AND pr.enabled = ?", subscribeId, quantity, true).
|
||||
Where("pr.deleted_at IS NULL").
|
||||
Order("pr.priority DESC").
|
||||
Order("pr.id ASC").
|
||||
|
||||
@@ -33,6 +33,7 @@ func (Rule) TableName() string {
|
||||
type SubscribePromo struct {
|
||||
Id int64 `gorm:"primaryKey"`
|
||||
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"`
|
||||
PromoPrice int64 `gorm:"type:bigint;not null;default:0;comment:Promo Price"`
|
||||
CreatedAt time.Time `gorm:"<-:create;comment:Create Time"`
|
||||
|
||||
@@ -2800,7 +2800,6 @@ type Subscribe struct {
|
||||
UnitPrice int64 `json:"unit_price"`
|
||||
UnitTime string `json:"unit_time"`
|
||||
Discount []SubscribeDiscount `json:"discount"`
|
||||
Promo *SubscribePromo `json:"promo"`
|
||||
NodeCount int64 `json:"node_count"`
|
||||
Replacement int64 `json:"replacement"`
|
||||
Inventory int64 `json:"inventory"`
|
||||
@@ -2861,10 +2860,11 @@ type SubscribeConfig struct {
|
||||
}
|
||||
|
||||
type SubscribeDiscount struct {
|
||||
Quantity int64 `json:"quantity"`
|
||||
Discount float64 `json:"discount"`
|
||||
NewUserOnly bool `json:"new_user_only"`
|
||||
MapApple string `json:"map_apple"`
|
||||
Quantity int64 `json:"quantity"`
|
||||
Discount float64 `json:"discount"`
|
||||
NewUserOnly bool `json:"new_user_only"`
|
||||
MapApple string `json:"map_apple"`
|
||||
Promo *SubscribePromo `json:"promo"`
|
||||
}
|
||||
|
||||
type SubscribeGroup struct {
|
||||
|
||||
@@ -3680,12 +3680,16 @@
|
||||
"discount": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"promo": {
|
||||
"$ref": "#/definitions/SubscribePromo"
|
||||
}
|
||||
},
|
||||
"title": "SubscribeDiscount",
|
||||
"required": [
|
||||
"quantity",
|
||||
"discount"
|
||||
"discount",
|
||||
"promo"
|
||||
]
|
||||
},
|
||||
"SubscribeGroup": {
|
||||
|
||||
@@ -149,7 +149,14 @@ func (l *ActivateOrderLogic) ProcessTask(ctx context.Context, task *asynq.Task)
|
||||
return err
|
||||
}
|
||||
|
||||
l.recordPromoUsage(ctx, orderInfo)
|
||||
if err = l.recordPromoUsage(ctx, orderInfo); err != nil {
|
||||
logger.WithContext(ctx).Error("[ActivateOrderLogic] 促销使用记录写入失败,将重试",
|
||||
logger.Field("order_no", orderInfo.OrderNo),
|
||||
logger.Field("promo_rule_id", orderInfo.PromoRuleId),
|
||||
logger.Field("error", err.Error()),
|
||||
)
|
||||
return err
|
||||
}
|
||||
l.finalizeCouponAndOrder(ctx, orderInfo)
|
||||
|
||||
commonLogic.SubscriptionTraceInfo(logger.WithContext(ctx), commonLogic.SubscriptionTraceFlowOrder, "activation_finished",
|
||||
@@ -159,9 +166,9 @@ func (l *ActivateOrderLogic) ProcessTask(ctx context.Context, task *asynq.Task)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *ActivateOrderLogic) recordPromoUsage(ctx context.Context, orderInfo *order.Order) {
|
||||
func (l *ActivateOrderLogic) recordPromoUsage(ctx context.Context, orderInfo *order.Order) error {
|
||||
if orderInfo == nil || orderInfo.PromoRuleId <= 0 || orderInfo.Quantity <= 0 || orderInfo.SubscribeId <= 0 || orderInfo.OrderNo == "" {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
promoPrice := int64(0)
|
||||
@@ -169,10 +176,10 @@ func (l *ActivateOrderLogic) recordPromoUsage(ctx context.Context, orderInfo *or
|
||||
promoPrice = (orderInfo.Price - orderInfo.PromoDiscount) / orderInfo.Quantity
|
||||
}
|
||||
if promoPrice <= 0 {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
err := l.svc.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
return l.svc.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var count int64
|
||||
if e := tx.Model(&promo.Usage{}).Where("order_no = ?", orderInfo.OrderNo).Count(&count).Error; e != nil {
|
||||
return e
|
||||
@@ -188,13 +195,6 @@ func (l *ActivateOrderLogic) recordPromoUsage(ctx context.Context, orderInfo *or
|
||||
PromoPrice: promoPrice,
|
||||
}, tx)
|
||||
})
|
||||
if err != nil {
|
||||
logger.WithContext(ctx).Error("Insert promo usage failed",
|
||||
logger.Field("error", err.Error()),
|
||||
logger.Field("order_no", orderInfo.OrderNo),
|
||||
logger.Field("promo_rule_id", orderInfo.PromoRuleId),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// parsePayload unMarshals the task payload into a structured format
|
||||
|
||||
Reference in New Issue
Block a user