523 lines
14 KiB
Go
523 lines
14 KiB
Go
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")
|
||
}
|