feat: product tags
This commit is contained in:
parent
fdc0799fcc
commit
2451bc4257
|
@ -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"
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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