Commit e715f459 authored by zhengyaoqiu's avatar zhengyaoqiu

feat(colorway): 算价逻辑补充

parent 6313d9f2
......@@ -32,6 +32,10 @@ type Product struct {
CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"`
}
func (receiver Product) FindPlatformStoreSku(platform types.Platform, store types.Store, route types.Route, skuId string) (ProductColorSkuPlatformStoreSku, bool) {
return receiver.Colors.FindPlatformStoreSku(platform, store, route, skuId)
}
type Products []Product
type ProductComposition struct {
......@@ -76,21 +80,39 @@ type ProductColor struct {
type ProductColors []ProductColor
func (receiver ProductColors) FindPlatformStoreSku(platform types.Platform, store types.Store, route types.Route, skuId string) (ProductColorSkuPlatformStoreSku, bool) {
for _, color := range receiver {
if productColorSkuPlatformStoreSku, ok := color.Skus.FindPlatformStoreSku(platform, store, route, skuId); ok {
return productColorSkuPlatformStoreSku, true
}
}
return ProductColorSkuPlatformStoreSku{}, false
}
type ProductColorSku struct {
Pc3SkuId string `bson:"pc3SkuId"`
Pc3ProductId string `bson:"pc3ProductId"`
SourceId string `bson:"sourceId"`
SiteId string `bson:"siteId"`
Size string `bson:"size"`
Stock int `bson:"stock"`
Price float64 `bson:"price"`
Currency string `bson:"currency"`
SubjectIds []string `bson:"subjectIds"`
ProductColorSkuPlatformStoreSkus []ProductColorSkuPlatformStoreSkus `bson:"productColorSkuPlatformStoreSkus"`
Pc3SkuId string `bson:"pc3SkuId"`
Pc3ProductId string `bson:"pc3ProductId"`
SourceId string `bson:"sourceId"`
SiteId string `bson:"siteId"`
Size string `bson:"size"`
Stock int `bson:"stock"`
Price float64 `bson:"price"`
Currency string `bson:"currency"`
SubjectIds []string `bson:"subjectIds"`
PlatformStoreSkus ProductColorSkuPlatformStoreSkus `bson:"platformStoreSkus"`
}
type ProductColorSkus []ProductColorSku
func (receiver ProductColorSkus) FindPlatformStoreSku(platform types.Platform, store types.Store, route types.Route, skuId string) (ProductColorSkuPlatformStoreSku, bool) {
for _, sku := range receiver {
if sku.Pc3SkuId == skuId {
return sku.PlatformStoreSkus.FindPlatformStoreSku(platform, store, route)
}
}
return ProductColorSkuPlatformStoreSku{}, false
}
type ProductTitle struct {
En string `bson:"en"`
Cn string `bson:"cn"`
......@@ -110,6 +132,7 @@ type ProductColorSkuPlatformStoreSku struct {
Price float64 `bson:"price"` // 最终价格
FixPrice *float64 `bson:"fixPrice,omitempty"` // 固定价格
FixMargin *float64 `bson:"fixMargin,omitempty"` // 固定加价率(影响计价)
FixedQty *int `bson:"fixedQty,omitempty"` // 虚拟库存
Rate float64 `bson:"rate"` // OriginCurrency -> Currency 的汇率
CnyRate float64 `bson:"cnyRate"` // Currency -> CNY 的汇率
Margin float64 `bson:"margin"` // 加价率
......@@ -126,3 +149,12 @@ type ProductColorSkuPlatformStoreSku struct {
}
type ProductColorSkuPlatformStoreSkus []ProductColorSkuPlatformStoreSku
func (receiver ProductColorSkuPlatformStoreSkus) FindPlatformStoreSku(platform types.Platform, store types.Store, route types.Route) (ProductColorSkuPlatformStoreSku, bool) {
for _, sku := range receiver {
if sku.Platform == platform && sku.Store == store && sku.Route == route {
return sku, true
}
}
return ProductColorSkuPlatformStoreSku{}, false
}
......@@ -31,7 +31,10 @@ func (l *UploadLogic) Upload(in *colorway.ProductUploadRequest) (*colorway.Produ
if err != nil {
return nil, err
}
ps := l.svcCtx.ProductConvert.Convert(productList.GetDesignerProducts())
ps, err := l.svcCtx.ProductConvert.Convert(productList.GetDesignerProducts())
if err != nil {
return nil, err
}
var writeModels []mongo.WriteModel
for _, p := range ps {
filter := bson.M{
......
......@@ -3,10 +3,12 @@ package convert
import (
"fmt"
"git.chillcy.com/golang/chillcy/pkg/currency"
"git.chillcy.com/golang/chillcy/pkg/gender"
math2 "git.chillcy.com/golang/chillcy/pkg/math"
"git.chillcy.com/golang/chillcy/pkg/platform"
"git.chillcy.com/golang/chillcy/pkg/sdk/pc3"
"git.chillcy.com/golang/chillcy/pkg/slice"
"git.chillcy.com/golang/chillcy/project/pc4/pkg/rpc/pb/pc4"
"git.chillcy.com/golang/colorway/internal/pkg/database/mongo"
siteGroupMarginConfigModel "git.chillcy.com/golang/colorway/internal/pkg/database/mongo/model/SiteGroupMarginConfig"
productModel "git.chillcy.com/golang/colorway/internal/pkg/database/mongo/model/product"
......@@ -31,44 +33,43 @@ type PlatformStoreSku struct {
brandRankCache cache.BrandRank
}
func (receiver *PlatformStoreSku) Convert(p productModel.Product) (productModel.ProductColorSkuPlatformStoreSkus, error) {
func NewPlatformStoreSku(mongoManager *mongo.Manager, rate rate.Rate, brandRankCache cache.BrandRank) *PlatformStoreSku {
return &PlatformStoreSku{mongoManager: mongoManager, rate: rate, brandRankCache: brandRankCache}
}
// Convert 转换单个 SKU 的所有平台-店铺-路线价格
func (receiver *PlatformStoreSku) Convert(product *pc4.ProductModel, sku *pc4.SkuModel, oldProduct *productModel.Product) (productModel.ProductColorSkuPlatformStoreSkus, error) {
var platformStoreSkus productModel.ProductColorSkuPlatformStoreSkus
for _, color := range p.Colors {
for _, sku := range color.Skus {
sitePlatformPriceConfigs := receiver.mongoManager.SitePlatformPriceConfigModel.GetCacheMany(func(document sitePlatformPriceConfigModel.SitePlatformPriceConfig) bool {
return document.Site == sku.SiteId
})
categoryIds := p.Category.GetIds()
for _, sitePlatformPriceConfig := range sitePlatformPriceConfigs {
// todo fixMargin
// todo pc3SkuModel
var pc3SkuModel pc3.ModelSku
platformPriceSku, err := receiver.calculatePlatformPrice(p, sku, 1, pc3SkuModel, categoryIds, sitePlatformPriceConfig)
if err != nil {
return nil, err
}
platformStoreSkus = append(platformStoreSkus, platformPriceSku)
}
sitePlatformPriceConfigs := receiver.mongoManager.SitePlatformPriceConfigModel.GetCacheMany(func(document sitePlatformPriceConfigModel.SitePlatformPriceConfig) bool {
return document.Site == sku.GetSite()
})
categoryIds := product.GetCategoryIds()
for _, sitePlatformPriceConfig := range sitePlatformPriceConfigs {
var pc3SkuModel pc3.ModelSku
platformPriceSku, err := receiver.calculatePlatformPrice(product, sku, oldProduct, pc3SkuModel, categoryIds, sitePlatformPriceConfig)
if err != nil {
return nil, err
}
platformStoreSkus = append(platformStoreSkus, platformPriceSku)
}
return platformStoreSkus, nil
}
func (receiver *PlatformStoreSku) calculatePlatformPrice(product productModel.Product, productColorSku productModel.ProductColorSku, fixMargin float64, pc3SkuModel pc3.ModelSku, categories []string, priceConfig sitePlatformPriceConfigModel.SitePlatformPriceConfig) (productModel.ProductColorSkuPlatformStoreSku, error) {
var sku productModel.ProductColorSkuPlatformStoreSku
func (receiver *PlatformStoreSku) calculatePlatformPrice(product *pc4.ProductModel, sku *pc4.SkuModel, oldProduct *productModel.Product, pc3SkuModel pc3.ModelSku, categories []string, priceConfig sitePlatformPriceConfigModel.SitePlatformPriceConfig) (productModel.ProductColorSkuPlatformStoreSku, error) {
var psSku productModel.ProductColorSkuPlatformStoreSku
if !priceConfig.IsValid() {
return sku, fmt.Errorf("路线价格配置不合法")
return psSku, fmt.Errorf("路线价格配置不合法")
}
sku, err := receiver.calculateRoutePrice(product, productColorSku, fixMargin, pc3SkuModel, categories, priceConfig.RoutePriceConfig, priceConfig.Seller)
psSku, err := receiver.calculateRoutePrice(product, sku, oldProduct, pc3SkuModel, categories, priceConfig.RoutePriceConfig, priceConfig.Seller)
if err != nil {
return sku, err
return psSku, err
}
return sku, nil
return psSku, nil
}
func (receiver *PlatformStoreSku) calculateRoutePrice(product productModel.Product, productColorSku productModel.ProductColorSku, fixMargin float64, pc3SkuModel pc3.ModelSku, categories []string, routeConfig sitePlatformPriceConfigModel.RoutePriceConfig, seller types.Seller) (productModel.ProductColorSkuPlatformStoreSku, error) {
func (receiver *PlatformStoreSku) calculateRoutePrice(product *pc4.ProductModel, sku *pc4.SkuModel, oldProduct *productModel.Product, pc3SkuModel pc3.ModelSku, categories []string, routeConfig sitePlatformPriceConfigModel.RoutePriceConfig, seller types.Seller) (productModel.ProductColorSkuPlatformStoreSku, error) {
var platformStoreSku productModel.ProductColorSkuPlatformStoreSku
adapter, err := priceRouteAdapter.SelectAdapter(routeConfig.Route)
......@@ -82,7 +83,7 @@ func (receiver *PlatformStoreSku) calculateRoutePrice(product productModel.Produ
}
subjects := receiver.mongoManager.SubjectModel.GetCacheMany(func(document subjectModel.Subject) bool {
return slices.Contains(productColorSku.SubjectIds, document.Id.Hex())
return slices.Contains(product.GetSubjects(), document.Id.Hex())
})
var subjectRate, subjectId = float64(1), ""
for _, subject := range subjects {
......@@ -93,11 +94,19 @@ func (receiver *PlatformStoreSku) calculateRoutePrice(product productModel.Produ
}
// 固定毛利
var fixMargin float64
if oldProduct != nil {
if platformStoreSku, ok := oldProduct.FindPlatformStoreSku(seller.Platform, seller.Store, routeConfig.Route, sku.GetId()); ok {
if platformStoreSku.FixMargin != nil {
fixMargin = *platformStoreSku.FixMargin
}
}
}
margin := 1.0
if fixMargin != 0 {
margin = 1 + (fixMargin / 100)
} else {
margin, err = receiver.calculateMargin(product, productColorSku, routeConfig, pc3SkuModel, seller)
margin, err = receiver.calculateMargin(product, sku, routeConfig, pc3SkuModel, seller)
if err != nil {
return platformStoreSku, err
}
......@@ -317,7 +326,15 @@ func (receiver *PlatformStoreSku) calculateFreight(pc3Sku pc3.ModelSku, category
return 0, nil
}
func (receiver *PlatformStoreSku) calculateMargin(product productModel.Product, productColorSku productModel.ProductColorSku, routeConfig sitePlatformPriceConfigModel.RoutePriceConfig, pc3Sku pc3.ModelSku, seller types.Seller) (float64, error) {
func StringSliceToGenders(gs []string) gender.Genders {
var genders gender.Genders
for _, g := range gs {
genders = append(genders, gender.Gender(g))
}
return genders
}
func (receiver *PlatformStoreSku) calculateMargin(product *pc4.ProductModel, sku *pc4.SkuModel, routeConfig sitePlatformPriceConfigModel.RoutePriceConfig, pc3Sku pc3.ModelSku, seller types.Seller) (float64, error) {
floatMargin := 1.0
if routeConfig.Route == types.RouteComplex {
......@@ -326,10 +343,10 @@ func (receiver *PlatformStoreSku) calculateMargin(product productModel.Product,
comboMarginConfig, ok := receiver.mongoManager.SiteComboMarginConfigModel.GetCacheOne(func(document siteComboMarginConfigModel.SiteComboMarginConfig) bool {
return document.Seller.Platform == seller.Platform &&
document.Seller.Store == seller.Store && !document.Enable &&
document.SiteId == productColorSku.SiteId &&
slices.Contains(document.Brands, product.Brand.Id) &&
document.IsGenderMatch(product.Genders) &&
slices.Contains(document.Categories, product.Category.Id)
document.SiteId == sku.GetSite() &&
slices.Contains(document.Brands, product.GetBrandId()) &&
document.IsGenderMatch(StringSliceToGenders(product.GetGenders())) &&
slices.Contains(document.Categories, product.GetCategoriesV2().GetCategoryId())
})
if ok {
return 1 + (comboMarginConfig.Margin / 100), nil
......@@ -359,7 +376,7 @@ func (receiver *PlatformStoreSku) calculateMargin(product productModel.Product,
// 尝试获取站点分组加价配置
siteGroupMarginConfig, ok := receiver.mongoManager.SiteGroupMarginConfigModel.GetCacheOne(func(document siteGroupMarginConfigModel.SiteGroupMarginConfig) bool {
return document.Seller.Platform == seller.Platform && document.Seller.Store == seller.Store &&
document.IsMatch(productColorSku.SiteId)
document.IsMatch(sku.GetSite())
})
if ok {
return (1 + (siteGroupMarginConfig.Margin / 100)) * floatMargin, nil
......@@ -385,7 +402,7 @@ func (receiver *PlatformStoreSku) calculateMargin(product productModel.Product,
}
}
func (receiver *PlatformStoreSku) IsFloatMarginConfigMatch(cr siteFloatMarginConfigModel.ConditionRule, p productModel.Product) (bool, string) {
func (receiver *PlatformStoreSku) IsFloatMarginConfigMatch(cr siteFloatMarginConfigModel.ConditionRule, p *pc4.ProductModel) (bool, string) {
if cr.Type == "season" && p.Season != "" {
if p.Season == "CO" {
return true, cr.Get(siteFloatMarginConfigModel.ConditionKeySeasonCO)
......@@ -410,22 +427,22 @@ func (receiver *PlatformStoreSku) IsFloatMarginConfigMatch(cr siteFloatMarginCon
}
}
if cr.Type == "brand" {
if p.Brand.Id == "" {
if p.GetBrandId() == "" {
return true, cr.Get(siteFloatMarginConfigModel.ConditionKeyBrandTail)
} else {
topBrands, middleBrands := receiver.brandRankCache.GetBrandRankIds()
if slice.Contains(topBrands, p.Brand.Id) {
if slice.Contains(topBrands, p.GetBrandId()) {
return true, cr.Get(siteFloatMarginConfigModel.ConditionKeyBrandTop)
}
if slice.Contains(middleBrands, p.Brand.Id) {
if slice.Contains(middleBrands, p.GetBrandId()) {
return true, cr.Get(siteFloatMarginConfigModel.ConditionKeyBrandMiddle)
}
return true, cr.Get(siteFloatMarginConfigModel.ConditionKeyBrandTail)
}
}
if cr.Type == "category" && p.Category.Id != "" {
if slice.Contains([]string{"clothing", "bag", "shoes", "accessory"}, p.Category.Id) {
return true, cr.Get("category_" + p.Category.Id)
if cr.Type == "category" && p.GetCategoriesV2().GetCategoryId() != "" {
if slice.Contains([]string{"clothing", "bag", "shoes", "accessory"}, p.GetCategoriesV2().GetCategoryId()) {
return true, cr.Get("category_" + p.GetCategoriesV2().GetCategoryId())
}
}
return false, ""
......
package convert
import (
"context"
"errors"
"git.chillcy.com/golang/chillcy/pkg/gender"
"git.chillcy.com/golang/chillcy/pkg/slice"
"git.chillcy.com/golang/chillcy/project/pc4/pkg/rpc/pb/pc4"
"git.chillcy.com/golang/colorway/internal/pkg/database/mongo"
model "git.chillcy.com/golang/colorway/internal/pkg/database/mongo/model/product"
"git.chillcy.com/golang/colorway/internal/rpc/internal/svc/cache"
"git.chillcy.com/golang/colorway/internal/rpc/internal/svc/rate"
"go.mongodb.org/mongo-driver/bson"
mongo2 "go.mongodb.org/mongo-driver/mongo"
"sync"
)
type Product struct {
cache *cache.Cache
cache *cache.Cache
mongoManager *mongo.Manager
rate rate.Rate
brandRankCache cache.BrandRank
}
func NewProduct(cache *cache.Cache) *Product {
return &Product{cache: cache}
func NewProduct(cache *cache.Cache, mongoManager *mongo.Manager, rate rate.Rate, brandRankCache cache.BrandRank) *Product {
return &Product{cache: cache, mongoManager: mongoManager, rate: rate, brandRankCache: brandRankCache}
}
func (receiver *Product) Convert(designerProducts []*pc4.DesignerProduct) model.Products {
func (receiver *Product) Convert(designerProducts []*pc4.DesignerProduct) (model.Products, error) {
var ps model.Products
for _, designerProduct := range designerProducts {
sourceIds := getSourceIds(designerProduct)
......@@ -94,10 +103,23 @@ func (receiver *Product) Convert(designerProducts []*pc4.DesignerProduct) model.
}
}
}
p.Colors = buildProductColors(designerProduct.GetColorProducts(), ws, designerProduct.GetDesignerId())
var oldProductPointer *model.Product
oldProduct, err := receiver.mongoManager.ProductModel.FindOne(context.TODO(), bson.M{"styleId": p.StyleId})
if err != nil && !errors.Is(err, mongo2.ErrNoDocuments) {
return nil, err
}
if !errors.Is(err, mongo2.ErrNoDocuments) {
oldProductPointer = &oldProduct
}
platformStoreSkuConvert := NewPlatformStoreSku(receiver.mongoManager, receiver.rate, receiver.brandRankCache)
colors, err := buildProductColors(designerProduct.GetColorProducts(), ws, oldProductPointer, platformStoreSkuConvert)
if err != nil {
return nil, err
}
p.Colors = colors
ps = append(ps, p)
}
return ps
return ps, nil
}
func getCategory(category *pc4.CategoryV2) model.ProductCategory {
......@@ -113,15 +135,19 @@ func getCategory(category *pc4.CategoryV2) model.ProductCategory {
return pc
}
func buildProductColors(colorProducts []*pc4.ColorProduct, ws weightSelect, styleNumber string) model.ProductColors {
func buildProductColors(colorProducts []*pc4.ColorProduct, ws weightSelect, oldProductPointer *model.Product, platformStoreSkuConvert *PlatformStoreSku) (model.ProductColors, error) {
var pcs model.ProductColors
for _, colorProduct := range colorProducts {
pcs = append(pcs, buildProductColor(colorProduct, ws, styleNumber))
pc, err := buildProductColor(colorProduct, ws, oldProductPointer, platformStoreSkuConvert)
if err != nil {
return nil, err
}
pcs = append(pcs, pc)
}
return pcs
return pcs, nil
}
func buildProductColor(colorProduct *pc4.ColorProduct, ws weightSelect, styleNumber string) model.ProductColor {
func buildProductColor(colorProduct *pc4.ColorProduct, ws weightSelect, oldProductPointer *model.Product, platformStoreSkuConvert *PlatformStoreSku) (model.ProductColor, error) {
pc := model.ProductColor{}
once := &sync.Once{}
for _, productAndSkus := range colorProduct.GetProductAndSkusList() {
......@@ -142,7 +168,7 @@ func buildProductColor(colorProduct *pc4.ColorProduct, ws weightSelect, styleNum
}
for _, sku := range productAndSkus.GetSkus() {
pc.Skus = append(pc.Skus, model.ProductColorSku{
pcSku := model.ProductColorSku{
Pc3SkuId: sku.GetId(),
Pc3ProductId: product.GetId(),
SourceId: product.GetSourceId(),
......@@ -152,10 +178,16 @@ func buildProductColor(colorProduct *pc4.ColorProduct, ws weightSelect, styleNum
Price: sku.GetPriceDetail().GetNow(),
Currency: sku.GetPriceDetail().GetCurrency(),
SubjectIds: product.Subjects,
})
}
psSkus, err := platformStoreSkuConvert.Convert(product, sku, oldProductPointer)
if err != nil {
return model.ProductColor{}, err
}
pcSku.PlatformStoreSkus = psSkus
pc.Skus = append(pc.Skus, pcSku)
}
}
return pc
return pc, nil
}
func (receiver *Product) getWeightSelect(sourceIds []string) weightSelect {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment