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

352 lines
8.9 KiB
Go
Raw Normal View History

2025-03-19 06:45:44 +00:00
package repository
import (
"context"
"errors"
"fmt"
"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/product"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository"
mgo "code.30cm.net/digimon/library-go/mongo"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/mon"
"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"
)
type ProductItemRepositoryParam struct {
Conf *mgo.Conf
CacheConf cache.CacheConf
DBOpts []mon.Option
CacheOpts []cache.Option
}
type ProductItemRepository struct {
DB mgo.DocumentDBWithCacheUseCase
}
func NewProductItemRepository(param ProductItemRepositoryParam) repository.ProductItemRepository {
e := entity.ProductItems{}
documentDB, err := mgo.MustDocumentDBWithCache(
param.Conf,
e.CollectionName(),
param.CacheConf,
param.DBOpts,
param.CacheOpts,
)
if err != nil {
panic(err)
}
return &ProductItemRepository{
DB: documentDB,
}
}
func (repo *ProductItemRepository) Insert(ctx context.Context, items []entity.ProductItems) error {
now := time.Now().UTC().UnixNano()
docs := make([]any, 0, len(items))
for i := range items {
if items[i].ID.IsZero() {
items[i].ID = primitive.NewObjectID()
items[i].CreatedAt = now
items[i].UpdatedAt = now
}
docs = append(docs, items[i])
}
if len(docs) == 0 {
return nil
}
_, err := repo.DB.GetClient().InsertMany(ctx, docs)
return err
}
func (repo *ProductItemRepository) FindByID(ctx context.Context, id string) (*entity.ProductItems, error) {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, ErrInvalidObjectID
}
var data entity.ProductItems
rk := domain.GetProductItemRK(id)
err = repo.DB.FindOne(ctx, rk, &data, bson.M{"_id": oid})
switch {
case err == nil:
return &data, nil
case errors.Is(err, mon.ErrNotFound):
return nil, ErrNotFound
default:
return nil, err
}
}
func (repo *ProductItemRepository) Delete(ctx context.Context, ids []string) error {
if len(ids) == 0 {
return nil
}
objectIDs := make([]primitive.ObjectID, 0, len(ids))
cacheKeys := make([]string, 0, len(ids))
for _, id := range ids {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
return ErrInvalidObjectID
}
objectIDs = append(objectIDs, oid)
cacheKeys = append(cacheKeys, domain.GetProductItemRK(id))
}
// 刪除多筆 Mongo 資料
filter := bson.M{"_id": bson.M{"$in": objectIDs}}
_, err := repo.DB.GetClient().DeleteMany(ctx, filter)
// 可選:刪除 Redis 快取(每個 ID 都清除)
_ = repo.DB.DelCache(ctx, cacheKeys...)
return err
}
func (repo *ProductItemRepository) Update(ctx context.Context, id string, param *repository.ProductUpdateItem) error {
// 將 `id` 轉換為 MongoDB 的 ObjectID
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return ErrInvalidObjectID
}
// 構建更新文檔
setFields := bson.M{}
// 檢查並添加需要更新的欄位
if param.Name != nil {
setFields["name"] = *param.Name
}
if param.Description != nil {
setFields["description"] = *param.Description
}
if param.ShortDescription != nil {
setFields["short_description"] = *param.ShortDescription
}
if param.Price != nil {
setFields["price"] = *param.Price
}
if param.Stock != nil {
setFields["stock"] = *param.Stock
}
if param.IsUnLimit != nil {
setFields["is_un_limit"] = *param.IsUnLimit
}
if param.IsFree != nil {
setFields["is_free"] = *param.IsFree
}
if param.SKU != nil {
setFields["sku"] = *param.SKU
}
if param.TimeSeries != nil {
setFields["time_series"] = *param.TimeSeries
}
if len(param.Media) > 0 {
setFields["media"] = param.Media
}
if param.CustomFields != nil {
setFields["custom_fields"] = param.CustomFields
}
if param.Freight != nil {
setFields["freight"] = param.Freight
}
// 如果沒有任何需要更新的內容,直接返回
if len(setFields) == 0 {
return fmt.Errorf("no fields to update")
}
// 執行更新操作
filter := bson.M{"_id": objectID}
rk := domain.GetProductItemRK(id)
_, err = repo.DB.UpdateOne(ctx, rk, filter, bson.M{
"$set": setFields,
})
if err != nil {
return fmt.Errorf("failed to update product item: %w", err)
}
return nil
}
func (repo *ProductItemRepository) DeleteByReferenceID(ctx context.Context, id string) error {
// 1. 查詢所有符合 reference_id 的項目,只取 _id 欄位
filter := bson.M{"reference_id": id}
projection := bson.M{"_id": 1}
opts := options.Find().SetProjection(projection)
var items []entity.ProductItems
err := repo.DB.GetClient().Find(ctx, &items, filter, opts)
if err != nil {
return err
}
// 2. 刪除這些 ID 對應的 Redis 快取
cacheKeys := make([]string, 0, len(items))
for _, item := range items {
cacheKeys = append(cacheKeys, domain.GetProductItemRK(item.ID.Hex()))
}
if len(cacheKeys) > 0 {
_ = repo.DB.DelCache(ctx, cacheKeys...)
}
// 3. 刪除 DB 中的資料
delFilter := bson.M{"reference_id": id}
_, err = repo.DB.GetClient().DeleteMany(ctx, delFilter)
if err != nil {
return err
}
return nil
}
func (repo *ProductItemRepository) ListProductItem(ctx context.Context, param repository.ProductItemQueryParams) ([]entity.ProductItems, int64, error) {
// 構建查詢過濾器
filter := bson.M{}
ids := make([]primitive.ObjectID, 0, len(param.ItemID))
for _, item := range param.ItemID {
oid, err := primitive.ObjectIDFromHex(item)
if err != nil {
continue
}
ids = append(ids, oid)
}
// 添加篩選條件
if len(param.ItemID) > 0 {
filter["_id"] = bson.M{"$in": ids}
}
if param.ReferenceID != nil {
filter["reference_id"] = *param.ReferenceID
}
if param.IsFree != nil {
filter["is_free"] = *param.IsFree
}
if param.Status != nil {
filter["status"] = *param.Status
}
// 設置排序選項
opts := options.Find().SetSkip((param.PageIndex - 1) * param.PageSize).SetLimit(param.PageSize)
opts.SetSort(bson.D{{Key: "created_at", Value: -1}})
// 查詢符合條件的總數
count, err := repo.DB.GetClient().CountDocuments(ctx, filter)
if err != nil {
return nil, 0, err
}
// 執行查詢並獲取結果
var products []entity.ProductItems
err = repo.DB.GetClient().Find(ctx, &products, filter, opts)
if err != nil {
return nil, 0, err
}
return products, count, nil
}
func (repo *ProductItemRepository) UpdateStatus(ctx context.Context, id string, status product.ItemStatus) error {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return ErrInvalidObjectID
}
filter := bson.M{"_id": objectID}
update := bson.M{"$set": bson.M{"status": status}}
rk := domain.GetProductItemRK(id)
_, err = repo.DB.UpdateOne(ctx, rk, filter, update)
if err != nil {
return fmt.Errorf("failed to update status: %w", err)
}
return nil
}
func (repo *ProductItemRepository) IncSalesCount(ctx context.Context, id string, count int64) error {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return ErrInvalidObjectID
}
filter := bson.M{"_id": objectID}
update := bson.M{"$inc": bson.M{"sales_count": count}}
rk := domain.GetProductItemRK(id)
_, err = repo.DB.UpdateOne(ctx, rk, filter, update)
if err != nil {
return fmt.Errorf("failed to decrease stock: %w", err)
}
return nil
}
func (repo *ProductItemRepository) DecSalesCount(ctx context.Context, id string, count int64) error {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return ErrInvalidObjectID
}
filter := bson.M{"_id": objectID, "sales_count": bson.M{"$gte": count}}
update := bson.M{"$inc": bson.M{"sales_count": -count}}
rk := domain.GetProductItemRK(id)
_, err = repo.DB.UpdateOne(ctx, rk, filter, update)
if err != nil {
return fmt.Errorf("failed to decrease stock: %w", err)
}
return nil
}
func (repo *ProductItemRepository) GetSalesCount(ctx context.Context, ids []string) ([]repository.ProductItemSalesCount, error) {
objectIDs := make([]primitive.ObjectID, 0, len(ids))
for _, id := range ids {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
continue
}
objectIDs = append(objectIDs, objectID)
}
if len(objectIDs) == 0 {
return nil, ErrInvalidObjectID
}
result := make([]entity.ProductItems, 0, len(ids))
filter := bson.M{"_id": bson.M{"$in": objectIDs}}
err := repo.DB.GetClient().Find(ctx, &result, filter)
if err != nil {
return nil, err
}
stockMap := make([]repository.ProductItemSalesCount, 0, len(result))
for _, item := range result {
stockMap = append(stockMap, repository.ProductItemSalesCount{
ID: item.ID.Hex(),
Count: item.SalesCount,
})
}
return stockMap, nil
}
func (repo *ProductItemRepository) Index20250317001UP(ctx context.Context) (*mongo.Cursor, error) {
repo.DB.PopulateIndex(ctx, "reference_id", 1, false)
repo.DB.PopulateIndex(ctx, "status", 1, false)
return repo.DB.GetClient().Indexes().List(ctx)
}