package productv1 import ( "fmt" "math" "strings" "time" "github.com/expr-lang/expr" "gorm.io/gorm" ) const ( defaultExchangeRate float64 = 7.3 defaultFreight float64 = 100 ) type Website int const ( WebSite_US_Coach_Outlet Website = iota + 1 WebSite_CN_Coach_Outlet WebSite_CN_Coach ) type PriceStatus int const ( PriceStatus_NoChange PriceStatus = iota PriceStatus_UP PriceStatus_DOWN //降价 ) type Product struct { ID uint `gorm:"primary_key" json:"id"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` DeletedAt gorm.DeletedAt `gorm:"index"` Name string `gorm:"index" json:"name"` Pid string `gorm:"unique;not null" json:"pid"` Color string `json:"color"` Link string `json:"link"` Image string `json:"image"` Orderable bool `json:"orderable"` OriginalPrice float64 `json:"originalPrice"` Website Website `json:"website"` DiscPercent int `json:"discPercent" gorm:"index"` //折扣力度 CNYPrice float64 `json:"cnyPrice"` // 最终的价格 CalMark string `json:"calMark"` //计算价格的过程 DWPrice float64 `json:"dwPrice"` DWSkuId int64 `json:"dwSkuId"` // 得物sku_id Freight float64 `json:"freight"` //运费 ExchangeRate float64 `json:"exchangeRate" gorm:"-"` //汇率 Rate float64 `json:"rate" gorm:"index"` //利润率 PriceStatus PriceStatus `json:"priceStatus"` //价格状态 Remark string `json:"remark"` //备注 HistoryPrices []HistoryPrice `gorm:"foreignKey:Pid;references:Pid" json:"historyPrices"` DWHistoryPrices []DWHistoryPrice `gorm:"foreignKey:Pid;references:Pid" json:"dwHistoryPrices"` CalculateProcess []CalculateProcess `gorm:"foreignKey:Pid;references:Pid" json:"calculateProcess"` } func (p *Product) TableName() string { return "products" } func (p *Product) Env() map[string]any { return map[string]any{ "originalPrice": p.OriginalPrice, "freight": p.Freight, "exchangeRate": p.ExchangeRate, } } func (p *Product) BeforeSave(tx *gorm.DB) (err error) { p.fillPriceStatus(tx) p.CalRate() return } func (p *Product) fillPriceStatus(tx *gorm.DB) { var lastPrice float64 tx.Model(&HistoryPrice{}).Where("pid = ?", p.Pid).Order("created_at desc").Limit(1).Pluck("original_price", &lastPrice) if p.OriginalPrice != lastPrice { p.HistoryPrices = append(p.HistoryPrices, HistoryPrice{ OriginalPrice: p.OriginalPrice, }) if p.OriginalPrice > lastPrice { p.PriceStatus = PriceStatus_UP } else { p.PriceStatus = PriceStatus_DOWN } } } func (p *Product) CalCNY(calculateProcess []CalculateProcess) { env := p.Env() var calculateStrings = make([]string, 0, len(calculateProcess)) p.CNYPrice = 0 for _, c := range calculateProcess { process, price := c.Calculate(env) if process != "" { calculateStrings = append(calculateStrings, process) if p.CNYPrice == 0 { p.CNYPrice = price } p.CNYPrice = min(p.CNYPrice, price) } } p.CalMark = strings.Join(calculateStrings, "\n") } // CalRate 计算收益率 func (p *Product) CalRate() { if p.DWPrice > 0 && p.CNYPrice > 0 { // 如果有得物价格,就计算收益率 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) } // Decimal 保留两位小数 func Decimal(value float64) float64 { return math.Trunc(value*1e2+0.5) * 1e-2 } type HistoryPrice struct { ID uint `gorm:"primary_key" json:"id"` CreatedAt time.Time `json:"createdAt"` Pid string `gorm:"index" json:"pid"` OriginalPrice float64 `json:"originalPrice"` } type DWHistoryPrice struct { ID uint `gorm:"primary_key" json:"id"` CreatedAt time.Time `json:"createdAt"` Pid string `gorm:"index" json:"pid"` 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) }