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

359 lines
11 KiB
Go
Raw Permalink Normal View History

2025-04-01 09:44:57 +00:00
package usecase
import (
2025-04-06 02:08:46 +00:00
"context"
"math"
2025-04-01 09:44:57 +00:00
"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
}
2025-04-06 02:08:46 +00:00
2025-04-01 09:44:57 +00:00
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,
2025-04-09 14:46:53 +00:00
UpdatedAt: e.UpdatedAt,
CreatedAt: e.CreatedAt,
2025-04-01 09:44:57 +00:00
}
}
// 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,
}
}
2025-04-06 02:08:46 +00:00
2025-04-01 09:44:57 +00:00
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,
}
}
2025-04-06 02:08:46 +00:00
2025-04-01 09:44:57 +00:00
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,
}
}
2025-04-06 02:08:46 +00:00
2025-04-01 09:44:57 +00:00
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,
}
}
2025-04-06 02:08:46 +00:00
2025-04-01 09:44:57 +00:00
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")
}
2025-04-06 02:08:46 +00:00
2025-04-01 09:44:57 +00:00
return fromEntity(ent), nil
}
2025-04-09 09:29:56 +00:00
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
}
2025-04-01 09:44:57 +00:00
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 {
2025-04-06 02:08:46 +00:00
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")
}
2025-04-01 09:44:57 +00:00
// 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 {
2025-04-06 02:08:46 +00:00
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")
}
2025-04-01 09:44:57 +00:00
// 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 {
2025-04-06 02:08:46 +00:00
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")
}
2025-04-01 09:44:57 +00:00
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
}
2025-04-09 09:29:56 +00:00
func (use *ProductItemUseCase) Delete(ctx context.Context, ids []string) error {
2025-04-01 09:44:57 +00:00
// repository.Delete 接收 slice因此將 id 放入 slice 中
2025-04-09 09:29:56 +00:00
if err := use.ProductItemUseCaseParam.ProductItems.Delete(ctx, ids); err != nil {
2025-04-01 09:44:57 +00:00
return errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
2025-04-09 09:29:56 +00:00
{Key: "id", Value: ids},
2025-04-01 09:44:57 +00:00
{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,
2025-04-10 07:47:07 +00:00
IsUnlimited: filter.IsUnLimit,
2025-04-01 09:44:57 +00:00
// 注意:若需要依 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")
}
2025-04-06 02:08:46 +00:00
result := make([]*usecase.ProductItems, 0, len(entities))
2025-04-01 09:44:57 +00:00
for i := range entities {
// 逐筆轉換 entity -> usecase 型別
result = append(result, fromEntity(&entities[i]))
}
2025-04-06 02:08:46 +00:00
2025-04-01 09:44:57 +00:00
return result, total, nil
}