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

523 lines
14 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 (
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity"
"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/app-cloudep-product-service/pkg/utils"
"code.30cm.net/digimon/library-go/errs"
"context"
"github.com/zeromicro/go-zero/core/logx"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readconcern"
)
type ProductUseCaseParam struct {
ProductRepo repository.ProductRepository
TagRepo repository.TagRepo
TagBinding repository.TagBindingRepo
ProductStatisticsRepo repository.ProductStatisticsRepo
}
type ProductUseCase struct {
ProductUseCaseParam
}
func MustProductUseCase(param ProductUseCaseParam) usecase.ProductUseCase {
return &ProductUseCase{
param,
}
}
func (use *ProductUseCase) Create(ctx context.Context, product *usecase.Product) error {
// 資料前處理:準備 entity.Product 實體
insert := &entity.Product{}
if product.UID != nil {
insert.UID = *product.UID
}
if product.Title != nil {
insert.Title = *product.Title
}
if product.IsPublished != nil {
insert.IsPublished = *product.IsPublished
}
if product.Category != nil {
insert.Category = *product.Category
}
if product.ShortTitle != nil {
insert.ShortTitle = product.ShortTitle
}
if product.Details != nil {
insert.Details = product.Details
}
if product.ShortDescription != nil {
insert.ShortDescription = *product.ShortDescription
}
if product.Slug != nil {
insert.Slug = product.Slug
}
if product.Amount != 0 {
insert.Amount = product.Amount
}
if product.StartTime != nil {
st := utils.Rfc3339ToUnix(utils.ToValue(product.StartTime))
insert.StartTime = &st
}
if product.EndTime != nil {
et := utils.Rfc3339ToUnix(utils.ToValue(product.EndTime))
insert.EndTime = &et
}
if len(product.Media) > 0 {
medias := make([]entity.Media, 0, len(product.Media))
for _, m := range product.Media {
medias = append(medias, entity.Media{
Sort: m.Sort,
URL: m.URL,
Type: m.Type,
})
}
insert.Media = medias
}
if len(product.CustomFields) > 0 {
cf := make([]entity.CustomFields, 0, len(product.CustomFields))
for _, field := range product.CustomFields {
cf = append(cf, entity.CustomFields{
Key: field.Key,
Value: field.Value,
})
}
insert.CustomFields = cf
}
// 綁定 Tags
tagsBinding := make([]*entity.TagsBindingTable, 0, len(product.Tags))
for _, tag := range product.Tags {
tagsBinding = append(tagsBinding, &entity.TagsBindingTable{
ReferenceID: insert.ID.Hex(),
TagID: tag,
})
}
// Transaction 設定:只做必要寫入
opts := options.Transaction().SetReadConcern(readconcern.Local())
err := use.ProductRepo.Transaction(ctx, func(sessCtx mongo.SessionContext) (any, error) {
// 插入 Product
if err := use.ProductRepo.Insert(sessCtx, insert); err != nil {
e := errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "req", Value: product},
{Key: "func", Value: "ProductRepo.Insert"},
{Key: "err", Value: err.Error()},
},
"failed to create product")
return nil, e
}
// 插入 Product 統計資料
if err := use.ProductStatisticsRepo.Create(sessCtx, &entity.ProductStatistics{
ProductID: insert.ID.Hex(),
Orders: 0,
OrdersUpdateTime: 0,
AverageRating: 0,
AverageRatingUpdateTime: 0,
FansCount: 0,
FansCountUpdateTime: 0,
}); err != nil {
e := errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "ProductID", Value: insert.ID.Hex()},
{Key: "func", Value: "ProductStatisticsRepo.Create"},
{Key: "err", Value: err.Error()},
},
"failed to create product statistics")
return nil, e
}
if err := use.TagBinding.BindTags(sessCtx, tagsBinding); err != nil {
e := errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "ReferenceID", Value: insert.ID.Hex()},
{Key: "tags", Value: product.Tags},
{Key: "func", Value: "TagBinding.BindTag"},
{Key: "err", Value: err.Error()},
}, "failed to binding product tags")
return nil, e
}
return nil, nil
}, opts)
if err != nil {
return err
}
return nil
}
func (use *ProductUseCase) Update(ctx context.Context, id string, product *usecase.Product) error {
// 資料前處理:準備 entity.Product 實體
update := &repository.ProductUpdateParams{}
if product.Title != nil {
update.Title = product.Title
}
if product.IsPublished != nil {
update.IsPublished = product.IsPublished
}
if product.Category != nil {
update.Category = product.Category
}
if product.ShortTitle != nil {
update.ShortTitle = product.ShortTitle
}
if product.Details != nil {
update.Details = product.Details
}
if product.ShortDescription != nil {
update.ShortDescription = *product.ShortDescription
}
if product.Slug != nil {
update.Slug = product.Slug
}
if product.Amount != 0 {
update.Amount = &product.Amount
}
if product.StartTime != nil {
st := utils.Rfc3339ToUnix(utils.ToValue(product.StartTime))
update.StartTime = &st
}
if product.EndTime != nil {
et := utils.Rfc3339ToUnix(utils.ToValue(product.EndTime))
update.EndTime = &et
}
if len(product.Media) > 0 {
medias := make([]entity.Media, 0, len(product.Media))
for _, m := range product.Media {
medias = append(medias, entity.Media{
Sort: m.Sort,
URL: m.URL,
Type: m.Type,
})
}
update.Media = medias
}
if len(product.CustomFields) > 0 {
cf := make([]entity.CustomFields, 0, len(product.CustomFields))
for _, field := range product.CustomFields {
cf = append(cf, entity.CustomFields{
Key: field.Key,
Value: field.Value,
})
}
update.CustomFields = cf
}
// 綁定 Tags
tagsBinding := make([]*entity.TagsBindingTable, 0, len(product.Tags))
for _, tag := range product.Tags {
tagsBinding = append(tagsBinding, &entity.TagsBindingTable{
ReferenceID: id,
TagID: tag,
})
}
// Transaction 設定:只做必要寫入
opts := options.Transaction().SetReadConcern(readconcern.Local())
err := use.ProductRepo.Transaction(ctx, func(sessCtx mongo.SessionContext) (any, error) {
_, err := use.ProductRepo.Update(sessCtx, id, update)
if err != nil {
e := errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "req", Value: product},
{Key: "func", Value: "ProductRepo.Update"},
{Key: "err", Value: err.Error()},
},
"failed to update product")
return nil, e
}
if err := use.TagBinding.UnbindTagByReferenceID(sessCtx, id); err != nil {
e := errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "ReferenceID", Value: id},
{Key: "func", Value: "TagBinding.UnbindTagByReferenceID"},
{Key: "err", Value: err.Error()},
}, "failed to unbind tags")
return nil, e
}
if err := use.TagBinding.BindTags(sessCtx, tagsBinding); err != nil {
e := errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "ReferenceID", Value: id},
{Key: "tags", Value: product.Tags},
{Key: "func", Value: "TagBinding.BindTags"},
{Key: "err", Value: err.Error()},
}, "failed to binding product tags")
return nil, e
}
return nil, nil
}, opts)
if err != nil {
return err
}
return nil
}
func (use *ProductUseCase) Delete(ctx context.Context, id string) error {
// Transaction 設定:只做必要寫入
opts := options.Transaction().SetReadConcern(readconcern.Local())
err := use.ProductRepo.Transaction(ctx, func(sessCtx mongo.SessionContext) (any, error) {
err := use.ProductRepo.Delete(sessCtx, id)
if err != nil {
e := errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "ReferenceID", Value: id},
{Key: "func", Value: "ProductRepo.Delete"},
{Key: "err", Value: err.Error()},
}, "failed to delete product")
return nil, e
}
err = use.TagRepo.UnbindTagByReferenceID(sessCtx, id)
if err != nil {
e := errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "ReferenceID", Value: id},
{Key: "func", Value: "TagBinding.UnbindTagByReferenceID"},
{Key: "err", Value: err.Error()},
}, "failed to unbind tags")
return nil, e
}
return nil, nil
}, opts)
if err != nil {
return err
}
return nil
}
func (use *ProductUseCase) Get(ctx context.Context, id string) (*usecase.ProductResp, error) {
product, err := use.ProductRepo.FindOneByID(ctx, id)
if err != nil {
e := errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "product_id", Value: id},
{Key: "func", Value: "ProductRepo.FindOneByID"},
{Key: "err", Value: err.Error()},
}, "failed to find product")
return nil, e
}
productStatistics, err := use.ProductStatisticsRepo.GetByID(ctx, id)
if err != nil {
e := errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "product_id", Value: id},
{Key: "func", Value: "ProductStatisticsRepo.GetByID"},
{Key: "err", Value: err.Error()},
}, "failed to get product statistics")
return nil, e
}
tags, err := use.TagRepo.GetBindingsByReference(ctx, id)
if err != nil {
e := errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "product_id", Value: id},
{Key: "func", Value: "TagRepo.GetBindingsByReference"},
{Key: "err", Value: err.Error()},
}, "failed to get tags")
return nil, e
}
t := make([]string, 0, len(tags))
for _, item := range tags {
t = append(t, item.TagID)
}
tagsInfo, err := use.TagRepo.GetByIDs(ctx, t)
if err != nil {
e := errs.DBErrorL(logx.WithContext(ctx),
[]logx.LogField{
{Key: "product_id", Value: id},
{Key: "func", Value: "TagRepo.GetByIDs"},
{Key: "err", Value: err.Error()},
}, "failed to get tags")
return nil, e
}
// 組合資料
result := &usecase.ProductResp{
ID: product.ID.Hex(),
UID: product.UID,
Title: product.Title,
ShortDescription: product.ShortDescription,
IsPublished: product.IsPublished,
Amount: product.Amount,
Category: product.Category,
Orders: productStatistics.Orders,
AverageRating: productStatistics.AverageRating,
FansCount: productStatistics.FansCount,
UpdatedAt: utils.UnixToRfc3339(product.UpdatedAt),
CreatedAt: utils.UnixToRfc3339(product.CreatedAt),
}
for _, tag := range tagsInfo {
item := usecase.Tags{
ID: tag.ID.Hex(),
Types: tag.Types,
Name: tag.Name,
ShowType: tag.ShowType,
UpdatedAt: utils.UnixToRfc3339(tag.UpdatedAt),
CreatedAt: utils.UnixToRfc3339(tag.CreatedAt),
}
if tag.Cover != nil {
item.Cover = *tag.Cover
}
result.Tags = append(result.Tags, item)
}
if len(product.Media) > 0 {
medias := make([]usecase.Media, 0, len(product.Media))
for _, m := range product.Media {
medias = append(medias, usecase.Media{
Sort: m.Sort,
URL: m.URL,
Type: m.Type,
})
}
result.Media = medias
}
if len(product.CustomFields) > 0 {
cf := make([]usecase.CustomFields, 0, len(product.CustomFields))
for _, field := range product.CustomFields {
cf = append(cf, usecase.CustomFields{
Key: field.Key,
Value: field.Value,
})
}
result.CustomFields = cf
}
if product.Slug != nil {
result.Slug = *product.Slug
}
if product.ShortTitle != nil {
result.ShortTitle = *product.ShortTitle
}
if product.Details != nil {
result.Details = *product.Details
}
if product.StartTime != nil {
result.StartTime = utils.UnixToRfc3339(*product.StartTime)
}
if product.EndTime != nil {
result.EndTime = utils.UnixToRfc3339(*product.EndTime)
}
if productStatistics.OrdersUpdateTime > 0 {
result.OrdersUpdateTime = utils.UnixToRfc3339(productStatistics.OrdersUpdateTime)
}
if productStatistics.FansCountUpdateTime > 0 {
result.FansCountUpdateTime = utils.UnixToRfc3339(productStatistics.FansCountUpdateTime)
}
if productStatistics.AverageRatingUpdateTime > 0 {
result.AverageRatingUpdateTime = utils.UnixToRfc3339(productStatistics.AverageRatingUpdateTime)
}
return result, nil
}
func (use *ProductUseCase) List(ctx context.Context, data usecase.ProductQueryParams) ([]*usecase.ProductResp, int64, error) {
query := &repository.ProductQueryParams{
PageSize: data.PageSize,
PageIndex: data.PageIndex,
}
if data.Slug != nil {
query.Slug = data.Slug
}
if data.UID != nil {
query.UID = data.UID
}
if data.IsPublished != nil {
query.IsPublished = data.IsPublished
}
if data.Category != nil {
query.Category = data.Category
}
if data.StartTime != nil {
query.StartTime = data.StartTime
}
EndTime * int64 // 結束時間Unix 時間戳)
Slug * string // URL 後綴
product, i, err := use.ProductRepo.ListProduct(ctx, &repository.ProductQueryParams{})
if err != nil {
return nil, 0, err
}
}
func (use *ProductUseCase) IncOrders(ctx context.Context, productID string, count int64) error {
//TODO implement me
panic("implement me")
}
func (use *ProductUseCase) DecOrders(ctx context.Context, productID string, count int64) error {
//TODO implement me
panic("implement me")
}
func (use *ProductUseCase) UpdateAverageRating(ctx context.Context, productID string, averageRating float64) error {
//TODO implement me
panic("implement me")
}
func (use *ProductUseCase) IncFansCount(ctx context.Context, productID string, fansCount uint64) error {
//TODO implement me
panic("implement me")
}
func (use *ProductUseCase) DecFansCount(ctx context.Context, productID string, fansCount uint64) error {
//TODO implement me
panic("implement me")
}
func (use *ProductUseCase) BindTag(ctx context.Context, binding usecase.TagsBindingTable) error {
//TODO implement me
panic("implement me")
}
func (use *ProductUseCase) UnbindTag(ctx context.Context, binding usecase.TagsBindingTable) error {
//TODO implement me
panic("implement me")
}
func (use *ProductUseCase) GetBindingsByReference(ctx context.Context, referenceID string) ([]usecase.TagsBindingTableResp, error) {
//TODO implement me
panic("implement me")
}
func (use *ProductUseCase) ListTagBinding(ctx context.Context, params usecase.TagBindingQueryParams) ([]usecase.TagsBindingTableResp, int64, error) {
//TODO implement me
panic("implement me")
}