package productv1 import ( "fmt" "github.com/expr-lang/expr" "gorm.io/gorm" "math" "strings" "time" ) 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"` 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") } //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() { 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) }