新功能(#79): 实现促销管理接口并支持数量配价
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
package promo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/perfect-panel/server/pkg/cache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
cachePromoRuleIdPrefix = "cache:promo_rule:id:"
|
||||
cacheSubscribePromoIdPrefix = "cache:subscribe_promo:id:"
|
||||
)
|
||||
|
||||
type Model interface {
|
||||
InsertRule(ctx context.Context, data *Rule) error
|
||||
FindRule(ctx context.Context, id int64) (*Rule, error)
|
||||
UpdateRule(ctx context.Context, data *Rule) error
|
||||
DeleteRule(ctx context.Context, id int64) error
|
||||
QueryRuleList(ctx context.Context, page, size int, ruleType string, enabled *bool, search string) (int64, []*Rule, error)
|
||||
UpsertPrices(ctx context.Context, ruleId int64, items []*SubscribePromo) error
|
||||
FindPrice(ctx context.Context, id int64) (*SubscribePromo, error)
|
||||
DeletePrice(ctx context.Context, id int64) error
|
||||
QueryPriceList(ctx context.Context, ruleId int64, page, size int) (int64, []*SubscribePromo, error)
|
||||
QueryUsageList(ctx context.Context, params UsageFilter) (int64, []*Usage, error)
|
||||
Transaction(ctx context.Context, fn func(db *gorm.DB) error) error
|
||||
}
|
||||
|
||||
type UsageFilter struct {
|
||||
Page int
|
||||
Size int
|
||||
RuleId int64
|
||||
UserId int64
|
||||
SubscribeId int64
|
||||
OrderNo string
|
||||
}
|
||||
|
||||
type customPromoModel struct {
|
||||
cache.CachedConn
|
||||
table string
|
||||
}
|
||||
|
||||
func NewModel(db *gorm.DB, c *redis.Client) Model {
|
||||
return &customPromoModel{
|
||||
CachedConn: cache.NewConn(db, c),
|
||||
table: "`promo_rule`",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *customPromoModel) ruleCacheKey(id int64) string {
|
||||
return fmt.Sprintf("%s%d", cachePromoRuleIdPrefix, id)
|
||||
}
|
||||
|
||||
func (m *customPromoModel) priceCacheKey(id int64) string {
|
||||
return fmt.Sprintf("%s%d", cacheSubscribePromoIdPrefix, id)
|
||||
}
|
||||
|
||||
func (m *customPromoModel) InsertRule(ctx context.Context, data *Rule) error {
|
||||
return m.ExecCtx(ctx, func(conn *gorm.DB) error {
|
||||
return conn.Create(data).Error
|
||||
}, m.ruleCacheKey(data.Id))
|
||||
}
|
||||
|
||||
func (m *customPromoModel) FindRule(ctx context.Context, id int64) (*Rule, error) {
|
||||
var resp Rule
|
||||
err := m.QueryCtx(ctx, &resp, m.ruleCacheKey(id), func(conn *gorm.DB, v interface{}) error {
|
||||
return conn.Model(&Rule{}).Where("id = ?", id).First(v).Error
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (m *customPromoModel) UpdateRule(ctx context.Context, data *Rule) error {
|
||||
old, err := m.FindRule(ctx, data.Id)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
keys := []string{m.ruleCacheKey(data.Id)}
|
||||
if old != nil {
|
||||
keys = append(keys, m.ruleCacheKey(old.Id))
|
||||
}
|
||||
return m.ExecCtx(ctx, func(conn *gorm.DB) error {
|
||||
return conn.Save(data).Error
|
||||
}, keys...)
|
||||
}
|
||||
|
||||
func (m *customPromoModel) DeleteRule(ctx context.Context, id int64) error {
|
||||
data, err := m.FindRule(ctx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return m.ExecCtx(ctx, func(conn *gorm.DB) error {
|
||||
return conn.Delete(&Rule{}, id).Error
|
||||
}, m.ruleCacheKey(data.Id))
|
||||
}
|
||||
|
||||
func (m *customPromoModel) QueryRuleList(ctx context.Context, page, size int, ruleType string, enabled *bool, search string) (int64, []*Rule, error) {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if size <= 0 {
|
||||
size = 10
|
||||
}
|
||||
var total int64
|
||||
var list []*Rule
|
||||
err := m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
|
||||
db := conn.Model(&Rule{})
|
||||
if ruleType != "" {
|
||||
db = db.Where("type = ?", ruleType)
|
||||
}
|
||||
if enabled != nil {
|
||||
db = db.Where("enabled = ?", *enabled)
|
||||
}
|
||||
if search != "" {
|
||||
db = db.Where("name LIKE ?", "%"+search+"%")
|
||||
}
|
||||
if err := db.Count(&total).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Order("priority DESC").Order("id DESC").Limit(size).Offset((page - 1) * size).Find(v).Error
|
||||
})
|
||||
return total, list, err
|
||||
}
|
||||
|
||||
func (m *customPromoModel) UpsertPrices(ctx context.Context, ruleId int64, items []*SubscribePromo) error {
|
||||
return m.ExecCtx(ctx, func(conn *gorm.DB) error {
|
||||
for _, item := range items {
|
||||
if item == nil {
|
||||
continue
|
||||
}
|
||||
item.PromoRuleId = ruleId
|
||||
var existing SubscribePromo
|
||||
err := conn.Model(&SubscribePromo{}).
|
||||
Where("subscribe_id = ? AND quantity = ? AND promo_rule_id = ?", item.SubscribeId, item.Quantity, ruleId).
|
||||
First(&existing).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
if err := conn.Create(item).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
existing.Quantity = item.Quantity
|
||||
existing.PromoPrice = item.PromoPrice
|
||||
if err := conn.Save(&existing).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (m *customPromoModel) FindPrice(ctx context.Context, id int64) (*SubscribePromo, error) {
|
||||
var resp SubscribePromo
|
||||
err := m.QueryCtx(ctx, &resp, m.priceCacheKey(id), func(conn *gorm.DB, v interface{}) error {
|
||||
return conn.Model(&SubscribePromo{}).Where("id = ?", id).First(v).Error
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (m *customPromoModel) DeletePrice(ctx context.Context, id int64) error {
|
||||
data, err := m.FindPrice(ctx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return m.ExecCtx(ctx, func(conn *gorm.DB) error {
|
||||
return conn.Delete(&SubscribePromo{}, id).Error
|
||||
}, m.priceCacheKey(data.Id))
|
||||
}
|
||||
|
||||
func (m *customPromoModel) QueryPriceList(ctx context.Context, ruleId int64, page, size int) (int64, []*SubscribePromo, error) {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if size <= 0 {
|
||||
size = 10
|
||||
}
|
||||
var total int64
|
||||
var list []*SubscribePromo
|
||||
err := m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
|
||||
db := conn.Model(&SubscribePromo{}).Where("promo_rule_id = ?", ruleId)
|
||||
if err := db.Count(&total).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Order("id DESC").Limit(size).Offset((page - 1) * size).Find(v).Error
|
||||
})
|
||||
return total, list, err
|
||||
}
|
||||
|
||||
func (m *customPromoModel) QueryUsageList(ctx context.Context, params UsageFilter) (int64, []*Usage, error) {
|
||||
if params.Page <= 0 {
|
||||
params.Page = 1
|
||||
}
|
||||
if params.Size <= 0 {
|
||||
params.Size = 10
|
||||
}
|
||||
var total int64
|
||||
var list []*Usage
|
||||
err := m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
|
||||
db := conn.Model(&Usage{})
|
||||
if params.RuleId > 0 {
|
||||
db = db.Where("promo_rule_id = ?", params.RuleId)
|
||||
}
|
||||
if params.UserId > 0 {
|
||||
db = db.Where("user_id = ?", params.UserId)
|
||||
}
|
||||
if params.SubscribeId > 0 {
|
||||
db = db.Where("subscribe_id = ?", params.SubscribeId)
|
||||
}
|
||||
if params.OrderNo != "" {
|
||||
db = db.Where("order_no = ?", params.OrderNo)
|
||||
}
|
||||
if err := db.Count(&total).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Order("id DESC").Limit(params.Size).Offset((params.Page - 1) * params.Size).Find(v).Error
|
||||
})
|
||||
return total, list, err
|
||||
}
|
||||
|
||||
func (m *customPromoModel) Transaction(ctx context.Context, fn func(db *gorm.DB) error) error {
|
||||
return m.TransactCtx(ctx, fn)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package promo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Rule struct {
|
||||
Id int64 `gorm:"primaryKey"`
|
||||
Name string `gorm:"type:varchar(100);not null;default:'';comment:Promo Rule Name"`
|
||||
Type string `gorm:"type:varchar(32);not null;default:'';comment:Promo Rule Type"`
|
||||
Params string `gorm:"type:json;not null;comment:Rule Params"`
|
||||
Priority int64 `gorm:"type:int;not null;default:0;comment:Priority"`
|
||||
Enabled *bool `gorm:"type:tinyint(1);not null;default:1;comment:Enabled"`
|
||||
StartTime *time.Time `gorm:"default:null;comment:Start Time"`
|
||||
EndTime *time.Time `gorm:"default:null;comment:End Time"`
|
||||
CreatedAt time.Time `gorm:"<-:create;comment:Create Time"`
|
||||
UpdatedAt time.Time `gorm:"comment:Update Time"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index;comment:Delete Time"`
|
||||
}
|
||||
|
||||
func (*Rule) TableName() string {
|
||||
return "promo_rule"
|
||||
}
|
||||
|
||||
type SubscribePromo struct {
|
||||
Id int64 `gorm:"primaryKey"`
|
||||
SubscribeId int64 `gorm:"not null;index:idx_subscribe_qty_rule,unique;comment:Subscribe ID"`
|
||||
Quantity int64 `gorm:"not null;default:0;index:idx_subscribe_qty_rule,unique;comment:Quantity"`
|
||||
PromoRuleId int64 `gorm:"not null;index:idx_subscribe_qty_rule,unique;index:idx_promo_rule_id;comment:Promo Rule ID"`
|
||||
PromoPrice int64 `gorm:"type:bigint;not null;default:0;comment:Promo Price"`
|
||||
CreatedAt time.Time `gorm:"<-:create;comment:Create Time"`
|
||||
UpdatedAt time.Time `gorm:"comment:Update Time"`
|
||||
}
|
||||
|
||||
func (*SubscribePromo) TableName() string {
|
||||
return "subscribe_promo"
|
||||
}
|
||||
|
||||
type Usage struct {
|
||||
Id int64 `gorm:"primaryKey"`
|
||||
UserId int64 `gorm:"not null;index:idx_user_rule;comment:User ID"`
|
||||
PromoRuleId int64 `gorm:"not null;index:idx_user_rule;comment:Promo Rule ID"`
|
||||
SubscribeId int64 `gorm:"not null;comment:Subscribe ID"`
|
||||
OrderNo string `gorm:"type:varchar(255);not null;default:'';index:idx_order_no;comment:Order No"`
|
||||
PromoPrice int64 `gorm:"type:bigint;not null;default:0;comment:Promo Price"`
|
||||
CreatedAt time.Time `gorm:"<-:create;comment:Create Time"`
|
||||
}
|
||||
|
||||
func (*Usage) TableName() string {
|
||||
return "promo_usage"
|
||||
}
|
||||
Reference in New Issue
Block a user