app-cloudep-product-service/pkg/usecase/product_item.go

359 lines
11 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package usecase
import (
"context"
"math"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/product"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/usecase"
"code.30cm.net/digimon/library-go/errs"
"github.com/shopspring/decimal"
"github.com/zeromicro/go-zero/core/logx"
)
type ProductItemUseCaseParam struct {
ProductItems repository.ProductItemRepository
TagRepo repository.TagRepo
}
type ProductItemUseCase struct {
ProductItemUseCaseParam
}
func MustProductItemUseCase(param ProductItemUseCaseParam) usecase.ProductItemUseCase {
return &ProductItemUseCase{
param,
}
}
// --- 輔助函式 ---
// 將 usecase.ProductItems 轉換為 entity.ProductItems用於 Create
func toEntity(u *usecase.ProductItems) (entity.ProductItems, error) {
// 將價格字串轉換為 decimal.Decimal
priceDec, err := decimal.NewFromString(u.Price)
if err != nil {
return entity.ProductItems{}, err
}
return entity.ProductItems{
// ID 在新增時通常由資料庫自動產生
ReferenceID: u.ReferenceID,
Name: u.Name,
Description: u.Description,
ShortDescription: u.ShortDescription,
IsUnLimit: u.IsUnLimit,
IsFree: u.IsFree,
Stock: u.Stock,
Price: priceDec,
SKU: u.SKU,
TimeSeries: u.TimeSeries,
Media: convertMediaToEntity(u.Media),
Status: u.Status,
Freight: convertCustomFieldsToEntity(u.Freight),
CustomFields: convertCustomFieldsToEntity(u.CustomFields),
}, nil
}
// 將 entity.ProductItems 轉換為 usecase.ProductItems用於 Get 與 List
func fromEntity(e *entity.ProductItems) *usecase.ProductItems {
return &usecase.ProductItems{
ID: e.ID.Hex(),
ReferenceID: e.ReferenceID,
Name: e.Name,
Description: e.Description,
ShortDescription: e.ShortDescription,
IsUnLimit: e.IsUnLimit,
IsFree: e.IsFree,
Stock: e.Stock,
Price: e.Price.String(),
SKU: e.SKU,
TimeSeries: e.TimeSeries,
Media: convertEntityMedia(e.Media),
Status: e.Status,
Freight: convertEntityCustomFields(e.Freight),
CustomFields: convertEntityCustomFields(e.CustomFields),
SalesCount: e.SalesCount,
UpdatedAt: e.UpdatedAt,
CreatedAt: e.CreatedAt,
}
}
// Media 轉換usecase.Media -> entity.Media
func convertMediaToEntity(meds []usecase.Media) []entity.Media {
res := make([]entity.Media, len(meds))
for i, m := range meds {
res[i] = entity.Media{
Sort: m.Sort,
Type: m.Type,
URL: m.URL,
}
}
return res
}
// Media 轉換entity.Media -> usecase.Media
func convertEntityMedia(meds []entity.Media) []usecase.Media {
res := make([]usecase.Media, len(meds))
for i, m := range meds {
res[i] = usecase.Media{
Sort: m.Sort,
Type: m.Type,
URL: m.URL,
}
}
return res
}
// CustomFields 轉換usecase.CustomFields -> entity.CustomFields
func convertCustomFieldsToEntity(cfs []usecase.CustomFields) []entity.CustomFields {
res := make([]entity.CustomFields, len(cfs))
for i, cf := range cfs {
res[i] = entity.CustomFields{
Key: cf.Key,
Value: cf.Value,
}
}
return res
}
// CustomFields 轉換entity.CustomFields -> usecase.CustomFields
func convertEntityCustomFields(cfs []entity.CustomFields) []usecase.CustomFields {
res := make([]usecase.CustomFields, len(cfs))
for i, cf := range cfs {
res[i] = usecase.CustomFields{
Key: cf.Key,
Value: cf.Value,
}
}
return res
}
// --- 實作 ProductItemUseCase interface ---
func (use *ProductItemUseCase) Create(ctx context.Context, productItem *usecase.ProductItems) error {
// 轉換成 entity 層型別
ent, err := toEntity(productItem)
if err != nil {
return errs.InvalidFormatL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "product_id", Value: productItem},
{Key: "func", Value: "ProductItemUseCase.Create"},
{Key: "err", Value: err.Error()},
}, "failed to change use case into entity")
}
err = use.ProductItemUseCaseParam.ProductItems.Insert(ctx, []entity.ProductItems{ent})
if err != nil {
return errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "req", Value: productItem},
{Key: "func", Value: "ProductItemUseCaseParam.ProductItems.Insert"},
{Key: "err", Value: err.Error()},
}, "failed to insert product item")
}
return nil
}
func (use *ProductItemUseCase) Get(ctx context.Context, id string) (*usecase.ProductItems, error) {
ent, err := use.ProductItemUseCaseParam.ProductItems.FindByID(ctx, id)
if err != nil {
return nil, errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "id", Value: id},
{Key: "func", Value: "ProductItemUseCaseParam.ProductItems.FindByID"},
{Key: "err", Value: err.Error()},
}, "failed to create product items")
}
return fromEntity(ent), nil
}
func (use *ProductItemUseCase) DeleteByReferenceID(ctx context.Context, id string) error {
err := use.ProductItems.DeleteByReferenceID(ctx, id)
if err != nil {
return errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "id", Value: id},
{Key: "func", Value: "ProductItemUseCaseParam.ProductItems.DeleteByReferenceID"},
{Key: "err", Value: err.Error()},
}, "failed to delete product items")
}
return nil
}
func (use *ProductItemUseCase) Update(ctx context.Context, id string, data *usecase.UpdateProductItems) error {
// 構建更新參數(僅更新部分欄位)
update := &repository.ProductUpdateItem{}
if data.Name != nil {
update.Name = data.Name
}
if data.Description != nil {
update.Description = data.Description
}
if data.ShortDescription != nil {
update.ShortDescription = data.ShortDescription
}
if data.IsUnLimit != nil {
update.IsUnLimit = data.IsUnLimit
}
if data.IsFree != nil {
update.IsFree = data.IsFree
}
if data.Stock != nil {
if *data.Stock > math.MaxInt64 {
return errs.InvalidFormatL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "id", Value: id},
{Key: "data.Stock ", Value: *data.Stock},
}, "data.Stock too large to convert to int64")
}
// data.Stock 為 *uint64需先解引用再轉換為 int64
s := int64(*data.Stock)
update.Stock = &s
}
if data.Price != nil {
// data.Price 為 *string需先解引用並轉換為 decimal.Decimal
dec, err := decimal.NewFromString(*data.Price)
if err != nil {
return errs.InvalidFormat("failed to convert price to decimal")
}
update.Price = &dec
}
if data.SKU != nil {
update.SKU = data.SKU
}
// 假設有 TimeSeries 欄位也需要更新(若有定義的話)
if data.TimeSeries != nil {
update.TimeSeries = data.TimeSeries
}
if len(data.Media) > 0 {
update.Media = convertMediaToEntity(data.Media)
}
if len(data.CustomFields) > 0 {
update.CustomFields = convertCustomFieldsToEntity(data.CustomFields)
}
if len(data.Freight) > 0 {
update.Freight = convertCustomFieldsToEntity(data.Freight)
}
if err := use.ProductItemUseCaseParam.ProductItems.Update(ctx, id, update); err != nil {
return errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "id", Value: id},
{Key: "req", Value: data},
{Key: "func", Value: "ProductItemUseCaseParam.ProductItems.Update"},
{Key: "err", Value: err.Error()},
}, "failed to update product item")
}
return nil
}
func (use *ProductItemUseCase) UpdateStatus(ctx context.Context, id string, status product.ItemStatus) error {
err := use.ProductItemUseCaseParam.ProductItems.UpdateStatus(ctx, id, status)
if err != nil {
return errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "id", Value: id},
{Key: "status", Value: status},
{Key: "func", Value: "ProductItemUseCaseParam.ProductItems.UpdateStatus"},
{Key: "err", Value: err.Error()},
}, "failed to update product item status")
}
return nil
}
func (use *ProductItemUseCase) IncSalesCount(ctx context.Context, id string, saleCount uint64) error {
if saleCount > math.MaxInt64 {
return errs.InvalidFormatL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "id", Value: id},
{Key: "saleCount", Value: saleCount},
}, "saleCount too large to convert to int64")
}
// repository 方法使用 int64故需轉型
if err := use.ProductItemUseCaseParam.ProductItems.IncSalesCount(ctx, id, int64(saleCount)); err != nil {
return errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "id", Value: id},
{Key: "saleCount", Value: saleCount},
{Key: "func", Value: "ProductItemUseCaseParam.ProductItems.IncSalesCount"},
{Key: "err", Value: err.Error()},
}, "failed to insert product item")
}
return nil
}
func (use *ProductItemUseCase) DecSalesCount(ctx context.Context, id string, saleCount uint64) error {
if saleCount > math.MaxInt64 {
return errs.InvalidFormatL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "id", Value: id},
{Key: "saleCount", Value: saleCount},
}, "saleCount too large to convert to int64")
}
if err := use.ProductItemUseCaseParam.ProductItems.DecSalesCount(ctx, id, int64(saleCount)); err != nil {
return errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "id", Value: id},
{Key: "saleCount", Value: saleCount},
{Key: "func", Value: "ProductItemUseCaseParam.ProductItems.DecSalesCount"},
{Key: "err", Value: err.Error()},
}, "failed to Dec product item")
}
return nil
}
func (use *ProductItemUseCase) Delete(ctx context.Context, ids []string) error {
// repository.Delete 接收 slice因此將 id 放入 slice 中
if err := use.ProductItemUseCaseParam.ProductItems.Delete(ctx, ids); err != nil {
return errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "id", Value: ids},
{Key: "func", Value: "ProductItemUseCaseParam.ProductItems.Delete"},
{Key: "err", Value: err.Error()},
}, "failed to delete product item")
}
return nil
}
func (use *ProductItemUseCase) List(ctx context.Context, filter usecase.QueryProductItemParam) ([]*usecase.ProductItems, int64, error) {
repoQuery := repository.ProductItemQueryParams{
PageSize: filter.PageSize,
PageIndex: filter.PageIndex,
ReferenceID: filter.ReferenceID,
IsFree: filter.IsFree,
Status: filter.Status,
IsUnlimited: filter.IsUnLimit,
// 注意:若需要依 IsUnLimit 過濾,需擴充 repository.ProductItemQueryParams 結構
}
entities, total, err := use.ProductItemUseCaseParam.ProductItems.ListProductItem(ctx, repoQuery)
if err != nil {
return nil, 0, errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "filter", Value: filter},
{Key: "func", Value: "ProductItemUseCaseParam.ProductItems.ListProductItem"},
{Key: "err", Value: err.Error()},
}, "failed to list product item")
}
result := make([]*usecase.ProductItems, 0, len(entities))
for i := range entities {
// 逐筆轉換 entity -> usecase 型別
result = append(result, fromEntity(&entities[i]))
}
return result, total, nil
}