Skip to content

Commit

Permalink
feat(product): upsert specific and options
Browse files Browse the repository at this point in the history
  • Loading branch information
Matrix-X committed Jun 25, 2023
1 parent e08fed2 commit e085c9a
Show file tree
Hide file tree
Showing 15 changed files with 355 additions and 22 deletions.
14 changes: 14 additions & 0 deletions api/admin/crm/product/productspecific.api
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ service PowerX {
@handler CreateProductSpecific
post /product-specifics (CreateProductSpecificRequest) returns (CreateProductSpecificReply)

@doc "配置产品规格"
@handler ConfigProductSpecific
post /product-specifics/config (ConfigProductSpecificRequest) returns (ConfigProductSpecificReply)


@doc "全量产品规格"
@handler PutProductSpecific
Expand Down Expand Up @@ -97,6 +101,16 @@ type (
}
)

type (
ConfigProductSpecificRequest struct {
ProductSpecifics []ProductSpecific `json:"productSpecifics"`
}

ConfigProductSpecificReply struct {
Result bool `json:"result"`
}
)

type (
GetProductSpecificRequest struct {
ProductSpecificId int64 `path:"id"`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package productspecific

import (
"net/http"

"PowerX/internal/logic/admin/product/productspecific"
"PowerX/internal/svc"
"PowerX/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
)

func ConfigProductSpecificHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ConfigProductSpecificRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}

l := productspecific.NewConfigProductSpecificLogic(r.Context(), svcCtx)
resp, err := l.ConfigProductSpecific(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}
5 changes: 5 additions & 0 deletions internal/handler/routes.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions internal/logic/admin/product/createproductlogic.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ func TransformSpecificToSpecificReply(specific *product.ProductSpecific) (images
return nil
}
return &types.ProductSpecific{
Id: specific.Id,
ProductId: specific.ProductId,
Name: specific.Name,
SpecificOptions: TransformSpecificOptionsToSpecificOptionsReply(specific.Options),
}
Expand All @@ -248,7 +250,9 @@ func TransformSpecificOptionToSpecificOptionReply(option *product.SpecificOption
return nil
}
return &types.SpecificOption{
Name: option.Name,
IsActivated: option.IsActivated,
Id: option.Id,
ProductSpecificId: option.ProductSpecificId,
Name: option.Name,
IsActivated: option.IsActivated,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package productspecific

import (
product2 "PowerX/internal/model/product"
"PowerX/internal/types/errorx"
"context"

"PowerX/internal/svc"
"PowerX/internal/types"

"github.com/zeromicro/go-zero/core/logx"
)

type ConfigProductSpecificLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}

func NewConfigProductSpecificLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ConfigProductSpecificLogic {
return &ConfigProductSpecificLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}

func (l *ConfigProductSpecificLogic) ConfigProductSpecific(req *types.ConfigProductSpecificRequest) (resp *types.ConfigProductSpecificReply, err error) {

specifics := TransformProductSpecificsRequestToProductSpecifics(req.ProductSpecifics)
options := GetOptionsFromProductSpecificsRequest(specifics)
//fmt.Dump(specifics)
err = l.svcCtx.PowerX.ProductSpecific.ConfigProductSpecific(l.ctx, specifics, options)
//specifics, err = l.svcCtx.PowerX.ProductSpecific.UpsertProductSpecifics(l.ctx, specifics)
if err != nil {
return nil, errorx.WithCause(errorx.ErrBadRequest, err.Error())
}

// ReFact product skus
product, err := l.svcCtx.PowerX.Product.GetProduct(l.ctx, specifics[0].ProductId)
if err != nil {
return nil, errorx.WithCause(errorx.ErrBadRequest, err.Error())
}
err = l.svcCtx.PowerX.Product.ReFactSKUs(l.ctx, product)
if err != nil {
return nil, errorx.WithCause(errorx.ErrBadRequest, err.Error())
}

return &types.ConfigProductSpecificReply{
Result: true,
}, nil
}

func TransformProductSpecificsRequestToProductSpecifics(specificsRequest []types.ProductSpecific) []*product2.ProductSpecific {

specifics := []*product2.ProductSpecific{}
for _, specificRequest := range specificsRequest {
specifics = append(specifics, TransformProductSpecificRequestToProductSpecific(specificRequest))
}

return specifics
}

func GetOptionsFromProductSpecificsRequest(specifics []*product2.ProductSpecific) []*product2.SpecificOption {
options := []*product2.SpecificOption{}
for _, specific := range specifics {
if len(specific.Options) > 0 {
for _, option := range specific.Options {
options = append(options, option)
}
}
}
return options
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package productspecific
import (
product2 "PowerX/internal/model/product"
"context"
"time"

"PowerX/internal/svc"
"PowerX/internal/types"
Expand Down Expand Up @@ -40,17 +41,29 @@ func (l *CreateProductSpecificLogic) CreateProductSpecific(req *types.CreateProd
}

func TransformProductSpecificRequestToProductSpecific(specificRequest types.ProductSpecific) *product2.ProductSpecific {
return &product2.ProductSpecific{

if specificRequest.ProductId <= 0 || specificRequest.Name == "" {
return nil
}
specific := &product2.ProductSpecific{
ProductId: specificRequest.ProductId,
Name: specificRequest.Name,
Options: TransformSpecificOptionsRequestToSpecificOptions(specificRequest.SpecificOptions),
}

if specificRequest.Id > 0 {
specific.Id = specificRequest.Id
}
return specific
}

func TransformSpecificOptionsRequestToSpecificOptions(optionsRequest []*types.SpecificOption) (options []*product2.SpecificOption) {
options = []*product2.SpecificOption{}
for _, optionRequest := range optionsRequest {
options = append(options, TransformSpecificOptionRequestToSpecificOption(optionRequest))
option := TransformSpecificOptionRequestToSpecificOption(optionRequest)
if option != nil {
options = append(options, option)
}
}
return options
}
Expand All @@ -59,9 +72,23 @@ func TransformSpecificOptionRequestToSpecificOption(optionRequest *types.Specifi
if optionRequest == nil {
return nil
}
return &product2.SpecificOption{
ProductSpecificId: optionRequest.ProductSpecificId,
Name: optionRequest.Name,
IsActivated: optionRequest.IsActivated,

if optionRequest.ProductSpecificId < 0 || optionRequest.Name == "" {
return nil
}

option = &product2.SpecificOption{
Name: optionRequest.Name,
IsActivated: optionRequest.IsActivated,
}
// 前端如果是新建的选项,那么不会有 ProductSpecificId, 但仍需要新建
if optionRequest.ProductSpecificId > 0 {
option.ProductSpecificId = optionRequest.ProductSpecificId
}
// 更新已存在的选项
if optionRequest.Id > 0 {
option.Id = optionRequest.Id
option.UpdatedAt = time.Now()
}
return option
}
2 changes: 1 addition & 1 deletion internal/model/powermodel/powermodel.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ func UpsertModelsOnUniqueID(db *gorm.DB, mdl interface{}, uniqueName string,
fieldsToUpdate = GetModelFields(mdl)
}

if withAssociations {
if !withAssociations {
db = db.Omit(clause.Associations)
}

Expand Down
25 changes: 25 additions & 0 deletions internal/model/powermodel/powermodel_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package powermodel

import (
fmt "PowerX/pkg/printx"
"reflect"
"testing"
)

func TestGetModelFields(t *testing.T) {
type SpecificOption struct {
ProductSpecificId int64 `gorm:"comment: 产品规格Id;index;not null" json:"productSpecificId"`
Name string `gorm:"comment: 规格项名称;not null" json:"name"`
IsActivated bool `gorm:"comment: 是否被激活;" json:"isActivated"`
}

expectedFields := []string{"product_specific_id", "name", "is_activated"}

model := SpecificOption{}
fields := GetModelFields(model)
fmt.Dump(fields)

if !reflect.DeepEqual(fields, expectedFields) {
t.Errorf("Expected fields %v, but got %v", expectedFields, fields)
}
}
24 changes: 19 additions & 5 deletions internal/model/product/pivotskutospecificoption.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package product

import (
"PowerX/internal/model/powermodel"
"PowerX/pkg/securityx"
"fmt"
"github.com/ArtisanCloud/PowerLibs/v3/object"
)

// Table Name
Expand All @@ -13,11 +16,22 @@ func (mdl *PivotSkuToSpecificOption) TableName() string {
type PivotSkuToSpecificOption struct {
powermodel.PowerPivot

ProductId int64 `gorm:"comment:产品Id; column:product_id; not null;index:idx_product_id" json:"productId"`
SkuId int64 `gorm:"comment:SkuId; column:sku_id; not null;index:idx_sku_id" json:"SkuId"`
SpecificId int64 `gorm:"comment:规格Id; column:specific_id; not null;index:specific_id" json:"specificId"`
SpecificOptionId int64 `gorm:"comment:规格项Id; column:specific_option_id; not null;index:specific_option_id" json:"specificOptionId"`
IsActivated bool `gorm:"comment:是否被激活; column:is_activated;" json:"isActivated,optional"`
UniqueID object.NullString `gorm:"index:idx_unique_id;index:idx_product_id;index:idx_sku_id;index:idx_sku_id;index:index_specific_id;index:index_specific_option_id;column:index_unique_id;unique;not null"`
ProductId int64 `gorm:"comment:产品Id; column:product_id; not null;index:idx_product_id" json:"productId"`
SkuId int64 `gorm:"comment:SkuId; column:sku_id; not null;index:idx_sku_id" json:"SkuId"`
SpecificId int64 `gorm:"comment:规格Id; column:specific_id; not null;index:index_specific_id" json:"specificId"`
SpecificOptionId int64 `gorm:"comment:规格项Id; column:specific_option_id; not null;index:index_specific_option_id" json:"specificOptionId"`
IsActivated bool `gorm:"comment:是否被激活; column:is_activated;" json:"isActivated,optional"`
}

const TableNamePivotSkuToSpecificOption = "pivot_sku_to_specific_options"

func (mdl *PivotSkuToSpecificOption) GetPivotComposedUniqueID() object.NullString {
if mdl.ProductId > 0 && mdl.SkuId > 0 && mdl.SpecificId > 0 && mdl.SpecificOptionId > 0 {
strUniqueID := fmt.Sprintf("%d-%d-%d-%d", mdl.ProductId, mdl.SkuId, mdl.SpecificId, mdl.SpecificOptionId)
strUniqueID = securityx.HashStringData(strUniqueID)
return object.NewNullString(strUniqueID, true)
} else {
return object.NewNullString("", false)
}
}
5 changes: 3 additions & 2 deletions internal/model/product/productspecific.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ type SpecificOption struct {

ProductSpecificId int64 `gorm:"comment: 产品规格Id; index;not null" json:"productSpecificId"`
Name string `gorm:"comment: 规格项名称; not null" json:"name"`
IsActivated bool `gorm:"comment:是否被激活; column:is_activated;" json:"isActivated,optional"`
IsActivated bool `gorm:"comment: 是否被激活;" json:"isActivated"`
}

const ProductSpecificUniqueId = "name"
const ProductSpecificUniqueId = powermodel.UniqueId
const SpecificOptionUniqueId = powermodel.UniqueId
22 changes: 18 additions & 4 deletions internal/model/product/sku.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package product

import (
"PowerX/internal/model/powermodel"
"PowerX/pkg/securityx"
"fmt"
"github.com/ArtisanCloud/PowerLibs/v3/object"
"gorm.io/datatypes"
)

Expand All @@ -12,10 +15,21 @@ type SKU struct {
PivotSkuToSpecificOptions []*PivotSkuToSpecificOption `gorm:"foreignKey:SkuId;references:Id" json:"pivotSkuToSpecificOptions"`
PriceBookEntry *PriceBookEntry `gorm:"foreignKey:SkuId;references:Id" json:"priceBookEntry"`

ProductId int64 `gorm:"index:idx_product_id;not null;" json:"productId"`
SkuNo string `gorm:"comment:SKU编号" json:"sku"`
Inventory int `gorm:"comment:库存数量" json:"inventory"`
OptionIds datatypes.JSON `gorm:"comment:规格Ids" json:"OptionIds"`
UniqueID object.NullString `gorm:"index:idx_unique_id;index:idx_product_id;column:index_unique_id;unique;not null"`
ProductId int64 `gorm:"index:idx_product_id;not null;" json:"productId"`
SkuNo string `gorm:"comment:SKU编号" json:"sku"`
Inventory int `gorm:"comment:库存数量" json:"inventory"`
OptionIds datatypes.JSON `gorm:"comment:规格Ids" json:"OptionIds"`
}

const TableNameSKU = "sku"

func (mdl *SKU) GetComposedUniqueID() object.NullString {
if len(mdl.OptionIds) > 0 && mdl.ProductId > 0 {
strUniqueID := fmt.Sprintf("%d-%s", mdl.ProductId, mdl.OptionIds.String())
strUniqueID = securityx.HashStringData(strUniqueID)
return object.NewNullString(strUniqueID, true)
} else {
return object.NewNullString("", false)
}
}
31 changes: 31 additions & 0 deletions internal/model/product/sku_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package product

import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestSKU_GetComposedUniqueID(t *testing.T) {
sku := SKU{
ProductId: 40,
// 注意这里的json不能有空格
OptionIds: []byte(`[235,240]`),
}

expectedUniqueID := "42f065a6a7bdf58e09ad25e9cf5b2031" // 假设使用MD5进行哈希计算

composedUniqueID := sku.GetComposedUniqueID()
assert.True(t, composedUniqueID.Valid)
assert.Equal(t, expectedUniqueID, composedUniqueID.String)
}

func TestSKU_GetComposedUniqueID_Invalid(t *testing.T) {
sku := SKU{
ProductId: 123,
OptionIds: []byte{},
}

composedUniqueID := sku.GetComposedUniqueID()
assert.False(t, composedUniqueID.Valid)
assert.Empty(t, composedUniqueID.String)
}
8 changes: 8 additions & 0 deletions internal/types/types.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit e085c9a

Please sign in to comment.