feat: product tags
This commit is contained in:
parent
fdc0799fcc
commit
2451bc4257
|
@ -5,12 +5,16 @@ import (
|
|||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ProductTags struct {
|
||||
type Tags struct {
|
||||
ID primitive.ObjectID `bson:"_id,omitempty"` // 專案 ID
|
||||
Types product.ItemType `bson:"types"` // Tag 類型
|
||||
Name string `bson:"name"`
|
||||
Name string `bson:"name"` // tag 名稱
|
||||
ShowType product.ShowType `bson:"show_type"` // 顯示筐
|
||||
Cover *string `bson:"cover,omitempty"` // 封面圖片
|
||||
UpdatedAt int64 `bson:"updated_at" json:"updated_at"` // 更新時間
|
||||
CreatedAt int64 `bson:"created_at" json:"created_at"` // 創建時間
|
||||
}
|
||||
|
||||
func (p *Tags) CollectionName() string {
|
||||
return "tags"
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ const (
|
|||
GetProductRedisKey RedisKey = "get"
|
||||
GetProductItemRedisKey RedisKey = "get_item"
|
||||
GetProductStatisticsRedisKey RedisKey = "statistics"
|
||||
GetTagsRedisKey RedisKey = "tags"
|
||||
)
|
||||
|
||||
func GetProductRK(id string) string {
|
||||
|
@ -31,3 +32,7 @@ func GetProductItemRK(id string) string {
|
|||
func GetProductStatisticsRK(id string) string {
|
||||
return GetProductStatisticsRedisKey.With(id).ToString()
|
||||
}
|
||||
|
||||
func GetTagsRK(id string) string {
|
||||
return GetTagsRedisKey.With(id).ToString()
|
||||
}
|
||||
|
|
|
@ -1,7 +1,73 @@
|
|||
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 {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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")
|
||||
}
|
Loading…
Reference in New Issue