common/model/product/model.go

245 lines
7.6 KiB
Go
Raw Normal View History

2024-05-14 20:36:33 +08:00
package productv1
import (
"fmt"
2024-06-15 16:54:04 +08:00
"github.com/expr-lang/expr"
2024-05-14 20:36:33 +08:00
"gorm.io/gorm"
"math"
"strings"
"time"
)
const (
defaultExchangeRate float64 = 7.3
defaultFreight float64 = 100
)
2024-06-14 15:26:27 +08:00
type Website int
const (
WebSite_US_Coach_Outlet Website = iota + 1
2024-06-15 19:44:54 +08:00
WebSite_CN_Coach_Outlet
2024-06-14 15:26:27 +08:00
WebSite_CN_Coach
)
2024-05-15 13:54:53 +08:00
type PriceStatus int
const (
PriceStatus_NoChange PriceStatus = iota
PriceStatus_UP
PriceStatus_DOWN //降价
)
2024-05-14 20:36:33 +08:00
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"`
2024-06-15 21:51:41 +08:00
Pid string `gorm:"unique;not null" json:"pid"`
2024-05-14 20:36:33 +08:00
Color string `json:"color"`
Link string `json:"link"`
Image string `json:"image"`
Orderable bool `json:"orderable"`
2024-06-15 16:54:04 +08:00
OriginalPrice float64 `json:"originalPrice"`
2024-06-15 21:51:41 +08:00
Website Website `json:"website"`
2024-06-15 16:54:04 +08:00
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"` //备注
2024-05-16 12:52:06 +08:00
2024-06-15 19:44:54 +08:00
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"`
2024-05-14 20:36:33 +08:00
}
func (p *Product) TableName() string {
return "products"
}
2024-06-15 16:54:04 +08:00
func (p *Product) Env() map[string]any {
return map[string]any{
"originalPrice": p.OriginalPrice,
"freight": p.Freight,
"exchangeRate": p.ExchangeRate,
}
}
2024-05-14 20:36:33 +08:00
func (p *Product) BeforeSave(tx *gorm.DB) (err error) {
2024-06-14 16:27:31 +08:00
p.fillPriceStatus(tx)
2024-06-15 16:54:04 +08:00
p.CalRate()
2024-06-14 16:27:31 +08:00
return
}
func (p *Product) fillPriceStatus(tx *gorm.DB) {
2024-05-14 20:36:33 +08:00
var lastPrice float64
2024-06-15 16:54:04 +08:00
tx.Model(&HistoryPrice{}).Where("pid = ?", p.Pid).Order("created_at desc").Limit(1).Pluck("original_price", &lastPrice)
if p.OriginalPrice != lastPrice {
2024-05-14 20:36:33 +08:00
p.HistoryPrices = append(p.HistoryPrices, HistoryPrice{
2024-06-15 16:54:04 +08:00
OriginalPrice: p.OriginalPrice,
2024-05-14 20:36:33 +08:00
})
2024-06-15 16:54:04 +08:00
if p.OriginalPrice > lastPrice {
2024-05-15 13:54:53 +08:00
p.PriceStatus = PriceStatus_UP
} else {
p.PriceStatus = PriceStatus_DOWN
2024-06-14 16:27:31 +08:00
}
}
}
2024-05-15 13:54:53 +08:00
2024-06-15 16:54:04 +08:00
func (p *Product) CalCNY(calculateProcess []CalculateProcess) {
env := p.Env()
var calculateStrings = make([]string, 0, len(calculateProcess))
p.CNYPrice = 0
2024-06-15 16:54:04 +08:00
for _, c := range calculateProcess {
process, price := c.Calculate(env)
if process != "" {
calculateStrings = append(calculateStrings, process)
if p.CNYPrice == 0 {
p.CNYPrice = price
}
2024-06-15 16:54:04 +08:00
p.CNYPrice = min(p.CNYPrice, price)
2024-05-15 13:54:53 +08:00
}
2024-05-14 20:36:33 +08:00
}
2024-06-15 16:54:04 +08:00
p.CalMark = strings.Join(calculateStrings, "\n")
2024-05-14 20:36:33 +08:00
}
2024-06-15 16:54:04 +08:00
//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 计算收益率
2024-05-14 20:36:33 +08:00
func (p *Product) CalRate() {
if p.DWPrice > 0 && p.CNYPrice > 0 {
2024-06-14 15:26:27 +08:00
// 如果有得物价格,就计算收益率
p.Rate = Decimal((p.DWPrice - p.CNYPrice) / p.CNYPrice * 100)
2024-06-15 16:54:04 +08:00
} else if p.Rate > 0 {
// 如果得物价格为0, 收益率应该设置为0
p.Rate = 0
2024-06-14 15:26:27 +08:00
}
p.CNYPrice = Decimal(p.CNYPrice)
}
2024-05-14 20:36:33 +08:00
// 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"`
2024-06-15 16:54:04 +08:00
Pid string `gorm:"index" json:"pid"`
OriginalPrice float64 `json:"originalPrice"`
2024-05-14 20:36:33 +08:00
}
2024-05-16 12:52:06 +08:00
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"`
}
2024-06-15 16:54:04 +08:00
// 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)
}