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

259 lines
6.5 KiB
Go
Raw Normal View History

2025-03-17 02:08:22 +00:00
package repository
import (
"context"
"errors"
"time"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository"
mgo "code.30cm.net/digimon/library-go/mongo"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/mon"
)
type ProductRepositoryParam struct {
Conf *mgo.Conf
CacheConf cache.CacheConf
DBOpts []mon.Option
CacheOpts []cache.Option
}
type ProductRepository struct {
DB mgo.DocumentDBWithCacheUseCase
}
func NewProductRepository(param ProductRepositoryParam) repository.ProductRepository {
e := entity.Product{}
documentDB, err := mgo.MustDocumentDBWithCache(
param.Conf,
e.CollectionName(),
param.CacheConf,
param.DBOpts,
param.CacheOpts,
)
if err != nil {
panic(err)
}
return &ProductRepository{
DB: documentDB,
}
}
func (repo *ProductRepository) Insert(ctx context.Context, data *entity.Product) error {
if data.ID.IsZero() {
now := time.Now().UTC().UnixNano()
data.ID = primitive.NewObjectID()
data.CreatedAt = now
data.UpdatedAt = now
}
rk := domain.GetProductRK(data.ID.Hex())
_, err := repo.DB.InsertOne(ctx, rk, data)
return err
}
func (repo *ProductRepository) FindOneByID(ctx context.Context, id string) (*entity.Product, error) {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
var result *entity.Product
err = repo.DB.FindOne(ctx, domain.GetProductRK(id), &result, bson.M{"_id": oid})
switch {
case err == nil:
return result, nil
case errors.Is(err, mon.ErrNotFound):
return nil, ErrNotFound
default:
return nil, err
}
}
func (repo *ProductRepository) FindOneBySlug(ctx context.Context, slug string) (*entity.Product, error) {
var result *entity.Product
err := repo.DB.FindOne(ctx, domain.GetProductRK(slug), &result, bson.M{"slug": slug})
switch {
case err == nil:
return result, nil
case errors.Is(err, mon.ErrNotFound):
return nil, ErrNotFound
default:
return nil, err
}
}
func (repo *ProductRepository) Update(ctx context.Context, productID string, data *repository.ProductUpdateParams) (*mongo.UpdateResult, error) {
item, err := repo.FindOneByID(ctx, productID)
if err != nil {
return nil, err
}
now := time.Now().UTC().UnixNano()
// 動態構建更新內容
updateFields := bson.M{
"updated_at": now, // 確保 `updateAt` 總是更新
}
if data.Title != nil {
updateFields["title"] = *data.Title
}
if data.IsPublished != nil {
updateFields["is_published"] = *data.IsPublished
}
if data.StartTime != nil {
updateFields["start_time"] = *data.StartTime
}
if data.EndTime != nil {
updateFields["end_time"] = *data.EndTime
}
if data.ShortDescription != "" {
updateFields["short_description"] = data.ShortDescription
}
if data.Category != nil {
updateFields["category"] = *data.Category
}
if data.Details != nil {
updateFields["details"] = *data.Details
}
if data.ShortTitle != nil {
updateFields["short_title"] = *data.ShortTitle
}
if data.Slug != nil {
updateFields["slug"] = *data.Slug
}
if data.Amount != nil {
updateFields["amount"] = *data.Amount
}
if len(data.Media) > 0 {
updateFields["media"] = data.Media
}
if len(data.Media) > 0 {
updateFields["media"] = data.Media
}
if len(data.CustomFields) > 0 {
updateFields["custom_fields"] = data.CustomFields
}
oid, err := primitive.ObjectIDFromHex(productID)
if err != nil {
return nil, ErrInvalidObjectID
}
// 執行更新
rk := domain.GetProductRK(productID)
res, err := repo.DB.UpdateOne(ctx, rk, bson.M{"_id": oid}, bson.M{"$set": updateFields})
if err != nil {
return nil, err
}
_ = repo.DB.DelCache(ctx, domain.GetProductRK(*item.Slug))
return res, err
}
func (repo *ProductRepository) Delete(ctx context.Context, id string) error {
item, err := repo.FindOneByID(ctx, id)
if err != nil {
return err
}
_, err = repo.DB.DeleteOne(ctx, domain.GetProductRK(id), item)
if err != nil {
return err
}
_ = repo.DB.DelCache(ctx, domain.GetProductRK(*item.Slug))
return nil
}
func (repo *ProductRepository) ListProduct(ctx context.Context, params *repository.ProductQueryParams) ([]*entity.Product, int64, error) {
// 構建查詢過濾器
filter := bson.M{}
if params.UID != nil && *params.UID != "" {
filter["uid"] = *params.UID
}
if params.IsPublished != nil {
filter["is_published"] = *params.IsPublished
}
if params.Category != nil {
filter["category"] = *params.Category
}
if params.StartTime != nil {
filter["start_time"] = bson.M{"$gte": *params.StartTime} // 篩選開始時間在指定時間之後的專案
}
if params.EndTime != nil {
filter["end_time"] = bson.M{"$lte": *params.EndTime} // 篩選結束時間在指定時間之前的專案
}
if params.Slug != nil && *params.Slug != "" {
filter["slug"] = *params.Slug
}
// 設置排序選項
opts := options.Find().SetSkip((params.PageIndex - 1) * params.PageSize).SetLimit(params.PageSize)
opts.SetSort(bson.D{{Key: "updated_at", Value: -1}})
// 查詢符合條件的總數
count, err := repo.DB.GetClient().CountDocuments(ctx, filter)
if err != nil {
return nil, 0, err
}
// 執行查詢並獲取結果
var products []*entity.Product
err = repo.DB.GetClient().Find(ctx, &products, filter, opts)
if err != nil {
return nil, 0, err
}
return products, count, nil
}
func (repo *ProductRepository) Transaction(
ctx context.Context,
fn func(sessCtx mongo.SessionContext) (any, error),
opts ...*options.TransactionOptions) error {
// 獲取資料庫連接
db := repo.DB.GetClient().Database()
// 使用 MongoDB 的會話進行操作
return db.Client().UseSession(ctx, func(sessionCtx mongo.SessionContext) error {
// 開始交易,支持可選的 TransactionOptions
if err := sessionCtx.StartTransaction(opts...); err != nil {
return err
}
// 執行用戶提供的交易函數
result, err := fn(sessionCtx)
if err != nil {
// 若發生錯誤則中止交易
if abortErr := sessionCtx.AbortTransaction(ctx); abortErr != nil {
return abortErr
}
return err
}
// 提交交易
if err := sessionCtx.CommitTransaction(ctx); err != nil {
// 若提交失敗則中止交易
if abortErr := sessionCtx.AbortTransaction(ctx); abortErr != nil {
return abortErr
}
return err
}
// 返回交易執行結果
_ = result // 結果未使用,保留擴展可能
return nil
})
}