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") }