feat: product tags

This commit is contained in:
王性驊 2025-03-20 17:11:56 +08:00
parent fdc0799fcc
commit 2451bc4257
5 changed files with 342 additions and 5 deletions

View File

@ -5,12 +5,16 @@ import (
"go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/bson/primitive"
) )
type ProductTags struct { type Tags struct {
ID primitive.ObjectID `bson:"_id,omitempty"` // 專案 ID ID primitive.ObjectID `bson:"_id,omitempty"` // 專案 ID
Types product.ItemType `bson:"types"` // Tag 類型 Types product.ItemType `bson:"types"` // Tag 類型
Name string `bson:"name"` Name string `bson:"name"` // tag 名稱
ShowType product.ShowType `bson:"show_type"` // 顯示筐 ShowType product.ShowType `bson:"show_type"` // 顯示筐
Cover *string `bson:"cover,omitempty"` // 封面圖片 Cover *string `bson:"cover,omitempty"` // 封面圖片
UpdatedAt int64 `bson:"updated_at" json:"updated_at"` // 更新時間 UpdatedAt int64 `bson:"updated_at" json:"updated_at"` // 更新時間
CreatedAt int64 `bson:"created_at" json:"created_at"` // 創建時間 CreatedAt int64 `bson:"created_at" json:"created_at"` // 創建時間
} }
func (p *Tags) CollectionName() string {
return "tags"
}

View File

@ -18,6 +18,7 @@ const (
GetProductRedisKey RedisKey = "get" GetProductRedisKey RedisKey = "get"
GetProductItemRedisKey RedisKey = "get_item" GetProductItemRedisKey RedisKey = "get_item"
GetProductStatisticsRedisKey RedisKey = "statistics" GetProductStatisticsRedisKey RedisKey = "statistics"
GetTagsRedisKey RedisKey = "tags"
) )
func GetProductRK(id string) string { func GetProductRK(id string) string {
@ -31,3 +32,7 @@ func GetProductItemRK(id string) string {
func GetProductStatisticsRK(id string) string { func GetProductStatisticsRK(id string) string {
return GetProductStatisticsRedisKey.With(id).ToString() return GetProductStatisticsRedisKey.With(id).ToString()
} }
func GetTagsRK(id string) string {
return GetTagsRedisKey.With(id).ToString()
}

View File

@ -1,7 +1,73 @@
package repository package repository
import (
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/product"
"context"
"go.mongodb.org/mongo-driver/mongo"
)
// TagRepo 定義與 tag (Tags) 資料表相關的 CRUD 與查詢操作
type TagRepo interface { type TagRepo interface {
Base
TagBindingRepo
Index
} }
type TagBindingRepo interface { type Base interface {
// Create 新增一筆 Tag 資料
Create(ctx context.Context, tag *entity.Tags) error
// GetByID 根據 tag 的內部 ID 取得資料
GetByID(ctx context.Context, id string) (*entity.Tags, error)
// Update 更新現有的 Tag 資料
Update(ctx context.Context, id string, tag TagModifyParams) error
// Delete 刪除指定 ID 的 Tag 資料
Delete(ctx context.Context, id string) error
// List 根據查詢條件取得 Tag 資料列表
// 回傳值分別為資料列表與符合條件的總筆數
List(ctx context.Context, params TagQueryParams) ([]*entity.Tags, int64, error)
}
// TagQueryParams 為查詢 Tags 時的參數結構
type TagQueryParams struct {
Types *product.ItemType // 過濾 Tag 類型
Name *string // 過濾名稱(部分比對)
ShowType *product.ShowType // 過濾顯示筐
// 可根據需求增加其他查詢條件,如分頁、排序等
PageSize int64
PageIndex int64
}
type TagModifyParams struct {
Types *product.ItemType // 過濾 Tag 類型
Name *string // 過濾名稱(部分比對)
ShowType *product.ShowType // 過濾顯示筐
Cover *string `bson:"cover,omitempty"` // 封面圖片
}
// TagBindingRepo 定義與 tag binding (TagsBindingTable) 資料表相關的操作
type TagBindingRepo interface {
// BindTag 建立一筆 tag 與其他資料(例如專案)的綁定關係
BindTag(ctx context.Context, binding *entity.TagsBindingTable) error
// UnbindTag 刪除一筆綁定資料
UnbindTag(ctx context.Context, tagID, referenceID string) error
// GetBindingsByReference 根據參照 ID 取得所有綁定資料
GetBindingsByReference(ctx context.Context, referenceID string) ([]*entity.TagsBindingTable, error)
// ListTagBinding 根據查詢條件取得 tag binding 的資料列表
// 回傳值分別為資料列表與符合條件的總筆數
ListTagBinding(ctx context.Context, params TagBindingQueryParams) ([]*entity.TagsBindingTable, int64, error)
}
// TagBindingQueryParams 為查詢 TagBindingTable 時的參數結構
type TagBindingQueryParams struct {
ReferenceID *string // 過濾參照 ID
TagID *string // 過濾 Tag ID
// 可根據需求增加其他查詢條件,如分頁、排序等
PageSize int64
PageIndex int64
}
type Index interface {
IndexTags20250317001UP(ctx context.Context) (*mongo.Cursor, error)
IndexTagsBinding20250317001UP(ctx context.Context) (*mongo.Cursor, error)
} }

View File

@ -0,0 +1,181 @@
package repository
import (
"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"
"context"
"errors"
"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"
"time"
)
type TagsRepositoryParam struct {
Conf *mgo.Conf
CacheConf cache.CacheConf
DBOpts []mon.Option
CacheOpts []cache.Option
}
type TagsRepository struct {
Tags mgo.DocumentDBWithCacheUseCase
TageBinding mgo.DocumentDBWithCacheUseCase
}
func NewTagsRepository(param TagsRepositoryParam) repository.TagRepo {
tags := entity.Tags{}
tagsDB, err := mgo.MustDocumentDBWithCache(
param.Conf,
tags.CollectionName(),
param.CacheConf,
param.DBOpts,
param.CacheOpts,
)
if err != nil {
panic(err)
}
tagBinding := entity.TagsBindingTable{}
bindingsDB, err := mgo.MustDocumentDBWithCache(
param.Conf,
tagBinding.CollectionName(),
param.CacheConf,
param.DBOpts,
param.CacheOpts,
)
if err != nil {
panic(err)
}
return &TagsRepository{
Tags: tagsDB,
TageBinding: bindingsDB,
}
}
func (repo *TagsRepository) Create(ctx context.Context, data *entity.Tags) error {
if data.ID.IsZero() {
now := time.Now().UTC().UnixNano()
data.ID = primitive.NewObjectID()
data.CreatedAt = now
data.UpdatedAt = now
}
rk := domain.GetTagsRK(data.ID.Hex())
_, err := repo.Tags.InsertOne(ctx, rk, data)
return err
}
func (repo *TagsRepository) GetByID(ctx context.Context, id string) (*entity.Tags, error) {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
var result *entity.Tags
err = repo.Tags.FindOne(ctx, domain.GetTagsRK(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 *TagsRepository) Update(ctx context.Context, id string, data repository.TagModifyParams) error {
now := time.Now().UTC().UnixNano()
// 動態構建更新內容
updateFields := bson.M{
"updated_at": now, // 確保 `updateAt` 總是更新
}
if data.Name != nil {
updateFields["name"] = *data.Name
}
if data.Types != nil {
updateFields["types"] = *data.Types
}
if data.ShowType != nil {
updateFields["show_type"] = *data.ShowType
}
if data.Cover != nil {
updateFields["cover"] = *data.Cover
}
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
return ErrInvalidObjectID
}
// 執行更新
rk := domain.GetTagsRK(id)
_, err = repo.Tags.UpdateOne(ctx, rk, bson.M{"_id": oid}, bson.M{"$set": updateFields})
if err != nil {
return err
}
return err
}
func (repo *TagsRepository) Delete(ctx context.Context, id string) error {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
return ErrInvalidObjectID
}
filter := bson.M{"_id": oid}
_, err = repo.Tags.DeleteOne(ctx, domain.GetTagsRK(id), filter)
if err != nil {
return err
}
return nil
}
func (repo *TagsRepository) List(ctx context.Context, params repository.TagQueryParams) ([]*entity.Tags, int64, error) {
// 構建查詢過濾器
filter := bson.M{}
if params.Name != nil {
filter["name"] = *params.Name
}
if params.Types != nil {
filter["types"] = *params.Types
}
if params.ShowType != nil {
filter["show_type"] = *params.ShowType
}
// 設置排序選項
opts := options.Find().SetSkip((params.PageIndex - 1) * params.PageSize).SetLimit(params.PageSize)
opts.SetSort(bson.D{{Key: "updated_at", Value: -1}})
// 查詢符合條件的總數
count, err := repo.Tags.GetClient().CountDocuments(ctx, filter)
if err != nil {
return nil, 0, err
}
// 執行查詢並獲取結果
var tags []*entity.Tags
err = repo.Tags.GetClient().Find(ctx, &tags, filter, opts)
if err != nil {
return nil, 0, err
}
return tags, count, nil
}
func (repo *TagsRepository) IndexTags20250317001UP(ctx context.Context) (*mongo.Cursor, error) {
// 等價於 db.account.createIndex({"create_at": 1})
repo.Tags.PopulateIndex(ctx, "show_type", 1, false)
repo.Tags.PopulateIndex(ctx, "types", 1, false)
repo.Tags.PopulateIndex(ctx, "name", 1, false)
return repo.Tags.GetClient().Indexes().List(ctx)
}

View File

@ -0,0 +1,81 @@
package repository
import (
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository"
"context"
"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"
"time"
)
func (repo *TagsRepository) BindTag(ctx context.Context, data *entity.TagsBindingTable) error {
if data.ID.IsZero() {
now := time.Now().UTC().UnixNano()
data.ID = primitive.NewObjectID()
data.CreatedAt = now
data.UpdatedAt = now
}
_, err := repo.TageBinding.GetClient().InsertOne(ctx, data)
return err
}
func (repo *TagsRepository) UnbindTag(ctx context.Context, tagID, referenceID string) error {
filter := bson.M{"tag_id": tagID, "reference_id": referenceID}
_, err := repo.TageBinding.GetClient().DeleteOne(ctx, filter)
if err != nil {
return err
}
return nil
}
func (repo *TagsRepository) GetBindingsByReference(ctx context.Context, referenceID string) ([]*entity.TagsBindingTable, error) {
var result []*entity.TagsBindingTable
filter := bson.M{"reference_id": referenceID}
err := repo.TageBinding.GetClient().Find(ctx, &result, filter)
if err != nil {
return nil, err
}
return result, nil
}
func (repo *TagsRepository) ListTagBinding(ctx context.Context, params repository.TagBindingQueryParams) ([]*entity.TagsBindingTable, int64, error) {
// 構建查詢過濾器
filter := bson.M{}
if params.ReferenceID != nil {
filter["reference_id"] = *params.ReferenceID
}
if params.TagID != nil {
filter["tag_id"] = *params.TagID
}
// 設置排序選項
opts := options.Find().SetSkip((params.PageIndex - 1) * params.PageSize).SetLimit(params.PageSize)
opts.SetSort(bson.D{{Key: "updated_at", Value: -1}})
// 查詢符合條件的總數
count, err := repo.TageBinding.GetClient().CountDocuments(ctx, filter)
if err != nil {
return nil, 0, err
}
// 執行查詢並獲取結果
var tags []*entity.TagsBindingTable
err = repo.TageBinding.GetClient().Find(ctx, &tags, filter, opts)
if err != nil {
return nil, 0, err
}
return tags, count, nil
}
func (repo *TagsRepository) IndexTagsBinding20250317001UP(ctx context.Context) (*mongo.Cursor, error) {
//TODO implement me
panic("implement me")
}