diff --git a/Makefile b/Makefile index 9e0e0da..402200e 100644 --- a/Makefile +++ b/Makefile @@ -35,5 +35,8 @@ build-docker: .PHONY: mock-gen mock-gen: # 建立 mock 資料 - #mockgen -source=./pkg/domain/repository/token.go -destination=./pkg/mock/repository/token.go -package=mock + mockgen -source=./pkg/domain/repository/product.go -destination=./pkg/mock/repository/product.go -package=mock + mockgen -source=./pkg/domain/repository/product_item.go -destination=./pkg/mock/repository/product_item.go -package=mock + mockgen -source=./pkg/domain/repository/product_statistics.go -destination=./pkg/mock/repository/product_statistics.go -package=mock + mockgen -source=./pkg/domain/repository/tags.go -destination=./pkg/mock/repository/tags.go -package=mock @echo "Generate mock files successfully" diff --git a/pkg/domain/entity/kyc.go b/pkg/domain/entity/kyc.go new file mode 100644 index 0000000..8d6131f --- /dev/null +++ b/pkg/domain/entity/kyc.go @@ -0,0 +1,31 @@ +package entity + +import "go.mongodb.org/mongo-driver/bson/primitive" + +type KYC struct { + ID primitive.ObjectID `bson:"_id,omitempty"` + UID string `bson:"uid"` // 驗證人 UID + CountryRegion string `bson:"country_region"` // 地區(例如 "TW", "JP", "US"...) + Name string `bson:"name"` // 真實姓名 + Identification string `bson:"identification"` // 身分證字號 or 護照號碼 + IdentificationType string `bson:"identification_type"` // ID 類型:ID_CARD, PASSPORT, RESIDENT_CERT + Address string `bson:"address"` // 戶籍地址(或居住地址) + PostalCode string `bson:"postal_code"` // 郵遞區號(海外使用) + // 上傳文件網址(可為 object storage 的 URL) + IDFrontImage string `bson:"id_front_image"` // 身分證/護照 正面 + IDBackImage string `bson:"id_back_image"` // 身分證/居留證 反面 + BankStatementImg string `bson:"bank_statement_img"` // 銀行存摺封面照 + BankCode string `bson:"bank_code"` // 銀行代碼(可為 SWIFT) + BankName string `bson:"bank_name"` // 銀行名稱(顯示用) + BranchCode string `bson:"branch_code"` // 分行代碼 + BranchName string `bson:"branch_name"` // 分行名稱(顯示用) + BankAccount string `bson:"bank_account"` // 銀行帳號 + Status string `bson:"status"` // 審核狀態:PENDING, APPROVED, REJECTED + RejectReason string `bson:"reject_reason"` // 若被駁回,原因描述 + UpdatedAt int64 `bson:"updated_at,omitempty"` + CreatedAt int64 `bson:"created_at,omitempty"` +} + +func (p *KYC) CollectionName() string { + return "kyc" +} diff --git a/pkg/domain/repository/kyc.go b/pkg/domain/repository/kyc.go new file mode 100644 index 0000000..2b1ed70 --- /dev/null +++ b/pkg/domain/repository/kyc.go @@ -0,0 +1,50 @@ +package repository + +import ( + "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity" + "context" +) + +// KYC(Know Your Customer) + +type KYCRepository interface { + // Create 建立 KYC 資料 + Create(ctx context.Context, kyc *entity.KYC) error + // FindLatestByUID 根據使用者 UID 取得最新 KYC 紀錄 + FindLatestByUID(ctx context.Context, uid string) (*entity.KYC, error) + // FindByID 根據 KYC ID 查詢 + FindByID(ctx context.Context, id string) (*entity.KYC, error) + // List 分頁查詢(後台審核列表用) + List(ctx context.Context, params KYCQueryParams) ([]*entity.KYC, int64, error) + // UpdateStatus 更新 KYC 狀態與審核原因(審核用) + UpdateStatus(ctx context.Context, id string, status string, reason string) error + // UpdateKYCInfo 更新使用者的 KYC(限於尚未審核的) + UpdateKYCInfo(ctx context.Context, id string, update *KYCUpdateParams) error +} + +type KYCQueryParams struct { + UID *string + Country *string + Status *string // PENDING, APPROVED, REJECTED + PageSize int64 + PageIndex int64 + SortByDate bool // 是否依申請時間倒序 +} + +type KYCUpdateParams struct { + Name *string + Identification *string + IdentificationType *string + Address *string + PostalCode *string + DateOfBirth *string + Gender *string + IDFrontImage *string + IDBackImage *string + BankStatementImg *string + BankCode *string + BankName *string + BranchCode *string + BranchName *string + BankAccount *string +} diff --git a/pkg/domain/usecase/kyc.go b/pkg/domain/usecase/kyc.go new file mode 100644 index 0000000..aed2454 --- /dev/null +++ b/pkg/domain/usecase/kyc.go @@ -0,0 +1 @@ +package usecase diff --git a/pkg/domain/usecase/product_item.go b/pkg/domain/usecase/product_item.go new file mode 100644 index 0000000..aed2454 --- /dev/null +++ b/pkg/domain/usecase/product_item.go @@ -0,0 +1 @@ +package usecase diff --git a/pkg/domain/usecase/tags.go b/pkg/domain/usecase/tags.go new file mode 100644 index 0000000..aed2454 --- /dev/null +++ b/pkg/domain/usecase/tags.go @@ -0,0 +1 @@ +package usecase diff --git a/pkg/mock/repository/product.go b/pkg/mock/repository/product.go new file mode 100644 index 0000000..e9789e8 --- /dev/null +++ b/pkg/mock/repository/product.go @@ -0,0 +1,207 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./pkg/domain/repository/product.go +// +// Generated by this command: +// +// mockgen -source=./pkg/domain/repository/product.go -destination=./pkg/mock/repository/product.go -package=mock +// + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + entity "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity" + repository "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository" + mongo "go.mongodb.org/mongo-driver/mongo" + options "go.mongodb.org/mongo-driver/mongo/options" + gomock "go.uber.org/mock/gomock" +) + +// MockProductRepository is a mock of ProductRepository interface. +type MockProductRepository struct { + ctrl *gomock.Controller + recorder *MockProductRepositoryMockRecorder + isgomock struct{} +} + +// MockProductRepositoryMockRecorder is the mock recorder for MockProductRepository. +type MockProductRepositoryMockRecorder struct { + mock *MockProductRepository +} + +// NewMockProductRepository creates a new mock instance. +func NewMockProductRepository(ctrl *gomock.Controller) *MockProductRepository { + mock := &MockProductRepository{ctrl: ctrl} + mock.recorder = &MockProductRepositoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockProductRepository) EXPECT() *MockProductRepositoryMockRecorder { + return m.recorder +} + +// Delete mocks base method. +func (m *MockProductRepository) Delete(ctx context.Context, id string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockProductRepositoryMockRecorder) Delete(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockProductRepository)(nil).Delete), ctx, id) +} + +// FindOneByID mocks base method. +func (m *MockProductRepository) FindOneByID(ctx context.Context, id string) (*entity.Product, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindOneByID", ctx, id) + ret0, _ := ret[0].(*entity.Product) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindOneByID indicates an expected call of FindOneByID. +func (mr *MockProductRepositoryMockRecorder) FindOneByID(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOneByID", reflect.TypeOf((*MockProductRepository)(nil).FindOneByID), ctx, id) +} + +// FindOneBySlug mocks base method. +func (m *MockProductRepository) FindOneBySlug(ctx context.Context, slug string) (*entity.Product, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindOneBySlug", ctx, slug) + ret0, _ := ret[0].(*entity.Product) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindOneBySlug indicates an expected call of FindOneBySlug. +func (mr *MockProductRepositoryMockRecorder) FindOneBySlug(ctx, slug any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOneBySlug", reflect.TypeOf((*MockProductRepository)(nil).FindOneBySlug), ctx, slug) +} + +// Index20250317001UP mocks base method. +func (m *MockProductRepository) Index20250317001UP(ctx context.Context) (*mongo.Cursor, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Index20250317001UP", ctx) + ret0, _ := ret[0].(*mongo.Cursor) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Index20250317001UP indicates an expected call of Index20250317001UP. +func (mr *MockProductRepositoryMockRecorder) Index20250317001UP(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Index20250317001UP", reflect.TypeOf((*MockProductRepository)(nil).Index20250317001UP), ctx) +} + +// Insert mocks base method. +func (m *MockProductRepository) Insert(ctx context.Context, data *entity.Product) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Insert", ctx, data) + ret0, _ := ret[0].(error) + return ret0 +} + +// Insert indicates an expected call of Insert. +func (mr *MockProductRepositoryMockRecorder) Insert(ctx, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockProductRepository)(nil).Insert), ctx, data) +} + +// ListProduct mocks base method. +func (m *MockProductRepository) ListProduct(ctx context.Context, params *repository.ProductQueryParams) ([]*entity.Product, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListProduct", ctx, params) + ret0, _ := ret[0].([]*entity.Product) + ret1, _ := ret[1].(int64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ListProduct indicates an expected call of ListProduct. +func (mr *MockProductRepositoryMockRecorder) ListProduct(ctx, params any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProduct", reflect.TypeOf((*MockProductRepository)(nil).ListProduct), ctx, params) +} + +// Transaction mocks base method. +func (m *MockProductRepository) Transaction(ctx context.Context, fn func(mongo.SessionContext) (any, error), opts ...*options.TransactionOptions) error { + m.ctrl.T.Helper() + varargs := []any{ctx, fn} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Transaction", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Transaction indicates an expected call of Transaction. +func (mr *MockProductRepositoryMockRecorder) Transaction(ctx, fn any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, fn}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transaction", reflect.TypeOf((*MockProductRepository)(nil).Transaction), varargs...) +} + +// Update mocks base method. +func (m *MockProductRepository) Update(ctx context.Context, productID string, data *repository.ProductUpdateParams) (*mongo.UpdateResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, productID, data) + ret0, _ := ret[0].(*mongo.UpdateResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockProductRepositoryMockRecorder) Update(ctx, productID, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockProductRepository)(nil).Update), ctx, productID, data) +} + +// MockProductIndex is a mock of ProductIndex interface. +type MockProductIndex struct { + ctrl *gomock.Controller + recorder *MockProductIndexMockRecorder + isgomock struct{} +} + +// MockProductIndexMockRecorder is the mock recorder for MockProductIndex. +type MockProductIndexMockRecorder struct { + mock *MockProductIndex +} + +// NewMockProductIndex creates a new mock instance. +func NewMockProductIndex(ctrl *gomock.Controller) *MockProductIndex { + mock := &MockProductIndex{ctrl: ctrl} + mock.recorder = &MockProductIndexMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockProductIndex) EXPECT() *MockProductIndexMockRecorder { + return m.recorder +} + +// Index20250317001UP mocks base method. +func (m *MockProductIndex) Index20250317001UP(ctx context.Context) (*mongo.Cursor, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Index20250317001UP", ctx) + ret0, _ := ret[0].(*mongo.Cursor) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Index20250317001UP indicates an expected call of Index20250317001UP. +func (mr *MockProductIndexMockRecorder) Index20250317001UP(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Index20250317001UP", reflect.TypeOf((*MockProductIndex)(nil).Index20250317001UP), ctx) +} diff --git a/pkg/mock/repository/product_item.go b/pkg/mock/repository/product_item.go new file mode 100644 index 0000000..f86858a --- /dev/null +++ b/pkg/mock/repository/product_item.go @@ -0,0 +1,435 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./pkg/domain/repository/product_item.go +// +// Generated by this command: +// +// mockgen -source=./pkg/domain/repository/product_item.go -destination=./pkg/mock/repository/product_item.go -package=mock +// + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + entity "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity" + product "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/product" + repository "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository" + mongo "go.mongodb.org/mongo-driver/mongo" + gomock "go.uber.org/mock/gomock" +) + +// MockProductItemRepository is a mock of ProductItemRepository interface. +type MockProductItemRepository struct { + ctrl *gomock.Controller + recorder *MockProductItemRepositoryMockRecorder + isgomock struct{} +} + +// MockProductItemRepositoryMockRecorder is the mock recorder for MockProductItemRepository. +type MockProductItemRepositoryMockRecorder struct { + mock *MockProductItemRepository +} + +// NewMockProductItemRepository creates a new mock instance. +func NewMockProductItemRepository(ctrl *gomock.Controller) *MockProductItemRepository { + mock := &MockProductItemRepository{ctrl: ctrl} + mock.recorder = &MockProductItemRepositoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockProductItemRepository) EXPECT() *MockProductItemRepositoryMockRecorder { + return m.recorder +} + +// DecSalesCount mocks base method. +func (m *MockProductItemRepository) DecSalesCount(ctx context.Context, id string, count int64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DecSalesCount", ctx, id, count) + ret0, _ := ret[0].(error) + return ret0 +} + +// DecSalesCount indicates an expected call of DecSalesCount. +func (mr *MockProductItemRepositoryMockRecorder) DecSalesCount(ctx, id, count any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecSalesCount", reflect.TypeOf((*MockProductItemRepository)(nil).DecSalesCount), ctx, id, count) +} + +// Delete mocks base method. +func (m *MockProductItemRepository) Delete(ctx context.Context, ids []string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, ids) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockProductItemRepositoryMockRecorder) Delete(ctx, ids any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockProductItemRepository)(nil).Delete), ctx, ids) +} + +// DeleteByReferenceID mocks base method. +func (m *MockProductItemRepository) DeleteByReferenceID(ctx context.Context, id string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteByReferenceID", ctx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteByReferenceID indicates an expected call of DeleteByReferenceID. +func (mr *MockProductItemRepositoryMockRecorder) DeleteByReferenceID(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteByReferenceID", reflect.TypeOf((*MockProductItemRepository)(nil).DeleteByReferenceID), ctx, id) +} + +// FindByID mocks base method. +func (m *MockProductItemRepository) FindByID(ctx context.Context, id string) (*entity.ProductItems, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindByID", ctx, id) + ret0, _ := ret[0].(*entity.ProductItems) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindByID indicates an expected call of FindByID. +func (mr *MockProductItemRepositoryMockRecorder) FindByID(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindByID", reflect.TypeOf((*MockProductItemRepository)(nil).FindByID), ctx, id) +} + +// GetSalesCount mocks base method. +func (m *MockProductItemRepository) GetSalesCount(ctx context.Context, ids []string) ([]repository.ProductItemSalesCount, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSalesCount", ctx, ids) + ret0, _ := ret[0].([]repository.ProductItemSalesCount) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSalesCount indicates an expected call of GetSalesCount. +func (mr *MockProductItemRepositoryMockRecorder) GetSalesCount(ctx, ids any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSalesCount", reflect.TypeOf((*MockProductItemRepository)(nil).GetSalesCount), ctx, ids) +} + +// IncSalesCount mocks base method. +func (m *MockProductItemRepository) IncSalesCount(ctx context.Context, id string, count int64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IncSalesCount", ctx, id, count) + ret0, _ := ret[0].(error) + return ret0 +} + +// IncSalesCount indicates an expected call of IncSalesCount. +func (mr *MockProductItemRepositoryMockRecorder) IncSalesCount(ctx, id, count any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncSalesCount", reflect.TypeOf((*MockProductItemRepository)(nil).IncSalesCount), ctx, id, count) +} + +// Index20250317001UP mocks base method. +func (m *MockProductItemRepository) Index20250317001UP(ctx context.Context) (*mongo.Cursor, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Index20250317001UP", ctx) + ret0, _ := ret[0].(*mongo.Cursor) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Index20250317001UP indicates an expected call of Index20250317001UP. +func (mr *MockProductItemRepositoryMockRecorder) Index20250317001UP(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Index20250317001UP", reflect.TypeOf((*MockProductItemRepository)(nil).Index20250317001UP), ctx) +} + +// Insert mocks base method. +func (m *MockProductItemRepository) Insert(ctx context.Context, item []entity.ProductItems) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Insert", ctx, item) + ret0, _ := ret[0].(error) + return ret0 +} + +// Insert indicates an expected call of Insert. +func (mr *MockProductItemRepositoryMockRecorder) Insert(ctx, item any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockProductItemRepository)(nil).Insert), ctx, item) +} + +// ListProductItem mocks base method. +func (m *MockProductItemRepository) ListProductItem(ctx context.Context, param repository.ProductItemQueryParams) ([]entity.ProductItems, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListProductItem", ctx, param) + ret0, _ := ret[0].([]entity.ProductItems) + ret1, _ := ret[1].(int64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ListProductItem indicates an expected call of ListProductItem. +func (mr *MockProductItemRepositoryMockRecorder) ListProductItem(ctx, param any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProductItem", reflect.TypeOf((*MockProductItemRepository)(nil).ListProductItem), ctx, param) +} + +// Update mocks base method. +func (m *MockProductItemRepository) Update(ctx context.Context, id string, param *repository.ProductUpdateItem) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, id, param) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update. +func (mr *MockProductItemRepositoryMockRecorder) Update(ctx, id, param any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockProductItemRepository)(nil).Update), ctx, id, param) +} + +// UpdateStatus mocks base method. +func (m *MockProductItemRepository) UpdateStatus(ctx context.Context, id string, status product.ItemStatus) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateStatus", ctx, id, status) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateStatus indicates an expected call of UpdateStatus. +func (mr *MockProductItemRepositoryMockRecorder) UpdateStatus(ctx, id, status any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStatus", reflect.TypeOf((*MockProductItemRepository)(nil).UpdateStatus), ctx, id, status) +} + +// MockProductItemIndex is a mock of ProductItemIndex interface. +type MockProductItemIndex struct { + ctrl *gomock.Controller + recorder *MockProductItemIndexMockRecorder + isgomock struct{} +} + +// MockProductItemIndexMockRecorder is the mock recorder for MockProductItemIndex. +type MockProductItemIndexMockRecorder struct { + mock *MockProductItemIndex +} + +// NewMockProductItemIndex creates a new mock instance. +func NewMockProductItemIndex(ctrl *gomock.Controller) *MockProductItemIndex { + mock := &MockProductItemIndex{ctrl: ctrl} + mock.recorder = &MockProductItemIndexMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockProductItemIndex) EXPECT() *MockProductItemIndexMockRecorder { + return m.recorder +} + +// Index20250317001UP mocks base method. +func (m *MockProductItemIndex) Index20250317001UP(ctx context.Context) (*mongo.Cursor, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Index20250317001UP", ctx) + ret0, _ := ret[0].(*mongo.Cursor) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Index20250317001UP indicates an expected call of Index20250317001UP. +func (mr *MockProductItemIndexMockRecorder) Index20250317001UP(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Index20250317001UP", reflect.TypeOf((*MockProductItemIndex)(nil).Index20250317001UP), ctx) +} + +// MockProductItemBasic is a mock of ProductItemBasic interface. +type MockProductItemBasic struct { + ctrl *gomock.Controller + recorder *MockProductItemBasicMockRecorder + isgomock struct{} +} + +// MockProductItemBasicMockRecorder is the mock recorder for MockProductItemBasic. +type MockProductItemBasicMockRecorder struct { + mock *MockProductItemBasic +} + +// NewMockProductItemBasic creates a new mock instance. +func NewMockProductItemBasic(ctrl *gomock.Controller) *MockProductItemBasic { + mock := &MockProductItemBasic{ctrl: ctrl} + mock.recorder = &MockProductItemBasicMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockProductItemBasic) EXPECT() *MockProductItemBasicMockRecorder { + return m.recorder +} + +// Delete mocks base method. +func (m *MockProductItemBasic) Delete(ctx context.Context, ids []string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, ids) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockProductItemBasicMockRecorder) Delete(ctx, ids any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockProductItemBasic)(nil).Delete), ctx, ids) +} + +// DeleteByReferenceID mocks base method. +func (m *MockProductItemBasic) DeleteByReferenceID(ctx context.Context, id string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteByReferenceID", ctx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteByReferenceID indicates an expected call of DeleteByReferenceID. +func (mr *MockProductItemBasicMockRecorder) DeleteByReferenceID(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteByReferenceID", reflect.TypeOf((*MockProductItemBasic)(nil).DeleteByReferenceID), ctx, id) +} + +// FindByID mocks base method. +func (m *MockProductItemBasic) FindByID(ctx context.Context, id string) (*entity.ProductItems, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindByID", ctx, id) + ret0, _ := ret[0].(*entity.ProductItems) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindByID indicates an expected call of FindByID. +func (mr *MockProductItemBasicMockRecorder) FindByID(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindByID", reflect.TypeOf((*MockProductItemBasic)(nil).FindByID), ctx, id) +} + +// Insert mocks base method. +func (m *MockProductItemBasic) Insert(ctx context.Context, item []entity.ProductItems) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Insert", ctx, item) + ret0, _ := ret[0].(error) + return ret0 +} + +// Insert indicates an expected call of Insert. +func (mr *MockProductItemBasicMockRecorder) Insert(ctx, item any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockProductItemBasic)(nil).Insert), ctx, item) +} + +// ListProductItem mocks base method. +func (m *MockProductItemBasic) ListProductItem(ctx context.Context, param repository.ProductItemQueryParams) ([]entity.ProductItems, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListProductItem", ctx, param) + ret0, _ := ret[0].([]entity.ProductItems) + ret1, _ := ret[1].(int64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ListProductItem indicates an expected call of ListProductItem. +func (mr *MockProductItemBasicMockRecorder) ListProductItem(ctx, param any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProductItem", reflect.TypeOf((*MockProductItemBasic)(nil).ListProductItem), ctx, param) +} + +// Update mocks base method. +func (m *MockProductItemBasic) Update(ctx context.Context, id string, param *repository.ProductUpdateItem) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, id, param) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update. +func (mr *MockProductItemBasicMockRecorder) Update(ctx, id, param any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockProductItemBasic)(nil).Update), ctx, id, param) +} + +// UpdateStatus mocks base method. +func (m *MockProductItemBasic) UpdateStatus(ctx context.Context, id string, status product.ItemStatus) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateStatus", ctx, id, status) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateStatus indicates an expected call of UpdateStatus. +func (mr *MockProductItemBasicMockRecorder) UpdateStatus(ctx, id, status any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStatus", reflect.TypeOf((*MockProductItemBasic)(nil).UpdateStatus), ctx, id, status) +} + +// MockProductItemStatistics is a mock of ProductItemStatistics interface. +type MockProductItemStatistics struct { + ctrl *gomock.Controller + recorder *MockProductItemStatisticsMockRecorder + isgomock struct{} +} + +// MockProductItemStatisticsMockRecorder is the mock recorder for MockProductItemStatistics. +type MockProductItemStatisticsMockRecorder struct { + mock *MockProductItemStatistics +} + +// NewMockProductItemStatistics creates a new mock instance. +func NewMockProductItemStatistics(ctrl *gomock.Controller) *MockProductItemStatistics { + mock := &MockProductItemStatistics{ctrl: ctrl} + mock.recorder = &MockProductItemStatisticsMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockProductItemStatistics) EXPECT() *MockProductItemStatisticsMockRecorder { + return m.recorder +} + +// DecSalesCount mocks base method. +func (m *MockProductItemStatistics) DecSalesCount(ctx context.Context, id string, count int64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DecSalesCount", ctx, id, count) + ret0, _ := ret[0].(error) + return ret0 +} + +// DecSalesCount indicates an expected call of DecSalesCount. +func (mr *MockProductItemStatisticsMockRecorder) DecSalesCount(ctx, id, count any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecSalesCount", reflect.TypeOf((*MockProductItemStatistics)(nil).DecSalesCount), ctx, id, count) +} + +// GetSalesCount mocks base method. +func (m *MockProductItemStatistics) GetSalesCount(ctx context.Context, ids []string) ([]repository.ProductItemSalesCount, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSalesCount", ctx, ids) + ret0, _ := ret[0].([]repository.ProductItemSalesCount) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSalesCount indicates an expected call of GetSalesCount. +func (mr *MockProductItemStatisticsMockRecorder) GetSalesCount(ctx, ids any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSalesCount", reflect.TypeOf((*MockProductItemStatistics)(nil).GetSalesCount), ctx, ids) +} + +// IncSalesCount mocks base method. +func (m *MockProductItemStatistics) IncSalesCount(ctx context.Context, id string, count int64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IncSalesCount", ctx, id, count) + ret0, _ := ret[0].(error) + return ret0 +} + +// IncSalesCount indicates an expected call of IncSalesCount. +func (mr *MockProductItemStatisticsMockRecorder) IncSalesCount(ctx, id, count any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncSalesCount", reflect.TypeOf((*MockProductItemStatistics)(nil).IncSalesCount), ctx, id, count) +} diff --git a/pkg/mock/repository/product_statistics.go b/pkg/mock/repository/product_statistics.go new file mode 100644 index 0000000..57932dc --- /dev/null +++ b/pkg/mock/repository/product_statistics.go @@ -0,0 +1,225 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./pkg/domain/repository/product_statistics.go +// +// Generated by this command: +// +// mockgen -source=./pkg/domain/repository/product_statistics.go -destination=./pkg/mock/repository/product_statistics.go -package=mock +// + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + entity "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity" + mongo "go.mongodb.org/mongo-driver/mongo" + gomock "go.uber.org/mock/gomock" +) + +// MockProductStatisticsRepo is a mock of ProductStatisticsRepo interface. +type MockProductStatisticsRepo struct { + ctrl *gomock.Controller + recorder *MockProductStatisticsRepoMockRecorder + isgomock struct{} +} + +// MockProductStatisticsRepoMockRecorder is the mock recorder for MockProductStatisticsRepo. +type MockProductStatisticsRepoMockRecorder struct { + mock *MockProductStatisticsRepo +} + +// NewMockProductStatisticsRepo creates a new mock instance. +func NewMockProductStatisticsRepo(ctrl *gomock.Controller) *MockProductStatisticsRepo { + mock := &MockProductStatisticsRepo{ctrl: ctrl} + mock.recorder = &MockProductStatisticsRepoMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockProductStatisticsRepo) EXPECT() *MockProductStatisticsRepoMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockProductStatisticsRepo) Create(ctx context.Context, stats *entity.ProductStatistics) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, stats) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockProductStatisticsRepoMockRecorder) Create(ctx, stats any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockProductStatisticsRepo)(nil).Create), ctx, stats) +} + +// DecFansCount mocks base method. +func (m *MockProductStatisticsRepo) DecFansCount(ctx context.Context, productID string, fansCount uint64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DecFansCount", ctx, productID, fansCount) + ret0, _ := ret[0].(error) + return ret0 +} + +// DecFansCount indicates an expected call of DecFansCount. +func (mr *MockProductStatisticsRepoMockRecorder) DecFansCount(ctx, productID, fansCount any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecFansCount", reflect.TypeOf((*MockProductStatisticsRepo)(nil).DecFansCount), ctx, productID, fansCount) +} + +// DecOrders mocks base method. +func (m *MockProductStatisticsRepo) DecOrders(ctx context.Context, productID string, count int64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DecOrders", ctx, productID, count) + ret0, _ := ret[0].(error) + return ret0 +} + +// DecOrders indicates an expected call of DecOrders. +func (mr *MockProductStatisticsRepoMockRecorder) DecOrders(ctx, productID, count any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecOrders", reflect.TypeOf((*MockProductStatisticsRepo)(nil).DecOrders), ctx, productID, count) +} + +// Delete mocks base method. +func (m *MockProductStatisticsRepo) Delete(ctx context.Context, id string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockProductStatisticsRepoMockRecorder) Delete(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockProductStatisticsRepo)(nil).Delete), ctx, id) +} + +// GetByID mocks base method. +func (m *MockProductStatisticsRepo) GetByID(ctx context.Context, id string) (*entity.ProductStatistics, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetByID", ctx, id) + ret0, _ := ret[0].(*entity.ProductStatistics) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetByID indicates an expected call of GetByID. +func (mr *MockProductStatisticsRepoMockRecorder) GetByID(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByID", reflect.TypeOf((*MockProductStatisticsRepo)(nil).GetByID), ctx, id) +} + +// GetByProductID mocks base method. +func (m *MockProductStatisticsRepo) GetByProductID(ctx context.Context, productID string) (*entity.ProductStatistics, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetByProductID", ctx, productID) + ret0, _ := ret[0].(*entity.ProductStatistics) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetByProductID indicates an expected call of GetByProductID. +func (mr *MockProductStatisticsRepoMockRecorder) GetByProductID(ctx, productID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByProductID", reflect.TypeOf((*MockProductStatisticsRepo)(nil).GetByProductID), ctx, productID) +} + +// IncFansCount mocks base method. +func (m *MockProductStatisticsRepo) IncFansCount(ctx context.Context, productID string, fansCount uint64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IncFansCount", ctx, productID, fansCount) + ret0, _ := ret[0].(error) + return ret0 +} + +// IncFansCount indicates an expected call of IncFansCount. +func (mr *MockProductStatisticsRepoMockRecorder) IncFansCount(ctx, productID, fansCount any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncFansCount", reflect.TypeOf((*MockProductStatisticsRepo)(nil).IncFansCount), ctx, productID, fansCount) +} + +// IncOrders mocks base method. +func (m *MockProductStatisticsRepo) IncOrders(ctx context.Context, productID string, count int64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IncOrders", ctx, productID, count) + ret0, _ := ret[0].(error) + return ret0 +} + +// IncOrders indicates an expected call of IncOrders. +func (mr *MockProductStatisticsRepoMockRecorder) IncOrders(ctx, productID, count any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncOrders", reflect.TypeOf((*MockProductStatisticsRepo)(nil).IncOrders), ctx, productID, count) +} + +// Index20250317001UP mocks base method. +func (m *MockProductStatisticsRepo) Index20250317001UP(ctx context.Context) (*mongo.Cursor, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Index20250317001UP", ctx) + ret0, _ := ret[0].(*mongo.Cursor) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Index20250317001UP indicates an expected call of Index20250317001UP. +func (mr *MockProductStatisticsRepoMockRecorder) Index20250317001UP(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Index20250317001UP", reflect.TypeOf((*MockProductStatisticsRepo)(nil).Index20250317001UP), ctx) +} + +// UpdateAverageRating mocks base method. +func (m *MockProductStatisticsRepo) UpdateAverageRating(ctx context.Context, productID string, averageRating float64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateAverageRating", ctx, productID, averageRating) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateAverageRating indicates an expected call of UpdateAverageRating. +func (mr *MockProductStatisticsRepoMockRecorder) UpdateAverageRating(ctx, productID, averageRating any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAverageRating", reflect.TypeOf((*MockProductStatisticsRepo)(nil).UpdateAverageRating), ctx, productID, averageRating) +} + +// MockProductStatisticsIndex is a mock of ProductStatisticsIndex interface. +type MockProductStatisticsIndex struct { + ctrl *gomock.Controller + recorder *MockProductStatisticsIndexMockRecorder + isgomock struct{} +} + +// MockProductStatisticsIndexMockRecorder is the mock recorder for MockProductStatisticsIndex. +type MockProductStatisticsIndexMockRecorder struct { + mock *MockProductStatisticsIndex +} + +// NewMockProductStatisticsIndex creates a new mock instance. +func NewMockProductStatisticsIndex(ctrl *gomock.Controller) *MockProductStatisticsIndex { + mock := &MockProductStatisticsIndex{ctrl: ctrl} + mock.recorder = &MockProductStatisticsIndexMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockProductStatisticsIndex) EXPECT() *MockProductStatisticsIndexMockRecorder { + return m.recorder +} + +// Index20250317001UP mocks base method. +func (m *MockProductStatisticsIndex) Index20250317001UP(ctx context.Context) (*mongo.Cursor, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Index20250317001UP", ctx) + ret0, _ := ret[0].(*mongo.Cursor) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Index20250317001UP indicates an expected call of Index20250317001UP. +func (mr *MockProductStatisticsIndexMockRecorder) Index20250317001UP(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Index20250317001UP", reflect.TypeOf((*MockProductStatisticsIndex)(nil).Index20250317001UP), ctx) +} diff --git a/pkg/mock/repository/tags.go b/pkg/mock/repository/tags.go new file mode 100644 index 0000000..b544ac6 --- /dev/null +++ b/pkg/mock/repository/tags.go @@ -0,0 +1,498 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./pkg/domain/repository/tags.go +// +// Generated by this command: +// +// mockgen -source=./pkg/domain/repository/tags.go -destination=./pkg/mock/repository/tags.go -package=mock +// + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + entity "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity" + repository "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository" + mongo "go.mongodb.org/mongo-driver/mongo" + gomock "go.uber.org/mock/gomock" +) + +// MockTagRepo is a mock of TagRepo interface. +type MockTagRepo struct { + ctrl *gomock.Controller + recorder *MockTagRepoMockRecorder + isgomock struct{} +} + +// MockTagRepoMockRecorder is the mock recorder for MockTagRepo. +type MockTagRepoMockRecorder struct { + mock *MockTagRepo +} + +// NewMockTagRepo creates a new mock instance. +func NewMockTagRepo(ctrl *gomock.Controller) *MockTagRepo { + mock := &MockTagRepo{ctrl: ctrl} + mock.recorder = &MockTagRepoMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockTagRepo) EXPECT() *MockTagRepoMockRecorder { + return m.recorder +} + +// BindTags mocks base method. +func (m *MockTagRepo) BindTags(ctx context.Context, binding []*entity.TagsBindingTable) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BindTags", ctx, binding) + ret0, _ := ret[0].(error) + return ret0 +} + +// BindTags indicates an expected call of BindTags. +func (mr *MockTagRepoMockRecorder) BindTags(ctx, binding any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BindTags", reflect.TypeOf((*MockTagRepo)(nil).BindTags), ctx, binding) +} + +// Create mocks base method. +func (m *MockTagRepo) Create(ctx context.Context, tag *entity.Tags) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, tag) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockTagRepoMockRecorder) Create(ctx, tag any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockTagRepo)(nil).Create), ctx, tag) +} + +// Delete mocks base method. +func (m *MockTagRepo) Delete(ctx context.Context, id string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockTagRepoMockRecorder) Delete(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockTagRepo)(nil).Delete), ctx, id) +} + +// GetBindingsByReference mocks base method. +func (m *MockTagRepo) GetBindingsByReference(ctx context.Context, referenceID string) ([]*entity.TagsBindingTable, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBindingsByReference", ctx, referenceID) + ret0, _ := ret[0].([]*entity.TagsBindingTable) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBindingsByReference indicates an expected call of GetBindingsByReference. +func (mr *MockTagRepoMockRecorder) GetBindingsByReference(ctx, referenceID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBindingsByReference", reflect.TypeOf((*MockTagRepo)(nil).GetBindingsByReference), ctx, referenceID) +} + +// GetByID mocks base method. +func (m *MockTagRepo) GetByID(ctx context.Context, id string) (*entity.Tags, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetByID", ctx, id) + ret0, _ := ret[0].(*entity.Tags) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetByID indicates an expected call of GetByID. +func (mr *MockTagRepoMockRecorder) GetByID(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByID", reflect.TypeOf((*MockTagRepo)(nil).GetByID), ctx, id) +} + +// GetByIDs mocks base method. +func (m *MockTagRepo) GetByIDs(ctx context.Context, ids []string) ([]*entity.Tags, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetByIDs", ctx, ids) + ret0, _ := ret[0].([]*entity.Tags) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetByIDs indicates an expected call of GetByIDs. +func (mr *MockTagRepoMockRecorder) GetByIDs(ctx, ids any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByIDs", reflect.TypeOf((*MockTagRepo)(nil).GetByIDs), ctx, ids) +} + +// IndexTags20250317001UP mocks base method. +func (m *MockTagRepo) IndexTags20250317001UP(ctx context.Context) (*mongo.Cursor, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IndexTags20250317001UP", ctx) + ret0, _ := ret[0].(*mongo.Cursor) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IndexTags20250317001UP indicates an expected call of IndexTags20250317001UP. +func (mr *MockTagRepoMockRecorder) IndexTags20250317001UP(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexTags20250317001UP", reflect.TypeOf((*MockTagRepo)(nil).IndexTags20250317001UP), ctx) +} + +// IndexTagsBinding20250317001UP mocks base method. +func (m *MockTagRepo) IndexTagsBinding20250317001UP(ctx context.Context) (*mongo.Cursor, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IndexTagsBinding20250317001UP", ctx) + ret0, _ := ret[0].(*mongo.Cursor) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IndexTagsBinding20250317001UP indicates an expected call of IndexTagsBinding20250317001UP. +func (mr *MockTagRepoMockRecorder) IndexTagsBinding20250317001UP(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexTagsBinding20250317001UP", reflect.TypeOf((*MockTagRepo)(nil).IndexTagsBinding20250317001UP), ctx) +} + +// List mocks base method. +func (m *MockTagRepo) List(ctx context.Context, params repository.TagQueryParams) ([]*entity.Tags, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", ctx, params) + ret0, _ := ret[0].([]*entity.Tags) + ret1, _ := ret[1].(int64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// List indicates an expected call of List. +func (mr *MockTagRepoMockRecorder) List(ctx, params any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockTagRepo)(nil).List), ctx, params) +} + +// ListTagBinding mocks base method. +func (m *MockTagRepo) ListTagBinding(ctx context.Context, params repository.TagBindingQueryParams) ([]*entity.TagsBindingTable, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListTagBinding", ctx, params) + ret0, _ := ret[0].([]*entity.TagsBindingTable) + ret1, _ := ret[1].(int64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ListTagBinding indicates an expected call of ListTagBinding. +func (mr *MockTagRepoMockRecorder) ListTagBinding(ctx, params any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTagBinding", reflect.TypeOf((*MockTagRepo)(nil).ListTagBinding), ctx, params) +} + +// UnbindTag mocks base method. +func (m *MockTagRepo) UnbindTag(ctx context.Context, tagID, referenceID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnbindTag", ctx, tagID, referenceID) + ret0, _ := ret[0].(error) + return ret0 +} + +// UnbindTag indicates an expected call of UnbindTag. +func (mr *MockTagRepoMockRecorder) UnbindTag(ctx, tagID, referenceID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnbindTag", reflect.TypeOf((*MockTagRepo)(nil).UnbindTag), ctx, tagID, referenceID) +} + +// UnbindTagByReferenceID mocks base method. +func (m *MockTagRepo) UnbindTagByReferenceID(ctx context.Context, referenceID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnbindTagByReferenceID", ctx, referenceID) + ret0, _ := ret[0].(error) + return ret0 +} + +// UnbindTagByReferenceID indicates an expected call of UnbindTagByReferenceID. +func (mr *MockTagRepoMockRecorder) UnbindTagByReferenceID(ctx, referenceID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnbindTagByReferenceID", reflect.TypeOf((*MockTagRepo)(nil).UnbindTagByReferenceID), ctx, referenceID) +} + +// Update mocks base method. +func (m *MockTagRepo) Update(ctx context.Context, id string, tag repository.TagModifyParams) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, id, tag) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update. +func (mr *MockTagRepoMockRecorder) Update(ctx, id, tag any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockTagRepo)(nil).Update), ctx, id, tag) +} + +// MockBase is a mock of Base interface. +type MockBase struct { + ctrl *gomock.Controller + recorder *MockBaseMockRecorder + isgomock struct{} +} + +// MockBaseMockRecorder is the mock recorder for MockBase. +type MockBaseMockRecorder struct { + mock *MockBase +} + +// NewMockBase creates a new mock instance. +func NewMockBase(ctrl *gomock.Controller) *MockBase { + mock := &MockBase{ctrl: ctrl} + mock.recorder = &MockBaseMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBase) EXPECT() *MockBaseMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockBase) Create(ctx context.Context, tag *entity.Tags) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, tag) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockBaseMockRecorder) Create(ctx, tag any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockBase)(nil).Create), ctx, tag) +} + +// Delete mocks base method. +func (m *MockBase) Delete(ctx context.Context, id string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockBaseMockRecorder) Delete(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockBase)(nil).Delete), ctx, id) +} + +// GetByID mocks base method. +func (m *MockBase) GetByID(ctx context.Context, id string) (*entity.Tags, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetByID", ctx, id) + ret0, _ := ret[0].(*entity.Tags) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetByID indicates an expected call of GetByID. +func (mr *MockBaseMockRecorder) GetByID(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByID", reflect.TypeOf((*MockBase)(nil).GetByID), ctx, id) +} + +// GetByIDs mocks base method. +func (m *MockBase) GetByIDs(ctx context.Context, ids []string) ([]*entity.Tags, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetByIDs", ctx, ids) + ret0, _ := ret[0].([]*entity.Tags) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetByIDs indicates an expected call of GetByIDs. +func (mr *MockBaseMockRecorder) GetByIDs(ctx, ids any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByIDs", reflect.TypeOf((*MockBase)(nil).GetByIDs), ctx, ids) +} + +// List mocks base method. +func (m *MockBase) List(ctx context.Context, params repository.TagQueryParams) ([]*entity.Tags, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", ctx, params) + ret0, _ := ret[0].([]*entity.Tags) + ret1, _ := ret[1].(int64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// List indicates an expected call of List. +func (mr *MockBaseMockRecorder) List(ctx, params any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockBase)(nil).List), ctx, params) +} + +// Update mocks base method. +func (m *MockBase) Update(ctx context.Context, id string, tag repository.TagModifyParams) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, id, tag) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update. +func (mr *MockBaseMockRecorder) Update(ctx, id, tag any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockBase)(nil).Update), ctx, id, tag) +} + +// MockTagBindingRepo is a mock of TagBindingRepo interface. +type MockTagBindingRepo struct { + ctrl *gomock.Controller + recorder *MockTagBindingRepoMockRecorder + isgomock struct{} +} + +// MockTagBindingRepoMockRecorder is the mock recorder for MockTagBindingRepo. +type MockTagBindingRepoMockRecorder struct { + mock *MockTagBindingRepo +} + +// NewMockTagBindingRepo creates a new mock instance. +func NewMockTagBindingRepo(ctrl *gomock.Controller) *MockTagBindingRepo { + mock := &MockTagBindingRepo{ctrl: ctrl} + mock.recorder = &MockTagBindingRepoMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockTagBindingRepo) EXPECT() *MockTagBindingRepoMockRecorder { + return m.recorder +} + +// BindTags mocks base method. +func (m *MockTagBindingRepo) BindTags(ctx context.Context, binding []*entity.TagsBindingTable) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BindTags", ctx, binding) + ret0, _ := ret[0].(error) + return ret0 +} + +// BindTags indicates an expected call of BindTags. +func (mr *MockTagBindingRepoMockRecorder) BindTags(ctx, binding any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BindTags", reflect.TypeOf((*MockTagBindingRepo)(nil).BindTags), ctx, binding) +} + +// GetBindingsByReference mocks base method. +func (m *MockTagBindingRepo) GetBindingsByReference(ctx context.Context, referenceID string) ([]*entity.TagsBindingTable, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBindingsByReference", ctx, referenceID) + ret0, _ := ret[0].([]*entity.TagsBindingTable) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBindingsByReference indicates an expected call of GetBindingsByReference. +func (mr *MockTagBindingRepoMockRecorder) GetBindingsByReference(ctx, referenceID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBindingsByReference", reflect.TypeOf((*MockTagBindingRepo)(nil).GetBindingsByReference), ctx, referenceID) +} + +// ListTagBinding mocks base method. +func (m *MockTagBindingRepo) ListTagBinding(ctx context.Context, params repository.TagBindingQueryParams) ([]*entity.TagsBindingTable, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListTagBinding", ctx, params) + ret0, _ := ret[0].([]*entity.TagsBindingTable) + ret1, _ := ret[1].(int64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ListTagBinding indicates an expected call of ListTagBinding. +func (mr *MockTagBindingRepoMockRecorder) ListTagBinding(ctx, params any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTagBinding", reflect.TypeOf((*MockTagBindingRepo)(nil).ListTagBinding), ctx, params) +} + +// UnbindTag mocks base method. +func (m *MockTagBindingRepo) UnbindTag(ctx context.Context, tagID, referenceID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnbindTag", ctx, tagID, referenceID) + ret0, _ := ret[0].(error) + return ret0 +} + +// UnbindTag indicates an expected call of UnbindTag. +func (mr *MockTagBindingRepoMockRecorder) UnbindTag(ctx, tagID, referenceID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnbindTag", reflect.TypeOf((*MockTagBindingRepo)(nil).UnbindTag), ctx, tagID, referenceID) +} + +// UnbindTagByReferenceID mocks base method. +func (m *MockTagBindingRepo) UnbindTagByReferenceID(ctx context.Context, referenceID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnbindTagByReferenceID", ctx, referenceID) + ret0, _ := ret[0].(error) + return ret0 +} + +// UnbindTagByReferenceID indicates an expected call of UnbindTagByReferenceID. +func (mr *MockTagBindingRepoMockRecorder) UnbindTagByReferenceID(ctx, referenceID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnbindTagByReferenceID", reflect.TypeOf((*MockTagBindingRepo)(nil).UnbindTagByReferenceID), ctx, referenceID) +} + +// MockIndex is a mock of Index interface. +type MockIndex struct { + ctrl *gomock.Controller + recorder *MockIndexMockRecorder + isgomock struct{} +} + +// MockIndexMockRecorder is the mock recorder for MockIndex. +type MockIndexMockRecorder struct { + mock *MockIndex +} + +// NewMockIndex creates a new mock instance. +func NewMockIndex(ctrl *gomock.Controller) *MockIndex { + mock := &MockIndex{ctrl: ctrl} + mock.recorder = &MockIndexMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIndex) EXPECT() *MockIndexMockRecorder { + return m.recorder +} + +// IndexTags20250317001UP mocks base method. +func (m *MockIndex) IndexTags20250317001UP(ctx context.Context) (*mongo.Cursor, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IndexTags20250317001UP", ctx) + ret0, _ := ret[0].(*mongo.Cursor) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IndexTags20250317001UP indicates an expected call of IndexTags20250317001UP. +func (mr *MockIndexMockRecorder) IndexTags20250317001UP(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexTags20250317001UP", reflect.TypeOf((*MockIndex)(nil).IndexTags20250317001UP), ctx) +} + +// IndexTagsBinding20250317001UP mocks base method. +func (m *MockIndex) IndexTagsBinding20250317001UP(ctx context.Context) (*mongo.Cursor, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IndexTagsBinding20250317001UP", ctx) + ret0, _ := ret[0].(*mongo.Cursor) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IndexTagsBinding20250317001UP indicates an expected call of IndexTagsBinding20250317001UP. +func (mr *MockIndexMockRecorder) IndexTagsBinding20250317001UP(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexTagsBinding20250317001UP", reflect.TypeOf((*MockIndex)(nil).IndexTagsBinding20250317001UP), ctx) +} diff --git a/pkg/repository/kyc.go b/pkg/repository/kyc.go new file mode 100644 index 0000000..178a8cf --- /dev/null +++ b/pkg/repository/kyc.go @@ -0,0 +1,181 @@ +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" + mgo "code.30cm.net/digimon/library-go/mongo" + "context" + "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/options" + "time" +) + +type KYCRepositoryParam struct { + Conf *mgo.Conf + CacheConf cache.CacheConf + DBOpts []mon.Option + CacheOpts []cache.Option +} + +type KYCRepository struct { + DB mgo.DocumentDBWithCacheUseCase +} + +func NewKYCRepository(param KYCRepositoryParam) repository.KYCRepository { + e := entity.KYC{} + documentDB, err := mgo.MustDocumentDBWithCache( + param.Conf, + e.CollectionName(), + param.CacheConf, + param.DBOpts, + param.CacheOpts, + ) + if err != nil { + panic(err) + } + + return &KYCRepository{ + DB: documentDB, + } +} + +func (repo *KYCRepository) Create(ctx context.Context, data *entity.KYC) error { + if data.ID.IsZero() { + now := time.Now().UTC().UnixNano() + data.ID = primitive.NewObjectID() + data.CreatedAt = now + data.UpdatedAt = now + } + + _, err := repo.DB.GetClient().InsertOne(ctx, data) + + return err +} + +func (repo *KYCRepository) FindLatestByUID(ctx context.Context, uid string) (*entity.KYC, error) { + filter := bson.M{"uid": uid} + var result entity.KYC + opts := options.FindOne().SetSort(bson.D{{"created_at", -1}}) // 用 SetSort 加入排序 + + err := repo.DB.GetClient().FindOne(ctx, &result, filter, opts) + if err != nil { + return nil, err + } + return &result, nil +} + +func (repo *KYCRepository) FindByID(ctx context.Context, id string) (*entity.KYC, error) { + oid, err := primitive.ObjectIDFromHex(id) + if err != nil { + return nil, err + } + filter := bson.M{"_id": oid} + var result entity.KYC + err = repo.DB.GetClient().FindOne(ctx, &result, filter) + if err != nil { + return nil, err + } + return &result, nil +} + +func (repo *KYCRepository) List(ctx context.Context, params repository.KYCQueryParams) ([]*entity.KYC, int64, error) { + filter := bson.M{} + if params.UID != nil { + filter["uid"] = *params.UID + } + if params.Country != nil { + filter["country_region"] = *params.Country + } + if params.Status != nil { + filter["status"] = *params.Status + } + sort := bson.D{} + if params.SortByDate { + sort = append(sort, bson.E{Key: "created_at", Value: -1}) + } + + var results []*entity.KYC + total, err := repo.DB.GetClient().FindManyWithTotal(ctx, &results, filter, params.PageIndex, params.PageSize, WithSort(sort)) + if err != nil { + return nil, 0, err + } + return results, total, nil +} + +func (repo *KYCRepository) UpdateStatus(ctx context.Context, id string, status string, reason string) error { + oid, err := primitive.ObjectIDFromHex(id) + if err != nil { + return err + } + filter := bson.M{"_id": oid} + update := bson.M{ + "$set": bson.M{ + "status": status, + "reject_reason": reason, + "updated_at": time.Now().UTC().UnixNano(), + }, + } + _, err = repo.DB.GetClient().UpdateOne(ctx, filter, update) + return err +} + +func (repo *KYCRepository) UpdateKYCInfo(ctx context.Context, id string, update *repository.KYCUpdateParams) error { + oid, err := primitive.ObjectIDFromHex(id) + if err != nil { + return err + } + setFields := bson.M{"updated_at": time.Now().UTC().UnixNano()} + if update.Name != nil { + setFields["name"] = *update.Name + } + if update.Identification != nil { + setFields["identification"] = *update.Identification + } + if update.IdentificationType != nil { + setFields["identification_type"] = *update.IdentificationType + } + if update.Address != nil { + setFields["address"] = *update.Address + } + if update.PostalCode != nil { + setFields["postal_code"] = *update.PostalCode + } + if update.DateOfBirth != nil { + setFields["date_of_birth"] = *update.DateOfBirth + } + if update.Gender != nil { + setFields["gender"] = *update.Gender + } + if update.IDFrontImage != nil { + setFields["id_front_image"] = *update.IDFrontImage + } + if update.IDBackImage != nil { + setFields["id_back_image"] = *update.IDBackImage + } + if update.BankStatementImg != nil { + setFields["bank_statement_img"] = *update.BankStatementImg + } + if update.BankCode != nil { + setFields["bank_code"] = *update.BankCode + } + if update.BankName != nil { + setFields["bank_name"] = *update.BankName + } + if update.BranchCode != nil { + setFields["branch_code"] = *update.BranchCode + } + if update.BranchName != nil { + setFields["branch_name"] = *update.BranchName + } + if update.BankAccount != nil { + setFields["bank_account"] = *update.BankAccount + } + + filter := bson.M{"_id": oid, "status": "PENDING"} // 僅允許更新尚未審核的 + updateDoc := bson.M{"$set": setFields} + _, err = repo.DB.GetClient().UpdateOne(ctx, filter, updateDoc) + return err +} diff --git a/pkg/repository/product_statistics.go b/pkg/repository/product_statistics.go index 1793f2a..4052c7f 100644 --- a/pkg/repository/product_statistics.go +++ b/pkg/repository/product_statistics.go @@ -97,7 +97,14 @@ func (repo *ProductStatisticsRepository) GetByProductID(ctx context.Context, pro func (repo *ProductStatisticsRepository) IncOrders(ctx context.Context, productID string, count int64) error { filter := bson.M{"product_id": productID} - update := bson.M{"$inc": bson.M{"total_orders": count}} + now := time.Now().UTC().UnixNano() + update := bson.M{ + "$inc": bson.M{"total_orders": count}, + "$set": bson.M{ + "total_orders_update_time": now, + "updated_at": now, + }, + } rk := domain.GetProductStatisticsRK(productID) _, err := repo.DB.UpdateOne(ctx, rk, filter, update) @@ -117,7 +124,14 @@ func (repo *ProductStatisticsRepository) IncOrders(ctx context.Context, productI func (repo *ProductStatisticsRepository) DecOrders(ctx context.Context, productID string, count int64) error { filter := bson.M{"product_id": productID, "total_orders": bson.M{"$gte": count}} - update := bson.M{"$inc": bson.M{"total_orders": -count}} + now := time.Now().UTC().UnixNano() + update := bson.M{ + "$inc": bson.M{"total_orders": -count}, + "$set": bson.M{ + "total_orders_update_time": now, + "updated_at": now, + }, + } rk := domain.GetProductStatisticsRK(productID) _, err := repo.DB.UpdateOne(ctx, rk, filter, update) @@ -137,7 +151,7 @@ func (repo *ProductStatisticsRepository) DecOrders(ctx context.Context, productI func (repo *ProductStatisticsRepository) UpdateAverageRating(ctx context.Context, productID string, averageRating float64) error { filter := bson.M{"product_id": productID} - now := time.Now().UnixNano() + now := time.Now().UTC().UnixNano() update := bson.M{ "$set": bson.M{ "average_rating": averageRating, @@ -163,7 +177,7 @@ func (repo *ProductStatisticsRepository) UpdateAverageRating(ctx context.Context func (repo *ProductStatisticsRepository) IncFansCount(ctx context.Context, productID string, fansCount uint64) error { filter := bson.M{"product_id": productID} - now := time.Now().UnixNano() + now := time.Now().UTC().UnixNano() update := bson.M{ "$inc": bson.M{"fans_count": fansCount}, "$set": bson.M{ @@ -191,7 +205,7 @@ func (repo *ProductStatisticsRepository) IncFansCount(ctx context.Context, produ func (repo *ProductStatisticsRepository) DecFansCount(ctx context.Context, productID string, fansCount uint64) error { // 只允許在 fans_count 大於或等於欲扣減值時進行扣減 filter := bson.M{"product_id": productID, "fans_count": bson.M{"$gte": fansCount}} - now := time.Now().UnixNano() + now := time.Now().UTC().UnixNano() // 在更新之前先檢查 fansCount 是否過大 if fansCount > uint64(math.MaxInt64) { diff --git a/pkg/repository/product_tags_test.go b/pkg/repository/product_tags_test.go index a11f261..40d7bed 100644 --- a/pkg/repository/product_tags_test.go +++ b/pkg/repository/product_tags_test.go @@ -801,3 +801,115 @@ func TestUnbindTag(t *testing.T) { }) } } + +func TestGetByIDs(t *testing.T) { + repo, tearDown, err := SetupTestProductTagsRepo("testDB_get_by_ids") + require.NoError(t, err) + defer tearDown() + + ctx := context.Background() + now := time.Now().Unix() + + tag1 := &entity.Tags{ + ID: primitive.NewObjectID(), + Name: "Tag1", + Types: product.ItemTypeProduct, + ShowType: product.ShowTypeNormal, + CreatedAt: now, + UpdatedAt: now, + } + tag2 := &entity.Tags{ + ID: primitive.NewObjectID(), + Name: "Tag2", + Types: product.ItemTypeSkill, + ShowType: product.ShowTypeNormal, + CreatedAt: now, + UpdatedAt: now, + } + + // 寫入測試資料 + require.NoError(t, repo.Create(ctx, tag1)) + require.NoError(t, repo.Create(ctx, tag2)) + + t.Run("return matching tags", func(t *testing.T) { + results, err := repo.GetByIDs(ctx, []string{tag1.ID.Hex(), tag2.ID.Hex()}) + require.NoError(t, err) + require.Len(t, results, 2) + + gotIDs := map[primitive.ObjectID]bool{} + for _, tag := range results { + gotIDs[tag.ID] = true + } + assert.True(t, gotIDs[tag1.ID]) + assert.True(t, gotIDs[tag2.ID]) + }) + + t.Run("skip invalid object ids", func(t *testing.T) { + results, err := repo.GetByIDs(ctx, []string{"invalid_id", tag1.ID.Hex()}) + require.NoError(t, err) + require.Len(t, results, 1) + assert.Equal(t, tag1.ID, results[0].ID) + }) + + t.Run("return empty if all ids invalid", func(t *testing.T) { + results, err := repo.GetByIDs(ctx, []string{"invalid1", "wrong!"}) + require.NoError(t, err) + assert.Empty(t, results) + }) +} + +func TestUnbindTagByReferenceID(t *testing.T) { + repo, tearDown, err := SetupTestProductTagsRepo("testDB_get_by_ids") + require.NoError(t, err) + defer tearDown() + + ctx := context.Background() + now := time.Now().Unix() + referenceID := "ref123" + + // 建立測試資料 + binding1 := &entity.TagsBindingTable{ + ID: primitive.NewObjectID(), + ReferenceID: referenceID, + TagID: "tag1", + CreatedAt: now, + UpdatedAt: now, + } + binding2 := &entity.TagsBindingTable{ + ID: primitive.NewObjectID(), + ReferenceID: referenceID, + TagID: "tag2", + CreatedAt: now, + UpdatedAt: now, + } + bindingOther := &entity.TagsBindingTable{ + ID: primitive.NewObjectID(), + ReferenceID: "ref999", + TagID: "tag999", + CreatedAt: now, + UpdatedAt: now, + } + + require.NoError(t, repo.BindTags(ctx, []*entity.TagsBindingTable{binding1, binding2, bindingOther})) + + t.Run("should delete bindings by referenceID", func(t *testing.T) { + err := repo.UnbindTagByReferenceID(ctx, referenceID) + require.NoError(t, err) + + // 確認 ref123 的都被刪了,ref999 還在 + result, err := repo.GetBindingsByReference(ctx, referenceID) + require.NoError(t, err) + assert.Len(t, result, 0) + + // 檢查其他 reference 還在 + resultOthers, err := repo.GetBindingsByReference(ctx, "ref999") + require.NoError(t, err) + assert.Len(t, resultOthers, 1) + assert.Equal(t, "tag999", resultOthers[0].TagID) + }) + + t.Run("should not fail if no matching data", func(t *testing.T) { + err := repo.UnbindTagByReferenceID(ctx, "nonexistent-ref") + require.NoError(t, err) + }) +} diff --git a/pkg/repository/product_test.go b/pkg/repository/product_test.go index 622f53f..964c041 100644 --- a/pkg/repository/product_test.go +++ b/pkg/repository/product_test.go @@ -68,7 +68,6 @@ func SetupTestProductRepository(db string) (repository.ProductRepository, func() func TestListProduct(t *testing.T) { model, tearDown, err := SetupTestProductRepository("testDB") defer tearDown() - fmt.Println("ddddddddddddddddddddd", err.Error()) assert.NoError(t, err) now := time.Now() diff --git a/pkg/usecase/product.go b/pkg/usecase/product.go index 9c66e06..6bd577c 100644 --- a/pkg/usecase/product.go +++ b/pkg/usecase/product.go @@ -4,9 +4,11 @@ import ( "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity" "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository" "code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/usecase" + repo "code.30cm.net/digimon/app-cloudep-product-service/pkg/repository" "code.30cm.net/digimon/app-cloudep-product-service/pkg/utils" "code.30cm.net/digimon/library-go/errs" "context" + "errors" "github.com/zeromicro/go-zero/core/logx" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" @@ -31,92 +33,14 @@ func MustProductUseCase(param ProductUseCaseParam) usecase.ProductUseCase { } func (use *ProductUseCase) Create(ctx context.Context, product *usecase.Product) error { - // 資料前處理:準備 entity.Product 實體 - insert := &entity.Product{} - - if product.UID != nil { - insert.UID = *product.UID - } - if product.Title != nil { - insert.Title = *product.Title - } - if product.IsPublished != nil { - insert.IsPublished = *product.IsPublished - } - if product.Category != nil { - insert.Category = *product.Category - } - if product.ShortTitle != nil { - insert.ShortTitle = product.ShortTitle - } - if product.Details != nil { - insert.Details = product.Details - } - if product.ShortDescription != nil { - insert.ShortDescription = *product.ShortDescription - } - if product.Slug != nil { - insert.Slug = product.Slug - } - if product.Amount != 0 { - insert.Amount = product.Amount - } - if product.StartTime != nil { - st := utils.Rfc3339ToUnix(utils.ToValue(product.StartTime)) - insert.StartTime = &st - } - if product.EndTime != nil { - et := utils.Rfc3339ToUnix(utils.ToValue(product.EndTime)) - insert.EndTime = &et - } - if len(product.Media) > 0 { - medias := make([]entity.Media, 0, len(product.Media)) - for _, m := range product.Media { - medias = append(medias, entity.Media{ - Sort: m.Sort, - URL: m.URL, - Type: m.Type, - }) - } - insert.Media = medias - } - if len(product.CustomFields) > 0 { - cf := make([]entity.CustomFields, 0, len(product.CustomFields)) - for _, field := range product.CustomFields { - cf = append(cf, entity.CustomFields{ - Key: field.Key, - Value: field.Value, - }) - } - insert.CustomFields = cf - } - - // 綁定 Tags - tagsBinding := make([]*entity.TagsBindingTable, 0, len(product.Tags)) - for _, tag := range product.Tags { - tagsBinding = append(tagsBinding, &entity.TagsBindingTable{ - ReferenceID: insert.ID.Hex(), - TagID: tag, - }) - } - + insert := convertUseCaseToEntity(product) // Transaction 設定:只做必要寫入 opts := options.Transaction().SetReadConcern(readconcern.Local()) err := use.ProductRepo.Transaction(ctx, func(sessCtx mongo.SessionContext) (any, error) { - // 插入 Product if err := use.ProductRepo.Insert(sessCtx, insert); err != nil { - e := errs.DBErrorL(logx.WithContext(ctx), - []logx.LogField{ - {Key: "req", Value: product}, - {Key: "func", Value: "ProductRepo.Insert"}, - {Key: "err", Value: err.Error()}, - }, - "failed to create product") - - return nil, e + return nil, logDBError(ctx, "ProductRepo.Insert", []logx.LogField{{Key: "req", Value: product}}, err, "failed to create product") } - // 插入 Product 統計資料 if err := use.ProductStatisticsRepo.Create(sessCtx, &entity.ProductStatistics{ ProductID: insert.ID.Hex(), Orders: 0, @@ -126,27 +50,12 @@ func (use *ProductUseCase) Create(ctx context.Context, product *usecase.Product) FansCount: 0, FansCountUpdateTime: 0, }); err != nil { - e := errs.DBErrorL(logx.WithContext(ctx), - []logx.LogField{ - {Key: "ProductID", Value: insert.ID.Hex()}, - {Key: "func", Value: "ProductStatisticsRepo.Create"}, - {Key: "err", Value: err.Error()}, - }, - "failed to create product statistics") - - return nil, e + return nil, logDBError(ctx, "ProductStatisticsRepo.Create", []logx.LogField{{Key: "ProductID", Value: insert.ID.Hex()}}, err, "failed to create product statistics") } + tagsBinding := buildTagsBinding(insert.ID.Hex(), product.Tags) if err := use.TagBinding.BindTags(sessCtx, tagsBinding); err != nil { - e := errs.DBErrorL(logx.WithContext(ctx), - []logx.LogField{ - {Key: "ReferenceID", Value: insert.ID.Hex()}, - {Key: "tags", Value: product.Tags}, - {Key: "func", Value: "TagBinding.BindTag"}, - {Key: "err", Value: err.Error()}, - }, "failed to binding product tags") - - return nil, e + return nil, logDBError(ctx, "TagBinding.BindTags", []logx.LogField{{Key: "ReferenceID", Value: insert.ID.Hex()}, {Key: "tags", Value: product.Tags}}, err, "failed to bind product tags") } return nil, nil @@ -160,118 +69,25 @@ func (use *ProductUseCase) Create(ctx context.Context, product *usecase.Product) } func (use *ProductUseCase) Update(ctx context.Context, id string, product *usecase.Product) error { - // 資料前處理:準備 entity.Product 實體 - update := &repository.ProductUpdateParams{} - - if product.Title != nil { - update.Title = product.Title - } - if product.IsPublished != nil { - update.IsPublished = product.IsPublished - } - if product.Category != nil { - update.Category = product.Category - } - if product.ShortTitle != nil { - update.ShortTitle = product.ShortTitle - } - if product.Details != nil { - update.Details = product.Details - } - if product.ShortDescription != nil { - update.ShortDescription = *product.ShortDescription - } - if product.Slug != nil { - update.Slug = product.Slug - } - if product.Amount != 0 { - update.Amount = &product.Amount - } - if product.StartTime != nil { - st := utils.Rfc3339ToUnix(utils.ToValue(product.StartTime)) - update.StartTime = &st - } - if product.EndTime != nil { - et := utils.Rfc3339ToUnix(utils.ToValue(product.EndTime)) - update.EndTime = &et - } - if len(product.Media) > 0 { - medias := make([]entity.Media, 0, len(product.Media)) - for _, m := range product.Media { - medias = append(medias, entity.Media{ - Sort: m.Sort, - URL: m.URL, - Type: m.Type, - }) - } - update.Media = medias - } - if len(product.CustomFields) > 0 { - cf := make([]entity.CustomFields, 0, len(product.CustomFields)) - for _, field := range product.CustomFields { - cf = append(cf, entity.CustomFields{ - Key: field.Key, - Value: field.Value, - }) - } - update.CustomFields = cf - } - - // 綁定 Tags - tagsBinding := make([]*entity.TagsBindingTable, 0, len(product.Tags)) - for _, tag := range product.Tags { - tagsBinding = append(tagsBinding, &entity.TagsBindingTable{ - ReferenceID: id, - TagID: tag, - }) - } - - // Transaction 設定:只做必要寫入 + update := convertUseCaseToUpdateParams(product) + tagsBinding := buildTagsBinding(id, product.Tags) opts := options.Transaction().SetReadConcern(readconcern.Local()) err := use.ProductRepo.Transaction(ctx, func(sessCtx mongo.SessionContext) (any, error) { _, err := use.ProductRepo.Update(sessCtx, id, update) if err != nil { - e := errs.DBErrorL(logx.WithContext(ctx), - []logx.LogField{ - {Key: "req", Value: product}, - {Key: "func", Value: "ProductRepo.Update"}, - {Key: "err", Value: err.Error()}, - }, - "failed to update product") - - return nil, e + return nil, logDBError(ctx, "ProductRepo.Update", []logx.LogField{{Key: "req", Value: product}}, err, "failed to update product") } - if err := use.TagBinding.UnbindTagByReferenceID(sessCtx, id); err != nil { - e := errs.DBErrorL(logx.WithContext(ctx), - []logx.LogField{ - {Key: "ReferenceID", Value: id}, - {Key: "func", Value: "TagBinding.UnbindTagByReferenceID"}, - {Key: "err", Value: err.Error()}, - }, "failed to unbind tags") - - return nil, e + return nil, logDBError(ctx, "TagBinding.UnbindTagByReferenceID", []logx.LogField{{Key: "ReferenceID", Value: id}}, err, "failed to unbind tags") } - if err := use.TagBinding.BindTags(sessCtx, tagsBinding); err != nil { - e := errs.DBErrorL(logx.WithContext(ctx), - []logx.LogField{ - {Key: "ReferenceID", Value: id}, - {Key: "tags", Value: product.Tags}, - {Key: "func", Value: "TagBinding.BindTags"}, - {Key: "err", Value: err.Error()}, - }, "failed to binding product tags") - - return nil, e + return nil, logDBError(ctx, "TagBinding.BindTags", []logx.LogField{{Key: "ReferenceID", Value: id}, {Key: "tags", Value: product.Tags}}, err, "failed to bind tags") } return nil, nil }, opts) - if err != nil { - return err - } - return nil + return err } func (use *ProductUseCase) Delete(ctx context.Context, id string) error { @@ -314,136 +130,30 @@ func (use *ProductUseCase) Delete(ctx context.Context, id string) error { func (use *ProductUseCase) Get(ctx context.Context, id string) (*usecase.ProductResp, error) { product, err := use.ProductRepo.FindOneByID(ctx, id) if err != nil { - e := errs.DBErrorL(logx.WithContext(ctx), - []logx.LogField{ - {Key: "product_id", Value: id}, - {Key: "func", Value: "ProductRepo.FindOneByID"}, - {Key: "err", Value: err.Error()}, - }, "failed to find product") - - return nil, e + return nil, logDBError(ctx, "ProductRepo.FindOneByID", []logx.LogField{{Key: "product_id", Value: id}}, err, "failed to find product") } - productStatistics, err := use.ProductStatisticsRepo.GetByID(ctx, id) + stats, err := use.ProductStatisticsRepo.GetByID(ctx, id) if err != nil { - e := errs.DBErrorL(logx.WithContext(ctx), - []logx.LogField{ - {Key: "product_id", Value: id}, - {Key: "func", Value: "ProductStatisticsRepo.GetByID"}, - {Key: "err", Value: err.Error()}, - }, "failed to get product statistics") - - return nil, e + return nil, logDBError(ctx, "ProductStatisticsRepo.GetByID", []logx.LogField{{Key: "product_id", Value: id}}, err, "failed to get product statistics") } - - tags, err := use.TagRepo.GetBindingsByReference(ctx, id) + bindings, err := use.TagRepo.GetBindingsByReference(ctx, id) if err != nil { - e := errs.DBErrorL(logx.WithContext(ctx), - []logx.LogField{ - {Key: "product_id", Value: id}, - {Key: "func", Value: "TagRepo.GetBindingsByReference"}, - {Key: "err", Value: err.Error()}, - }, "failed to get tags") - - return nil, e + return nil, logDBError(ctx, "TagRepo.GetBindingsByReference", []logx.LogField{{Key: "product_id", Value: id}}, err, "failed to get tag bindings") } - - t := make([]string, 0, len(tags)) - for _, item := range tags { - t = append(t, item.TagID) + tagIDs := make([]string, 0, len(bindings)) + for _, item := range bindings { + tagIDs = append(tagIDs, item.TagID) } - - tagsInfo, err := use.TagRepo.GetByIDs(ctx, t) + tags, err := use.TagRepo.GetByIDs(ctx, tagIDs) if err != nil { - e := errs.DBErrorL(logx.WithContext(ctx), - []logx.LogField{ - {Key: "product_id", Value: id}, - {Key: "func", Value: "TagRepo.GetByIDs"}, - {Key: "err", Value: err.Error()}, - }, "failed to get tags") - - return nil, e - } - // 組合資料 - result := &usecase.ProductResp{ - ID: product.ID.Hex(), - UID: product.UID, - Title: product.Title, - ShortDescription: product.ShortDescription, - IsPublished: product.IsPublished, - Amount: product.Amount, - Category: product.Category, - Orders: productStatistics.Orders, - AverageRating: productStatistics.AverageRating, - FansCount: productStatistics.FansCount, - UpdatedAt: utils.UnixToRfc3339(product.UpdatedAt), - CreatedAt: utils.UnixToRfc3339(product.CreatedAt), + return nil, logDBError(ctx, "TagRepo.GetByIDs", []logx.LogField{{Key: "product_id", Value: id}}, err, "failed to get tags") } - for _, tag := range tagsInfo { - item := usecase.Tags{ - ID: tag.ID.Hex(), - Types: tag.Types, - Name: tag.Name, - ShowType: tag.ShowType, - UpdatedAt: utils.UnixToRfc3339(tag.UpdatedAt), - CreatedAt: utils.UnixToRfc3339(tag.CreatedAt), - } - if tag.Cover != nil { - item.Cover = *tag.Cover - } - result.Tags = append(result.Tags, item) - } - - if len(product.Media) > 0 { - medias := make([]usecase.Media, 0, len(product.Media)) - for _, m := range product.Media { - medias = append(medias, usecase.Media{ - Sort: m.Sort, - URL: m.URL, - Type: m.Type, - }) - } - result.Media = medias - } - if len(product.CustomFields) > 0 { - cf := make([]usecase.CustomFields, 0, len(product.CustomFields)) - for _, field := range product.CustomFields { - cf = append(cf, usecase.CustomFields{ - Key: field.Key, - Value: field.Value, - }) - } - result.CustomFields = cf - } - - if product.Slug != nil { - result.Slug = *product.Slug - } - if product.ShortTitle != nil { - result.ShortTitle = *product.ShortTitle - } - if product.Details != nil { - result.Details = *product.Details - } - if product.StartTime != nil { - result.StartTime = utils.UnixToRfc3339(*product.StartTime) - } - if product.EndTime != nil { - result.EndTime = utils.UnixToRfc3339(*product.EndTime) - } - if productStatistics.OrdersUpdateTime > 0 { - result.OrdersUpdateTime = utils.UnixToRfc3339(productStatistics.OrdersUpdateTime) - } - if productStatistics.FansCountUpdateTime > 0 { - result.FansCountUpdateTime = utils.UnixToRfc3339(productStatistics.FansCountUpdateTime) - } - if productStatistics.AverageRatingUpdateTime > 0 { - result.AverageRatingUpdateTime = utils.UnixToRfc3339(productStatistics.AverageRatingUpdateTime) - } - - return result, nil + return convertEntityToResp(product, stats, tags), nil } +// TODO 效能有問題這邊優先改,List 會有N + 1 問題 + func (use *ProductUseCase) List(ctx context.Context, data usecase.ProductQueryParams) ([]*usecase.ProductResp, int64, error) { query := &repository.ProductQueryParams{ PageSize: data.PageSize, @@ -465,58 +175,455 @@ func (use *ProductUseCase) List(ctx context.Context, data usecase.ProductQueryPa if data.StartTime != nil { query.StartTime = data.StartTime } + if data.EndTime != nil { + query.EndTime = data.EndTime + } - EndTime * int64 // 結束時間(Unix 時間戳) - Slug * string // URL 後綴 - - product, i, err := use.ProductRepo.ListProduct(ctx, &repository.ProductQueryParams{}) + products, total, err := use.ProductRepo.ListProduct(ctx, query) if err != nil { return nil, 0, err } + result := make([]*usecase.ProductResp, 0, len(products)) + for _, p := range products { + // 查詢統計資料 + stats, _ := use.ProductStatisticsRepo.GetByID(ctx, p.ID.Hex()) + + // 查詢 Tag 資訊 + tags, _ := use.TagRepo.GetBindingsByReference(ctx, p.ID.Hex()) + tagIDs := make([]string, 0, len(tags)) + for _, t := range tags { + tagIDs = append(tagIDs, t.TagID) + } + tagsInfo, _ := use.TagRepo.GetByIDs(ctx, tagIDs) + + // 組合 Tags 回應 + respTags := make([]usecase.Tags, 0, len(tagsInfo)) + for _, tag := range tagsInfo { + item := usecase.Tags{ + ID: tag.ID.Hex(), + Types: tag.Types, + Name: tag.Name, + ShowType: tag.ShowType, + UpdatedAt: utils.UnixToRfc3339(tag.UpdatedAt), + CreatedAt: utils.UnixToRfc3339(tag.CreatedAt), + } + if tag.Cover != nil { + item.Cover = *tag.Cover + } + respTags = append(respTags, item) + } + + // Media & CustomFields + media := make([]usecase.Media, 0, len(p.Media)) + for _, m := range p.Media { + media = append(media, usecase.Media{ + Sort: m.Sort, + URL: m.URL, + Type: m.Type, + }) + } + + customFields := make([]usecase.CustomFields, 0, len(p.CustomFields)) + for _, f := range p.CustomFields { + customFields = append(customFields, usecase.CustomFields{ + Key: f.Key, + Value: f.Value, + }) + } + + resp := &usecase.ProductResp{ + ID: p.ID.Hex(), + UID: p.UID, + Title: p.Title, + ShortTitle: utils.ToValue(p.ShortTitle), + Details: utils.ToValue(p.Details), + ShortDescription: p.ShortDescription, + Media: media, + Slug: utils.ToValue(p.Slug), + IsPublished: p.IsPublished, + Amount: p.Amount, + StartTime: utils.UnixToRfc3339(utils.ToValue(p.StartTime)), + EndTime: utils.UnixToRfc3339(utils.ToValue(p.EndTime)), + Category: p.Category, + CustomFields: customFields, + Tags: respTags, + Orders: stats.Orders, + OrdersUpdateTime: utils.UnixToRfc3339(stats.OrdersUpdateTime), + AverageRating: stats.AverageRating, + AverageRatingUpdateTime: utils.UnixToRfc3339(stats.AverageRatingUpdateTime), + FansCount: stats.FansCount, + FansCountUpdateTime: utils.UnixToRfc3339(stats.FansCountUpdateTime), + UpdatedAt: utils.UnixToRfc3339(p.UpdatedAt), + CreatedAt: utils.UnixToRfc3339(p.CreatedAt), + } + + result = append(result, resp) + } + + return result, total, nil } func (use *ProductUseCase) IncOrders(ctx context.Context, productID string, count int64) error { - //TODO implement me - panic("implement me") + err := use.ProductStatisticsRepo.IncOrders(ctx, productID, count) + if err != nil { + return errs.DBErrorL(logx.WithContext(ctx), + []logx.LogField{ + {Key: "product_id", Value: productID}, + {Key: "func", Value: "ProductStatisticsRepo.IncOrders"}, + {Key: "err", Value: err.Error()}, + }, "failed to inc order") + } + + return nil } func (use *ProductUseCase) DecOrders(ctx context.Context, productID string, count int64) error { - //TODO implement me - panic("implement me") + err := use.ProductStatisticsRepo.DecOrders(ctx, productID, count) + if err != nil { + return errs.DBErrorL(logx.WithContext(ctx), + []logx.LogField{ + {Key: "product_id", Value: productID}, + {Key: "func", Value: "ProductStatisticsRepo.DecOrders"}, + {Key: "err", Value: err.Error()}, + }, "failed to dec order") + } + + return nil } func (use *ProductUseCase) UpdateAverageRating(ctx context.Context, productID string, averageRating float64) error { - //TODO implement me - panic("implement me") + err := use.ProductStatisticsRepo.UpdateAverageRating(ctx, productID, averageRating) + if err != nil { + return errs.DBErrorL(logx.WithContext(ctx), + []logx.LogField{ + {Key: "product_id", Value: productID}, + {Key: "func", Value: "ProductStatisticsRepo.UpdateAverageRating"}, + {Key: "err", Value: err.Error()}, + }, "failed to update average rating") + } + + return nil } func (use *ProductUseCase) IncFansCount(ctx context.Context, productID string, fansCount uint64) error { - //TODO implement me - panic("implement me") + err := use.ProductStatisticsRepo.IncFansCount(ctx, productID, fansCount) + if err != nil { + return errs.DBErrorL(logx.WithContext(ctx), + []logx.LogField{ + {Key: "product_id", Value: productID}, + {Key: "func", Value: "ProductStatisticsRepo.IncFansCount"}, + {Key: "err", Value: err.Error()}, + }, "failed to inc fans count") + } + + return nil } func (use *ProductUseCase) DecFansCount(ctx context.Context, productID string, fansCount uint64) error { - //TODO implement me - panic("implement me") + err := use.ProductStatisticsRepo.DecFansCount(ctx, productID, fansCount) + if err != nil { + return errs.DBErrorL(logx.WithContext(ctx), + []logx.LogField{ + {Key: "product_id", Value: productID}, + {Key: "func", Value: "ProductStatisticsRepo.DecFansCount"}, + {Key: "err", Value: err.Error()}, + }, "failed to dec fans count") + } + + return nil } func (use *ProductUseCase) BindTag(ctx context.Context, binding usecase.TagsBindingTable) error { - //TODO implement me - panic("implement me") + _, err := use.TagRepo.GetByID(ctx, binding.TagID) + if err != nil { + if errors.Is(err, repo.ErrNotFound) { + return errs.ResourceNotFound("failed to get tags") + } + + return err + } + + _, err = use.ProductRepo.FindOneByID(ctx, binding.ReferenceID) + if err != nil { + if errors.Is(err, repo.ErrNotFound) { + return errs.ResourceNotFound("failed to get tags") + } + + return err + } + + err = use.TagBinding.BindTags(ctx, []*entity.TagsBindingTable{{ReferenceID: binding.ReferenceID, TagID: binding.TagID}}) + if err != nil { + return errs.DBErrorL(logx.WithContext(ctx), + []logx.LogField{ + {Key: "binding", Value: binding}, + {Key: "func", Value: "TagBinding.BindTags"}, + {Key: "err", Value: err.Error()}, + }, "failed to bind tags") + } + + return nil } func (use *ProductUseCase) UnbindTag(ctx context.Context, binding usecase.TagsBindingTable) error { - //TODO implement me - panic("implement me") + _, err := use.TagRepo.GetByID(ctx, binding.TagID) + if err != nil { + if errors.Is(err, repo.ErrNotFound) { + return errs.ResourceNotFound("failed to get tags") + } + + return err + } + + _, err = use.ProductRepo.FindOneByID(ctx, binding.ReferenceID) + if err != nil { + if errors.Is(err, repo.ErrNotFound) { + return errs.ResourceNotFound("failed to get tags") + } + + return err + } + + err = use.TagBinding.UnbindTag(ctx, binding.TagID, binding.ReferenceID) + if err != nil { + return errs.DBErrorL(logx.WithContext(ctx), + []logx.LogField{ + {Key: "binding", Value: binding}, + {Key: "func", Value: "TagBinding.UnbindTag"}, + {Key: "err", Value: err.Error()}, + }, "failed to unbind tags") + } + + return nil } func (use *ProductUseCase) GetBindingsByReference(ctx context.Context, referenceID string) ([]usecase.TagsBindingTableResp, error) { - //TODO implement me - panic("implement me") + ref, err := use.TagBinding.GetBindingsByReference(ctx, referenceID) + if err != nil { + return nil, errs.DBErrorL(logx.WithContext(ctx), + []logx.LogField{ + {Key: "referenceID", Value: referenceID}, + {Key: "func", Value: "TagBinding.GetBindingsByReference"}, + {Key: "err", Value: err.Error()}, + }, "failed to get bindings by reference") + } + + result := make([]usecase.TagsBindingTableResp, 0, len(ref)) + for _, bind := range ref { + result = append(result, usecase.TagsBindingTableResp{ + ID: bind.ID.Hex(), + ReferenceID: bind.ReferenceID, + TagID: bind.TagID, + CreatedAt: utils.UnixToRfc3339(bind.CreatedAt), + UpdatedAt: utils.UnixToRfc3339(bind.UpdatedAt), + }) + } + + return result, nil } func (use *ProductUseCase) ListTagBinding(ctx context.Context, params usecase.TagBindingQueryParams) ([]usecase.TagsBindingTableResp, int64, error) { - //TODO implement me - panic("implement me") + ref, total, err := use.TagBinding.ListTagBinding(ctx, repository.TagBindingQueryParams{ + ReferenceID: params.ReferenceID, + TagID: params.TagID, + PageIndex: params.PageIndex, + PageSize: params.PageSize, + }) + if err != nil { + return nil, 0, errs.DBErrorL(logx.WithContext(ctx), + []logx.LogField{ + {Key: "params", Value: params}, + {Key: "func", Value: "TagBinding.ListTagBinding"}, + {Key: "err", Value: err.Error()}, + }, "failed to list tags") + } + + result := make([]usecase.TagsBindingTableResp, 0, len(ref)) + for _, bind := range ref { + result = append(result, usecase.TagsBindingTableResp{ + ID: bind.ID.Hex(), + ReferenceID: bind.ReferenceID, + TagID: bind.TagID, + CreatedAt: utils.UnixToRfc3339(bind.CreatedAt), + UpdatedAt: utils.UnixToRfc3339(bind.UpdatedAt), + }) + } + + return result, total, nil +} + +func logDBError(ctx context.Context, funcName string, fields []logx.LogField, err error, msg string) error { + return errs.DBErrorL(logx.WithContext(ctx), append(fields, logx.Field("func", funcName), logx.Field("err", err.Error())), msg) +} + +func buildTagsBinding(referenceID string, tags []string) []*entity.TagsBindingTable { + binding := make([]*entity.TagsBindingTable, 0, len(tags)) + for _, tag := range tags { + binding = append(binding, &entity.TagsBindingTable{ + ReferenceID: referenceID, + TagID: tag, + }) + } + return binding +} + +func convertUseCaseToEntity(product *usecase.Product) *entity.Product { + insert := &entity.Product{} + if product.UID != nil { + insert.UID = *product.UID + } + if product.Title != nil { + insert.Title = *product.Title + } + if product.IsPublished != nil { + insert.IsPublished = *product.IsPublished + } + if product.Category != nil { + insert.Category = *product.Category + } + if product.ShortTitle != nil { + insert.ShortTitle = product.ShortTitle + } + if product.Details != nil { + insert.Details = product.Details + } + if product.ShortDescription != nil { + insert.ShortDescription = *product.ShortDescription + } + if product.Slug != nil { + insert.Slug = product.Slug + } + if product.Amount != 0 { + insert.Amount = product.Amount + } + if product.StartTime != nil { + st := utils.Rfc3339ToUnix(utils.ToValue(product.StartTime)) + insert.StartTime = &st + } + if product.EndTime != nil { + et := utils.Rfc3339ToUnix(utils.ToValue(product.EndTime)) + insert.EndTime = &et + } + if len(product.Media) > 0 { + medias := make([]entity.Media, 0, len(product.Media)) + for _, m := range product.Media { + medias = append(medias, entity.Media{Sort: m.Sort, URL: m.URL, Type: m.Type}) + } + insert.Media = medias + } + if len(product.CustomFields) > 0 { + cf := make([]entity.CustomFields, 0, len(product.CustomFields)) + for _, field := range product.CustomFields { + cf = append(cf, entity.CustomFields{Key: field.Key, Value: field.Value}) + } + insert.CustomFields = cf + } + + return insert +} + +func convertUseCaseToUpdateParams(product *usecase.Product) *repository.ProductUpdateParams { + update := &repository.ProductUpdateParams{} + if product.Title != nil { + update.Title = product.Title + } + if product.IsPublished != nil { + update.IsPublished = product.IsPublished + } + if product.Category != nil { + update.Category = product.Category + } + if product.ShortTitle != nil { + update.ShortTitle = product.ShortTitle + } + if product.Details != nil { + update.Details = product.Details + } + if product.ShortDescription != nil { + update.ShortDescription = *product.ShortDescription + } + if product.Slug != nil { + update.Slug = product.Slug + } + if product.Amount != 0 { + update.Amount = &product.Amount + } + if product.StartTime != nil { + st := utils.Rfc3339ToUnix(utils.ToValue(product.StartTime)) + update.StartTime = &st + } + if product.EndTime != nil { + et := utils.Rfc3339ToUnix(utils.ToValue(product.EndTime)) + update.EndTime = &et + } + if len(product.Media) > 0 { + medias := make([]entity.Media, 0, len(product.Media)) + for _, m := range product.Media { + medias = append(medias, entity.Media{Sort: m.Sort, URL: m.URL, Type: m.Type}) + } + update.Media = medias + } + if len(product.CustomFields) > 0 { + cf := make([]entity.CustomFields, 0, len(product.CustomFields)) + for _, field := range product.CustomFields { + cf = append(cf, entity.CustomFields{Key: field.Key, Value: field.Value}) + } + update.CustomFields = cf + } + return update +} + +func convertEntityToResp(p *entity.Product, stats *entity.ProductStatistics, tags []*entity.Tags) *usecase.ProductResp { + tagsResp := make([]usecase.Tags, 0, len(tags)) + for _, tag := range tags { + item := usecase.Tags{ + ID: tag.ID.Hex(), + Types: tag.Types, + Name: tag.Name, + ShowType: tag.ShowType, + UpdatedAt: utils.UnixToRfc3339(tag.UpdatedAt), + CreatedAt: utils.UnixToRfc3339(tag.CreatedAt), + } + if tag.Cover != nil { + item.Cover = *tag.Cover + } + tagsResp = append(tagsResp, item) + } + + media := make([]usecase.Media, 0, len(p.Media)) + for _, m := range p.Media { + media = append(media, usecase.Media{Sort: m.Sort, URL: m.URL, Type: m.Type}) + } + + cf := make([]usecase.CustomFields, 0, len(p.CustomFields)) + for _, field := range p.CustomFields { + cf = append(cf, usecase.CustomFields{Key: field.Key, Value: field.Value}) + } + + return &usecase.ProductResp{ + ID: p.ID.Hex(), + UID: p.UID, + Title: p.Title, + ShortTitle: utils.ToValue(p.ShortTitle), + Details: utils.ToValue(p.Details), + ShortDescription: p.ShortDescription, + Slug: utils.ToValue(p.Slug), + IsPublished: p.IsPublished, + Amount: p.Amount, + StartTime: utils.UnixToRfc3339(utils.ToValue(p.StartTime)), + EndTime: utils.UnixToRfc3339(utils.ToValue(p.EndTime)), + Category: p.Category, + Media: media, + CustomFields: cf, + Tags: tagsResp, + Orders: stats.Orders, + OrdersUpdateTime: utils.UnixToRfc3339(stats.OrdersUpdateTime), + AverageRating: stats.AverageRating, + AverageRatingUpdateTime: utils.UnixToRfc3339(stats.AverageRatingUpdateTime), + FansCount: stats.FansCount, + FansCountUpdateTime: utils.UnixToRfc3339(stats.FansCountUpdateTime), + UpdatedAt: utils.UnixToRfc3339(p.UpdatedAt), + CreatedAt: utils.UnixToRfc3339(p.CreatedAt), + } }