feat 优惠活动可编辑

This commit is contained in:
timerzz 2024-06-15 16:54:04 +08:00
parent e7fa9e7dd2
commit 3490bc7e57
4 changed files with 169 additions and 106 deletions

1
go.mod
View File

@ -6,6 +6,7 @@ require (
github.com/bytedance/sonic v1.11.6 github.com/bytedance/sonic v1.11.6
github.com/cloudwego/hertz v0.9.1 github.com/cloudwego/hertz v0.9.1
github.com/corpix/uarand v0.2.0 github.com/corpix/uarand v0.2.0
github.com/expr-lang/expr v1.16.9
github.com/go-resty/resty/v2 v2.13.1 github.com/go-resty/resty/v2 v2.13.1
github.com/golang/glog v1.2.1 github.com/golang/glog v1.2.1
github.com/metacubex/mihomo v1.18.4 github.com/metacubex/mihomo v1.18.4

2
go.sum
View File

@ -81,6 +81,8 @@ github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBE
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4= github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4=
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA= github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA=
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok= github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok=
github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI=
github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=

View File

@ -2,6 +2,7 @@ package productv1
import ( import (
"fmt" "fmt"
"github.com/expr-lang/expr"
"gorm.io/gorm" "gorm.io/gorm"
"math" "math"
"strings" "strings"
@ -35,21 +36,20 @@ type Product struct {
DeletedAt gorm.DeletedAt `gorm:"index"` DeletedAt gorm.DeletedAt `gorm:"index"`
Name string `gorm:"index" json:"name"` Name string `gorm:"index" json:"name"`
Pid string `gorm:"unique;not null" json:"pid"` Pid string `gorm:"index:,unique,composite:product_pid_website_idx;not null" json:"pid"`
Color string `json:"color"` Color string `json:"color"`
Link string `json:"link"` Link string `json:"link"`
Image string `json:"image"` Image string `json:"image"`
Orderable bool `json:"orderable"` Orderable bool `json:"orderable"`
USPrice float64 `json:"usPrice"` OriginalPrice float64 `json:"originalPrice"`
Website Website `json:"website" gorm:"index"` Website Website `json:"website" gorm:"index:,unique,composite:product_pid_website_idx"`
DiscPercent int `json:"discPercent" gorm:"index"` //折扣力度 DiscPercent int `json:"discPercent" gorm:"index"` //折扣力度
CNYPrice float64 `json:"cnyPrice"` CNYPrice float64 `json:"cnyPrice"` // 最终的价格
CalMark string `json:"calMark"` //计算价格的过程 CalMark string `json:"calMark"` //计算价格的过程
DWPrice float64 `json:"dwPrice"` DWPrice float64 `json:"dwPrice"`
Freight float64 `json:"freight"` //运费 Freight float64 `json:"freight"` //运费
ExchangeRate float64 `json:"exchangeRate" gorm:"-"` //汇率 ExchangeRate float64 `json:"exchangeRate" gorm:"-"` //汇率
Discount int `json:"discount" gorm:"-"` //折扣
Rate float64 `json:"rate" gorm:"index"` //利润率 Rate float64 `json:"rate" gorm:"index"` //利润率
PriceStatus PriceStatus `json:"priceStatus"` //价格状态 PriceStatus PriceStatus `json:"priceStatus"` //价格状态
Remark string `json:"remark"` //备注 Remark string `json:"remark"` //备注
@ -62,29 +62,29 @@ func (p *Product) TableName() string {
return "products" return "products"
} }
func (p *Product) Env() map[string]any {
return map[string]any{
"originalPrice": p.OriginalPrice,
"dwPrice": p.DWPrice,
"freight": p.Freight,
"exchangeRate": p.ExchangeRate,
}
}
func (p *Product) BeforeSave(tx *gorm.DB) (err error) { func (p *Product) BeforeSave(tx *gorm.DB) (err error) {
p.CalRate()
p.fillPriceStatus(tx) p.fillPriceStatus(tx)
p.CalRate()
return return
} }
func (p *Product) fillPriceStatus(tx *gorm.DB) { func (p *Product) fillPriceStatus(tx *gorm.DB) {
switch p.Website {
case WebSite_US_Coach_Outlet:
p.fillUSCoachPriceStatus(tx)
case WebSite_CN_Coach:
p.fillCNCoachPriceStatus(tx)
}
}
func (p *Product) fillUSCoachPriceStatus(tx *gorm.DB) {
var lastPrice float64 var lastPrice float64
tx.Model(&HistoryPrice{}).Where("pid = ?", p.Pid).Order("created_at desc").Limit(1).Pluck("us_price", &lastPrice) tx.Model(&HistoryPrice{}).Where("pid = ?", p.Pid).Order("created_at desc").Limit(1).Pluck("original_price", &lastPrice)
if p.USPrice != lastPrice { if p.OriginalPrice != lastPrice {
p.HistoryPrices = append(p.HistoryPrices, HistoryPrice{ p.HistoryPrices = append(p.HistoryPrices, HistoryPrice{
USPrice: p.USPrice, OriginalPrice: p.OriginalPrice,
}) })
if p.USPrice > lastPrice { if p.OriginalPrice > lastPrice {
p.PriceStatus = PriceStatus_UP p.PriceStatus = PriceStatus_UP
} else { } else {
p.PriceStatus = PriceStatus_DOWN p.PriceStatus = PriceStatus_DOWN
@ -92,81 +92,83 @@ func (p *Product) fillUSCoachPriceStatus(tx *gorm.DB) {
} }
} }
func (p *Product) fillCNCoachPriceStatus(tx *gorm.DB) { func (p *Product) CalCNY(calculateProcess []CalculateProcess) {
var lastPrice float64 env := p.Env()
tx.Model(&HistoryPrice{}).Where("pid = ?", p.Pid).Order("created_at desc").Limit(1).Pluck("us_price", &lastPrice) var calculateStrings = make([]string, 0, len(calculateProcess))
if p.CNYPrice != lastPrice { for _, c := range calculateProcess {
p.HistoryPrices = append(p.HistoryPrices, HistoryPrice{ process, price := c.Calculate(env)
USPrice: p.CNYPrice, if process != "" {
}) calculateStrings = append(calculateStrings, process)
if p.CNYPrice > lastPrice { p.CNYPrice = min(p.CNYPrice, price)
p.PriceStatus = PriceStatus_UP
} else {
p.PriceStatus = PriceStatus_DOWN
} }
} }
p.CalMark = strings.Join(calculateStrings, "\n")
} }
//func (p *Product) CalRate() {
// switch p.Website {
// case WebSite_US_Coach_Outlet:
// p.CalRateUSCoach()
// case WebSite_CN_Coach:
// p.CalRateCNCoach()
// }
//}
//func (p *Product) CalRateUSCoach() {
// calculationProcess := make([]string, 0, 2) //存放计算过程
// if p.ExchangeRate == 0 {
// p.ExchangeRate = defaultExchangeRate
// }
// if p.Freight == 0 {
// p.Freight = defaultFreight
// }
// if p.Discount == 0 {
// p.Discount = 100
// }
//
// //先计算打九折的价格
// tmpPrice := p.USPrice * 0.9 // 这是打九折的价格
// p.CNYPrice = tmpPrice*p.ExchangeRate + p.Freight
// calculationProcess = append(calculationProcess, fmt.Sprintf("【九折】%.2f * 0.9 * %.2f + %.2f = %.2f", p.USPrice, p.ExchangeRate, p.Freight, p.CNYPrice))
// var discountPrice = p.USPrice
// if 0 < p.Discount && p.Discount < 100 {
// discountPrice = p.USPrice * float64(p.Discount) / 100
// }
// if discountPrice >= 150 {
// calculationProcess = append(calculationProcess, fmt.Sprintf("【150 -20】(%.2f * %d%% - 20) * %.2f + %.2f = %.2f", p.USPrice, p.Discount, p.ExchangeRate, p.Freight, (discountPrice-20)*p.ExchangeRate+p.Freight))
// if discountPrice-20 < tmpPrice {
// // 符合满150-20,而且比九折便宜
// tmpPrice = discountPrice - 20
// p.CNYPrice = tmpPrice*p.ExchangeRate + p.Freight
// }
// } else if discountPrice >= 100 {
// calculationProcess = append(calculationProcess, fmt.Sprintf("【100 -10】(%.2f * %d%% - 10) * %.2f + %.2f = %.2f", p.USPrice, p.Discount, p.ExchangeRate, p.Freight, (discountPrice-10)*p.ExchangeRate+p.Freight))
// if discountPrice-10 < tmpPrice {
// // 符合满100 -10,而且比九折便宜
// tmpPrice = discountPrice - 10
// p.CNYPrice = tmpPrice*p.ExchangeRate + p.Freight
// }
// } else if p.Discount < 90 {
// calculationProcess = append(calculationProcess, fmt.Sprintf("【打折扣】%.2f * %d%% * %.2f + %.2f = %.2f", p.USPrice, p.Discount, p.ExchangeRate, p.Freight, p.USPrice*float64(p.Discount)/100*p.ExchangeRate+p.Freight))
// p.CNYPrice = p.USPrice*float64(p.Discount)/100*p.ExchangeRate + p.Freight
// }
// p.CalMark = strings.Join(calculationProcess, "\n")
// if p.DWPrice > 0 {
// // 如果有得物价格,就计算收益率
// p.Rate = Decimal((p.DWPrice - p.CNYPrice) / p.CNYPrice * 100)
// }
//
// p.CNYPrice = Decimal(p.CNYPrice)
//}
// CalRate 计算收益率
func (p *Product) CalRate() { func (p *Product) CalRate() {
switch p.Website {
case WebSite_US_Coach_Outlet:
p.CalRateUSCoach()
case WebSite_CN_Coach:
p.CalRateCNCoach()
}
}
func (p *Product) CalRateUSCoach() {
calculationProcess := make([]string, 0, 2) //存放计算过程
if p.ExchangeRate == 0 {
p.ExchangeRate = defaultExchangeRate
}
if p.Freight == 0 {
p.Freight = defaultFreight
}
if p.Discount == 0 {
p.Discount = 100
}
//先计算打九折的价格
tmpPrice := p.USPrice * 0.9 // 这是打九折的价格
p.CNYPrice = tmpPrice*p.ExchangeRate + p.Freight
calculationProcess = append(calculationProcess, fmt.Sprintf("【九折】%.2f * 0.9 * %.2f + %.2f = %.2f", p.USPrice, p.ExchangeRate, p.Freight, p.CNYPrice))
var discountPrice = p.USPrice
if 0 < p.Discount && p.Discount < 100 {
discountPrice = p.USPrice * float64(p.Discount) / 100
}
if discountPrice >= 150 {
calculationProcess = append(calculationProcess, fmt.Sprintf("【150 -20】(%.2f * %d%% - 20) * %.2f + %.2f = %.2f", p.USPrice, p.Discount, p.ExchangeRate, p.Freight, (discountPrice-20)*p.ExchangeRate+p.Freight))
if discountPrice-20 < tmpPrice {
// 符合满150-20,而且比九折便宜
tmpPrice = discountPrice - 20
p.CNYPrice = tmpPrice*p.ExchangeRate + p.Freight
}
} else if discountPrice >= 100 {
calculationProcess = append(calculationProcess, fmt.Sprintf("【100 -10】(%.2f * %d%% - 10) * %.2f + %.2f = %.2f", p.USPrice, p.Discount, p.ExchangeRate, p.Freight, (discountPrice-10)*p.ExchangeRate+p.Freight))
if discountPrice-10 < tmpPrice {
// 符合满100 -10,而且比九折便宜
tmpPrice = discountPrice - 10
p.CNYPrice = tmpPrice*p.ExchangeRate + p.Freight
}
} else if p.Discount < 90 {
calculationProcess = append(calculationProcess, fmt.Sprintf("【打折扣】%.2f * %d%% * %.2f + %.2f = %.2f", p.USPrice, p.Discount, p.ExchangeRate, p.Freight, p.USPrice*float64(p.Discount)/100*p.ExchangeRate+p.Freight))
p.CNYPrice = p.USPrice*float64(p.Discount)/100*p.ExchangeRate + p.Freight
}
p.CalMark = strings.Join(calculationProcess, "\n")
if p.DWPrice > 0 {
// 如果有得物价格,就计算收益率
p.Rate = Decimal((p.DWPrice - p.CNYPrice) / p.CNYPrice * 100)
}
p.CNYPrice = Decimal(p.CNYPrice)
}
func (p *Product) CalRateCNCoach() {
if p.DWPrice > 0 { if p.DWPrice > 0 {
// 如果有得物价格,就计算收益率 // 如果有得物价格,就计算收益率
p.Rate = Decimal((p.DWPrice - p.CNYPrice) / p.CNYPrice * 100) p.Rate = Decimal((p.DWPrice - p.CNYPrice) / p.CNYPrice * 100)
} else if p.Rate > 0 {
// 如果得物价格为0, 收益率应该设置为0
p.Rate = 0
} }
p.CNYPrice = Decimal(p.CNYPrice) p.CNYPrice = Decimal(p.CNYPrice)
@ -182,7 +184,7 @@ type HistoryPrice struct {
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
Pid string `gorm:"index" json:"pid"` Pid string `gorm:"index" json:"pid"`
USPrice float64 `json:"usPrice"` OriginalPrice float64 `json:"originalPrice"`
} }
type DWHistoryPrice struct { type DWHistoryPrice struct {
@ -192,3 +194,46 @@ type DWHistoryPrice struct {
Pid string `gorm:"index" json:"pid"` Pid string `gorm:"index" json:"pid"`
DWPrice float64 `json:"dwPrice"` DWPrice float64 `json:"dwPrice"`
} }
// CalculateProcess 计算过程
type CalculateProcess struct {
ID uint `gorm:"primary_key" json:"id"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
DeletedAt gorm.DeletedAt `gorm:"index"`
Pid string
Website Website
Condition string
Process string
Name string
}
func (c *CalculateProcess) Calculate(env map[string]any) (string, float64) {
if c.Condition != "" {
condition, err := expr.Compile(c.Condition, expr.AsBool())
if err != nil {
return fmt.Sprintf("【%s】 condition compile error: %v", c.Name, err), 0
}
if ok, err := expr.Run(condition, env); err != nil {
return fmt.Sprintf("【%s】 condition run error: %v", c.Name, err), 0
} else if !ok.(bool) {
return "", 0
}
}
program, err := expr.Compile(c.Process, expr.AsFloat64())
if err != nil {
return fmt.Sprintf("【%s】 process compile error: %v", c.Name, err), 0
}
output, err := expr.Run(program, env)
if err != nil {
return fmt.Sprintf("【%s】 process run error: %v", c.Name, err), 0
}
var replaceList = make([]string, 0, 2*len(env))
for k, v := range env {
replaceList = append(replaceList, k, fmt.Sprintf("%v", v))
}
replacer := strings.NewReplacer(replaceList...)
return replacer.Replace(fmt.Sprintf("【%s】 %s = %.2f", c.Name, c.Process, output.(float64))), output.(float64)
}

View File

@ -32,7 +32,7 @@ func TestUpsert(t *testing.T) {
Link: "www.baidu.com", Link: "www.baidu.com",
Image: "image", Image: "image",
Orderable: false, Orderable: false,
USPrice: 123, OriginalPrice: 123,
DWPrice: 3000, DWPrice: 3000,
Freight: 130, Freight: 130,
}, },
@ -50,7 +50,7 @@ func TestUpdate(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
var p = Product{Pid: "CJ575-QBO4G", DWPrice: 3000, Freight: 150, Remark: "test", USPrice: 209.3, ExchangeRate: 7.3} var p = Product{Pid: "CJ575-QBO4G", DWPrice: 3000, Freight: 150, Remark: "test", OriginalPrice: 209.3, ExchangeRate: 7.3}
err = db.Model(&p).Where("pid = ?", p.Pid).Select("dw_price", "freight", "remark", "rate", "cal_mark", "cny_price").Updates(&p).Error err = db.Model(&p).Where("pid = ?", p.Pid).Select("dw_price", "freight", "remark", "rate", "cal_mark", "cny_price").Updates(&p).Error
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -72,3 +72,18 @@ func TestGetConfig(t *testing.T) {
db.Table("options").Select("exchange_rate").Where("id = ?", 1).Scan(&exchangeRate) db.Table("options").Select("exchange_rate").Where("id = ?", 1).Scan(&exchangeRate)
t.Log(exchangeRate) t.Log(exchangeRate)
} }
func TestCalculateProcess_Calculate(t *testing.T) {
p := Product{
OriginalPrice: 160.9,
Freight: 100,
ExchangeRate: 7.3,
PriceStatus: 0,
}
cal := CalculateProcess{
Condition: "usPrice >= 150",
Process: "(usPrice - 20) * exchangeRate + freight",
Name: "满150 - 20",
}
t.Log(cal.Calculate(p.Env()))
}