2024-05-14 20:36:33 +08:00
|
|
|
package productv1
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
"strings"
|
|
|
|
"time"
|
2024-07-24 15:06:31 +08:00
|
|
|
|
|
|
|
"github.com/expr-lang/expr"
|
|
|
|
"gorm.io/gorm"
|
2024-05-14 20:36:33 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
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"`
|
2024-07-24 15:06:31 +08:00
|
|
|
DWSkuId int64 `json:"dwSkuId"` // 得物sku_id
|
2024-07-29 15:10:04 +08:00
|
|
|
DWSpuId int64 `json:"dwSpuId"` //得物spu_id
|
2024-06-15 16:54:04 +08:00
|
|
|
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))
|
2024-06-15 23:48:53 +08:00
|
|
|
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)
|
2024-06-15 22:47:43 +08:00
|
|
|
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
|
|
|
// CalRate 计算收益率
|
2024-05-14 20:36:33 +08:00
|
|
|
func (p *Product) CalRate() {
|
2024-06-15 22:47:43 +08:00
|
|
|
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)
|
|
|
|
}
|