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 } oid, err := primitive.ObjectIDFromHex(id) if err != nil { return ErrInvalidObjectID } filter := bson.M{"_id": oid} _, err = repo.DB.DeleteOne(ctx, domain.GetProductRK(id), filter) 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 }) } func (repo *ProductRepository) Index20250317001UP(ctx context.Context) (*mongo.Cursor, error) { // 等價於 db.account.createIndex({"create_at": 1}) repo.DB.PopulateIndex(ctx, "uid", 1, false) repo.DB.PopulateIndex(ctx, "slug", 1, true) repo.DB.PopulateIndex(ctx, "category", 1, false) return repo.DB.GetClient().Indexes().List(ctx) }