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
|
|
|
|
})
|
|
|
|
}
|
2025-03-19 06:45:44 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|