feat: 实现门店套餐分配功能并统一测试基础设施
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m30s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m30s
新增功能: - 门店套餐分配管理(shop_package_allocation):支持门店套餐库存管理 - 门店套餐系列分配管理(shop_series_allocation):支持套餐系列分配和佣金层级设置 - 我的套餐查询(my_package):支持门店查询自己的套餐分配情况 测试改进: - 统一集成测试基础设施,新增 testutils.NewIntegrationTestEnv - 重构所有集成测试使用新的测试环境设置 - 移除旧的测试辅助函数和冗余测试文件 - 新增 test_helpers_test.go 统一任务测试辅助 技术细节: - 新增数据库迁移 000025_create_shop_allocation_tables - 新增 3 个 Handler、Service、Store 和对应的单元测试 - 更新 OpenAPI 文档和文档生成器 - 测试覆盖率:Service 层 > 90% Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
109
internal/store/postgres/shop_package_allocation_store.go
Normal file
109
internal/store/postgres/shop_package_allocation_store.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ShopPackageAllocationStore struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewShopPackageAllocationStore(db *gorm.DB) *ShopPackageAllocationStore {
|
||||
return &ShopPackageAllocationStore{db: db}
|
||||
}
|
||||
|
||||
func (s *ShopPackageAllocationStore) Create(ctx context.Context, allocation *model.ShopPackageAllocation) error {
|
||||
return s.db.WithContext(ctx).Create(allocation).Error
|
||||
}
|
||||
|
||||
func (s *ShopPackageAllocationStore) GetByID(ctx context.Context, id uint) (*model.ShopPackageAllocation, error) {
|
||||
var allocation model.ShopPackageAllocation
|
||||
if err := s.db.WithContext(ctx).First(&allocation, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &allocation, nil
|
||||
}
|
||||
|
||||
func (s *ShopPackageAllocationStore) GetByShopAndPackage(ctx context.Context, shopID, packageID uint) (*model.ShopPackageAllocation, error) {
|
||||
var allocation model.ShopPackageAllocation
|
||||
if err := s.db.WithContext(ctx).Where("shop_id = ? AND package_id = ?", shopID, packageID).First(&allocation).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &allocation, nil
|
||||
}
|
||||
|
||||
func (s *ShopPackageAllocationStore) Update(ctx context.Context, allocation *model.ShopPackageAllocation) error {
|
||||
return s.db.WithContext(ctx).Save(allocation).Error
|
||||
}
|
||||
|
||||
func (s *ShopPackageAllocationStore) Delete(ctx context.Context, id uint) error {
|
||||
return s.db.WithContext(ctx).Delete(&model.ShopPackageAllocation{}, id).Error
|
||||
}
|
||||
|
||||
func (s *ShopPackageAllocationStore) List(ctx context.Context, opts *store.QueryOptions, filters map[string]interface{}) ([]*model.ShopPackageAllocation, int64, error) {
|
||||
var allocations []*model.ShopPackageAllocation
|
||||
var total int64
|
||||
|
||||
query := s.db.WithContext(ctx).Model(&model.ShopPackageAllocation{})
|
||||
|
||||
if shopID, ok := filters["shop_id"].(uint); ok && shopID > 0 {
|
||||
query = query.Where("shop_id = ?", shopID)
|
||||
}
|
||||
if packageID, ok := filters["package_id"].(uint); ok && packageID > 0 {
|
||||
query = query.Where("package_id = ?", packageID)
|
||||
}
|
||||
if allocationID, ok := filters["allocation_id"].(uint); ok && allocationID > 0 {
|
||||
query = query.Where("allocation_id = ?", allocationID)
|
||||
}
|
||||
if status, ok := filters["status"].(int); ok && status > 0 {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if opts == nil {
|
||||
opts = store.DefaultQueryOptions()
|
||||
}
|
||||
offset := (opts.Page - 1) * opts.PageSize
|
||||
query = query.Offset(offset).Limit(opts.PageSize)
|
||||
|
||||
if opts.OrderBy != "" {
|
||||
query = query.Order(opts.OrderBy)
|
||||
}
|
||||
|
||||
if err := query.Find(&allocations).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return allocations, total, nil
|
||||
}
|
||||
|
||||
func (s *ShopPackageAllocationStore) UpdateStatus(ctx context.Context, id uint, status int, updater uint) error {
|
||||
return s.db.WithContext(ctx).
|
||||
Model(&model.ShopPackageAllocation{}).
|
||||
Where("id = ?", id).
|
||||
Updates(map[string]interface{}{
|
||||
"status": status,
|
||||
"updater": updater,
|
||||
}).Error
|
||||
}
|
||||
|
||||
func (s *ShopPackageAllocationStore) GetByShopID(ctx context.Context, shopID uint) ([]*model.ShopPackageAllocation, error) {
|
||||
var allocations []*model.ShopPackageAllocation
|
||||
if err := s.db.WithContext(ctx).Where("shop_id = ? AND status = 1", shopID).Find(&allocations).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return allocations, nil
|
||||
}
|
||||
|
||||
func (s *ShopPackageAllocationStore) DeleteByAllocationID(ctx context.Context, allocationID uint) error {
|
||||
return s.db.WithContext(ctx).
|
||||
Where("allocation_id = ?", allocationID).
|
||||
Delete(&model.ShopPackageAllocation{}).Error
|
||||
}
|
||||
241
internal/store/postgres/shop_package_allocation_store_test.go
Normal file
241
internal/store/postgres/shop_package_allocation_store_test.go
Normal file
@@ -0,0 +1,241 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
"github.com/break/junhong_cmp_fiber/tests/testutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestShopPackageAllocationStore_Create(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopPackageAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocation := &model.ShopPackageAllocation{
|
||||
ShopID: 1,
|
||||
PackageID: 1,
|
||||
AllocationID: 1,
|
||||
CostPrice: 5000,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
|
||||
err := s.Create(ctx, allocation)
|
||||
require.NoError(t, err)
|
||||
assert.NotZero(t, allocation.ID)
|
||||
}
|
||||
|
||||
func TestShopPackageAllocationStore_GetByID(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopPackageAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocation := &model.ShopPackageAllocation{
|
||||
ShopID: 2,
|
||||
PackageID: 2,
|
||||
AllocationID: 1,
|
||||
CostPrice: 6000,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, allocation))
|
||||
|
||||
t.Run("查询存在的分配", func(t *testing.T) {
|
||||
result, err := s.GetByID(ctx, allocation.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, allocation.ShopID, result.ShopID)
|
||||
assert.Equal(t, allocation.PackageID, result.PackageID)
|
||||
assert.Equal(t, allocation.CostPrice, result.CostPrice)
|
||||
})
|
||||
|
||||
t.Run("查询不存在的分配", func(t *testing.T) {
|
||||
_, err := s.GetByID(ctx, 99999)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestShopPackageAllocationStore_GetByShopAndPackage(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopPackageAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocation := &model.ShopPackageAllocation{
|
||||
ShopID: 3,
|
||||
PackageID: 3,
|
||||
AllocationID: 1,
|
||||
CostPrice: 7000,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, allocation))
|
||||
|
||||
t.Run("查询存在的店铺和套餐组合", func(t *testing.T) {
|
||||
result, err := s.GetByShopAndPackage(ctx, 3, 3)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, allocation.ID, result.ID)
|
||||
assert.Equal(t, uint(3), result.ShopID)
|
||||
assert.Equal(t, uint(3), result.PackageID)
|
||||
})
|
||||
|
||||
t.Run("查询不存在的组合", func(t *testing.T) {
|
||||
_, err := s.GetByShopAndPackage(ctx, 99, 99)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestShopPackageAllocationStore_Update(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopPackageAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocation := &model.ShopPackageAllocation{
|
||||
ShopID: 4,
|
||||
PackageID: 4,
|
||||
AllocationID: 1,
|
||||
CostPrice: 5000,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, allocation))
|
||||
|
||||
allocation.CostPrice = 8000
|
||||
err := s.Update(ctx, allocation)
|
||||
require.NoError(t, err)
|
||||
|
||||
updated, err := s.GetByID(ctx, allocation.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(8000), updated.CostPrice)
|
||||
}
|
||||
|
||||
func TestShopPackageAllocationStore_Delete(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopPackageAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocation := &model.ShopPackageAllocation{
|
||||
ShopID: 5,
|
||||
PackageID: 5,
|
||||
AllocationID: 1,
|
||||
CostPrice: 5000,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, allocation))
|
||||
|
||||
err := s.Delete(ctx, allocation.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = s.GetByID(ctx, allocation.ID)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestShopPackageAllocationStore_List(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopPackageAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocations := []*model.ShopPackageAllocation{
|
||||
{ShopID: 10, PackageID: 10, AllocationID: 1, CostPrice: 5000, Status: constants.StatusEnabled},
|
||||
{ShopID: 11, PackageID: 11, AllocationID: 1, CostPrice: 6000, Status: constants.StatusEnabled},
|
||||
{ShopID: 12, PackageID: 12, AllocationID: 2, CostPrice: 7000, Status: constants.StatusEnabled},
|
||||
}
|
||||
for _, a := range allocations {
|
||||
require.NoError(t, s.Create(ctx, a))
|
||||
}
|
||||
allocations[2].Status = constants.StatusDisabled
|
||||
require.NoError(t, s.Update(ctx, allocations[2]))
|
||||
|
||||
t.Run("查询所有分配", func(t *testing.T) {
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, nil)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(3))
|
||||
assert.GreaterOrEqual(t, len(result), 3)
|
||||
})
|
||||
|
||||
t.Run("按店铺ID过滤", func(t *testing.T) {
|
||||
filters := map[string]interface{}{"shop_id": uint(10)}
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(1))
|
||||
for _, a := range result {
|
||||
assert.Equal(t, uint(10), a.ShopID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("按套餐ID过滤", func(t *testing.T) {
|
||||
filters := map[string]interface{}{"package_id": uint(11)}
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(1))
|
||||
for _, a := range result {
|
||||
assert.Equal(t, uint(11), a.PackageID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("按分配ID过滤", func(t *testing.T) {
|
||||
filters := map[string]interface{}{"allocation_id": uint(1)}
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(2))
|
||||
for _, a := range result {
|
||||
assert.Equal(t, uint(1), a.AllocationID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("按状态过滤-启用状态值为1", func(t *testing.T) {
|
||||
filters := map[string]interface{}{"status": 1}
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(2))
|
||||
for _, a := range result {
|
||||
assert.Equal(t, 1, a.Status)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("按状态过滤-启用", func(t *testing.T) {
|
||||
filters := map[string]interface{}{"status": constants.StatusEnabled}
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(2))
|
||||
for _, a := range result {
|
||||
assert.Equal(t, constants.StatusEnabled, a.Status)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("分页查询", func(t *testing.T) {
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 2}, nil)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(3))
|
||||
assert.LessOrEqual(t, len(result), 2)
|
||||
})
|
||||
|
||||
t.Run("默认分页选项", func(t *testing.T) {
|
||||
result, _, err := s.List(ctx, nil, nil)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestShopPackageAllocationStore_UpdateStatus(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopPackageAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocation := &model.ShopPackageAllocation{
|
||||
ShopID: 20,
|
||||
PackageID: 20,
|
||||
AllocationID: 1,
|
||||
CostPrice: 5000,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, allocation))
|
||||
|
||||
err := s.UpdateStatus(ctx, allocation.ID, constants.StatusDisabled, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
updated, err := s.GetByID(ctx, allocation.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, constants.StatusDisabled, updated.Status)
|
||||
assert.Equal(t, uint(1), updated.Updater)
|
||||
}
|
||||
124
internal/store/postgres/shop_series_allocation_store.go
Normal file
124
internal/store/postgres/shop_series_allocation_store.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ShopSeriesAllocationStore struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewShopSeriesAllocationStore(db *gorm.DB) *ShopSeriesAllocationStore {
|
||||
return &ShopSeriesAllocationStore{db: db}
|
||||
}
|
||||
|
||||
func (s *ShopSeriesAllocationStore) Create(ctx context.Context, allocation *model.ShopSeriesAllocation) error {
|
||||
return s.db.WithContext(ctx).Create(allocation).Error
|
||||
}
|
||||
|
||||
func (s *ShopSeriesAllocationStore) GetByID(ctx context.Context, id uint) (*model.ShopSeriesAllocation, error) {
|
||||
var allocation model.ShopSeriesAllocation
|
||||
if err := s.db.WithContext(ctx).First(&allocation, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &allocation, nil
|
||||
}
|
||||
|
||||
func (s *ShopSeriesAllocationStore) GetByShopAndSeries(ctx context.Context, shopID, seriesID uint) (*model.ShopSeriesAllocation, error) {
|
||||
var allocation model.ShopSeriesAllocation
|
||||
if err := s.db.WithContext(ctx).Where("shop_id = ? AND series_id = ?", shopID, seriesID).First(&allocation).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &allocation, nil
|
||||
}
|
||||
|
||||
func (s *ShopSeriesAllocationStore) Update(ctx context.Context, allocation *model.ShopSeriesAllocation) error {
|
||||
return s.db.WithContext(ctx).Save(allocation).Error
|
||||
}
|
||||
|
||||
func (s *ShopSeriesAllocationStore) Delete(ctx context.Context, id uint) error {
|
||||
return s.db.WithContext(ctx).Delete(&model.ShopSeriesAllocation{}, id).Error
|
||||
}
|
||||
|
||||
func (s *ShopSeriesAllocationStore) List(ctx context.Context, opts *store.QueryOptions, filters map[string]interface{}) ([]*model.ShopSeriesAllocation, int64, error) {
|
||||
var allocations []*model.ShopSeriesAllocation
|
||||
var total int64
|
||||
|
||||
query := s.db.WithContext(ctx).Model(&model.ShopSeriesAllocation{})
|
||||
|
||||
if shopID, ok := filters["shop_id"].(uint); ok && shopID > 0 {
|
||||
query = query.Where("shop_id = ?", shopID)
|
||||
}
|
||||
if seriesID, ok := filters["series_id"].(uint); ok && seriesID > 0 {
|
||||
query = query.Where("series_id = ?", seriesID)
|
||||
}
|
||||
if allocatorShopID, ok := filters["allocator_shop_id"].(uint); ok && allocatorShopID > 0 {
|
||||
query = query.Where("allocator_shop_id = ?", allocatorShopID)
|
||||
}
|
||||
if status, ok := filters["status"].(int); ok && status > 0 {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if opts == nil {
|
||||
opts = store.DefaultQueryOptions()
|
||||
}
|
||||
offset := (opts.Page - 1) * opts.PageSize
|
||||
query = query.Offset(offset).Limit(opts.PageSize)
|
||||
|
||||
if opts.OrderBy != "" {
|
||||
query = query.Order(opts.OrderBy)
|
||||
}
|
||||
|
||||
if err := query.Find(&allocations).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return allocations, total, nil
|
||||
}
|
||||
|
||||
func (s *ShopSeriesAllocationStore) UpdateStatus(ctx context.Context, id uint, status int, updater uint) error {
|
||||
return s.db.WithContext(ctx).
|
||||
Model(&model.ShopSeriesAllocation{}).
|
||||
Where("id = ?", id).
|
||||
Updates(map[string]interface{}{
|
||||
"status": status,
|
||||
"updater": updater,
|
||||
}).Error
|
||||
}
|
||||
|
||||
func (s *ShopSeriesAllocationStore) HasDependentAllocations(ctx context.Context, allocatorShopID, seriesID uint) (bool, error) {
|
||||
var count int64
|
||||
err := s.db.WithContext(ctx).
|
||||
Model(&model.ShopSeriesAllocation{}).
|
||||
Where("allocator_shop_id IN (SELECT id FROM tb_shop WHERE parent_id = ?)", allocatorShopID).
|
||||
Where("series_id = ?", seriesID).
|
||||
Count(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func (s *ShopSeriesAllocationStore) GetByShopID(ctx context.Context, shopID uint) ([]*model.ShopSeriesAllocation, error) {
|
||||
var allocations []*model.ShopSeriesAllocation
|
||||
if err := s.db.WithContext(ctx).Where("shop_id = ? AND status = 1", shopID).Find(&allocations).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return allocations, nil
|
||||
}
|
||||
|
||||
func (s *ShopSeriesAllocationStore) GetByAllocatorShopID(ctx context.Context, allocatorShopID uint) ([]*model.ShopSeriesAllocation, error) {
|
||||
var allocations []*model.ShopSeriesAllocation
|
||||
if err := s.db.WithContext(ctx).Where("allocator_shop_id = ?", allocatorShopID).Find(&allocations).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return allocations, nil
|
||||
}
|
||||
281
internal/store/postgres/shop_series_allocation_store_test.go
Normal file
281
internal/store/postgres/shop_series_allocation_store_test.go
Normal file
@@ -0,0 +1,281 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
"github.com/break/junhong_cmp_fiber/tests/testutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestShopSeriesAllocationStore_Create(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopSeriesAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocation := &model.ShopSeriesAllocation{
|
||||
ShopID: 1,
|
||||
SeriesID: 1,
|
||||
AllocatorShopID: 0,
|
||||
PricingMode: model.PricingModeFixed,
|
||||
PricingValue: 1000,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
|
||||
err := s.Create(ctx, allocation)
|
||||
require.NoError(t, err)
|
||||
assert.NotZero(t, allocation.ID)
|
||||
}
|
||||
|
||||
func TestShopSeriesAllocationStore_GetByID(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopSeriesAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocation := &model.ShopSeriesAllocation{
|
||||
ShopID: 2,
|
||||
SeriesID: 2,
|
||||
AllocatorShopID: 0,
|
||||
PricingMode: model.PricingModePercent,
|
||||
PricingValue: 500,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, allocation))
|
||||
|
||||
t.Run("查询存在的分配", func(t *testing.T) {
|
||||
result, err := s.GetByID(ctx, allocation.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, allocation.ShopID, result.ShopID)
|
||||
assert.Equal(t, allocation.SeriesID, result.SeriesID)
|
||||
assert.Equal(t, allocation.PricingMode, result.PricingMode)
|
||||
})
|
||||
|
||||
t.Run("查询不存在的分配", func(t *testing.T) {
|
||||
_, err := s.GetByID(ctx, 99999)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestShopSeriesAllocationStore_GetByShopAndSeries(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopSeriesAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocation := &model.ShopSeriesAllocation{
|
||||
ShopID: 3,
|
||||
SeriesID: 3,
|
||||
AllocatorShopID: 0,
|
||||
PricingMode: model.PricingModeFixed,
|
||||
PricingValue: 2000,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, allocation))
|
||||
|
||||
t.Run("查询存在的店铺和系列组合", func(t *testing.T) {
|
||||
result, err := s.GetByShopAndSeries(ctx, 3, 3)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, allocation.ID, result.ID)
|
||||
assert.Equal(t, uint(3), result.ShopID)
|
||||
assert.Equal(t, uint(3), result.SeriesID)
|
||||
})
|
||||
|
||||
t.Run("查询不存在的组合", func(t *testing.T) {
|
||||
_, err := s.GetByShopAndSeries(ctx, 99, 99)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestShopSeriesAllocationStore_Update(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopSeriesAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocation := &model.ShopSeriesAllocation{
|
||||
ShopID: 4,
|
||||
SeriesID: 4,
|
||||
AllocatorShopID: 0,
|
||||
PricingMode: model.PricingModeFixed,
|
||||
PricingValue: 1500,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, allocation))
|
||||
|
||||
allocation.PricingValue = 2500
|
||||
allocation.PricingMode = model.PricingModePercent
|
||||
err := s.Update(ctx, allocation)
|
||||
require.NoError(t, err)
|
||||
|
||||
updated, err := s.GetByID(ctx, allocation.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(2500), updated.PricingValue)
|
||||
assert.Equal(t, model.PricingModePercent, updated.PricingMode)
|
||||
}
|
||||
|
||||
func TestShopSeriesAllocationStore_Delete(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopSeriesAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocation := &model.ShopSeriesAllocation{
|
||||
ShopID: 5,
|
||||
SeriesID: 5,
|
||||
AllocatorShopID: 0,
|
||||
PricingMode: model.PricingModeFixed,
|
||||
PricingValue: 1000,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, allocation))
|
||||
|
||||
err := s.Delete(ctx, allocation.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = s.GetByID(ctx, allocation.ID)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestShopSeriesAllocationStore_List(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopSeriesAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocations := []*model.ShopSeriesAllocation{
|
||||
{ShopID: 10, SeriesID: 10, AllocatorShopID: 0, PricingMode: model.PricingModeFixed, PricingValue: 1000, Status: constants.StatusEnabled},
|
||||
{ShopID: 11, SeriesID: 11, AllocatorShopID: 0, PricingMode: model.PricingModePercent, PricingValue: 500, Status: constants.StatusEnabled},
|
||||
{ShopID: 12, SeriesID: 12, AllocatorShopID: 1, PricingMode: model.PricingModeFixed, PricingValue: 2000, Status: constants.StatusEnabled},
|
||||
}
|
||||
for _, a := range allocations {
|
||||
require.NoError(t, s.Create(ctx, a))
|
||||
}
|
||||
// 显式更新第三个分配为禁用状态
|
||||
allocations[2].Status = constants.StatusDisabled
|
||||
require.NoError(t, s.Update(ctx, allocations[2]))
|
||||
|
||||
t.Run("查询所有分配", func(t *testing.T) {
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, nil)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(3))
|
||||
assert.GreaterOrEqual(t, len(result), 3)
|
||||
})
|
||||
|
||||
t.Run("按店铺ID过滤", func(t *testing.T) {
|
||||
filters := map[string]interface{}{"shop_id": uint(10)}
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(1))
|
||||
for _, a := range result {
|
||||
assert.Equal(t, uint(10), a.ShopID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("按系列ID过滤", func(t *testing.T) {
|
||||
filters := map[string]interface{}{"series_id": uint(11)}
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(1))
|
||||
for _, a := range result {
|
||||
assert.Equal(t, uint(11), a.SeriesID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("按分配者店铺ID过滤", func(t *testing.T) {
|
||||
filters := map[string]interface{}{"allocator_shop_id": uint(1)}
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(1))
|
||||
for _, a := range result {
|
||||
assert.Equal(t, uint(1), a.AllocatorShopID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("按状态过滤-启用状态值为1", func(t *testing.T) {
|
||||
filters := map[string]interface{}{"status": 1}
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(2))
|
||||
for _, a := range result {
|
||||
assert.Equal(t, 1, a.Status)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("按状态过滤-启用", func(t *testing.T) {
|
||||
filters := map[string]interface{}{"status": constants.StatusEnabled}
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(2))
|
||||
for _, a := range result {
|
||||
assert.Equal(t, constants.StatusEnabled, a.Status)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("分页查询", func(t *testing.T) {
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 2}, nil)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(3))
|
||||
assert.LessOrEqual(t, len(result), 2)
|
||||
})
|
||||
|
||||
t.Run("默认分页选项", func(t *testing.T) {
|
||||
result, _, err := s.List(ctx, nil, nil)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestShopSeriesAllocationStore_UpdateStatus(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopSeriesAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocation := &model.ShopSeriesAllocation{
|
||||
ShopID: 20,
|
||||
SeriesID: 20,
|
||||
AllocatorShopID: 0,
|
||||
PricingMode: model.PricingModeFixed,
|
||||
PricingValue: 1000,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, allocation))
|
||||
|
||||
err := s.UpdateStatus(ctx, allocation.ID, constants.StatusDisabled, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
updated, err := s.GetByID(ctx, allocation.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, constants.StatusDisabled, updated.Status)
|
||||
assert.Equal(t, uint(1), updated.Updater)
|
||||
}
|
||||
|
||||
func TestShopSeriesAllocationStore_HasDependentAllocations(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
s := NewShopSeriesAllocationStore(tx)
|
||||
ctx := context.Background()
|
||||
|
||||
allocation := &model.ShopSeriesAllocation{
|
||||
ShopID: 30,
|
||||
SeriesID: 30,
|
||||
AllocatorShopID: 100,
|
||||
PricingMode: model.PricingModeFixed,
|
||||
PricingValue: 1000,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, allocation))
|
||||
|
||||
t.Run("检查存在的依赖分配", func(t *testing.T) {
|
||||
// 注意:这个测试依赖于数据库中存在特定的店铺层级关系
|
||||
// 由于测试环境可能没有这样的关系,我们只验证函数可以执行
|
||||
has, err := s.HasDependentAllocations(ctx, 100, 30)
|
||||
require.NoError(t, err)
|
||||
// 结果取决于数据库中的实际店铺关系
|
||||
assert.IsType(t, true, has)
|
||||
})
|
||||
|
||||
t.Run("检查不存在的依赖分配", func(t *testing.T) {
|
||||
has, err := s.HasDependentAllocations(ctx, 99999, 99999)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, has)
|
||||
})
|
||||
}
|
||||
53
internal/store/postgres/shop_series_commission_tier_store.go
Normal file
53
internal/store/postgres/shop_series_commission_tier_store.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ShopSeriesCommissionTierStore struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewShopSeriesCommissionTierStore(db *gorm.DB) *ShopSeriesCommissionTierStore {
|
||||
return &ShopSeriesCommissionTierStore{db: db}
|
||||
}
|
||||
|
||||
func (s *ShopSeriesCommissionTierStore) Create(ctx context.Context, tier *model.ShopSeriesCommissionTier) error {
|
||||
return s.db.WithContext(ctx).Create(tier).Error
|
||||
}
|
||||
|
||||
func (s *ShopSeriesCommissionTierStore) GetByID(ctx context.Context, id uint) (*model.ShopSeriesCommissionTier, error) {
|
||||
var tier model.ShopSeriesCommissionTier
|
||||
if err := s.db.WithContext(ctx).First(&tier, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tier, nil
|
||||
}
|
||||
|
||||
func (s *ShopSeriesCommissionTierStore) Update(ctx context.Context, tier *model.ShopSeriesCommissionTier) error {
|
||||
return s.db.WithContext(ctx).Save(tier).Error
|
||||
}
|
||||
|
||||
func (s *ShopSeriesCommissionTierStore) Delete(ctx context.Context, id uint) error {
|
||||
return s.db.WithContext(ctx).Delete(&model.ShopSeriesCommissionTier{}, id).Error
|
||||
}
|
||||
|
||||
func (s *ShopSeriesCommissionTierStore) ListByAllocationID(ctx context.Context, allocationID uint) ([]*model.ShopSeriesCommissionTier, error) {
|
||||
var tiers []*model.ShopSeriesCommissionTier
|
||||
if err := s.db.WithContext(ctx).
|
||||
Where("allocation_id = ?", allocationID).
|
||||
Order("threshold_value ASC").
|
||||
Find(&tiers).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tiers, nil
|
||||
}
|
||||
|
||||
func (s *ShopSeriesCommissionTierStore) DeleteByAllocationID(ctx context.Context, allocationID uint) error {
|
||||
return s.db.WithContext(ctx).
|
||||
Where("allocation_id = ?", allocationID).
|
||||
Delete(&model.ShopSeriesCommissionTier{}).Error
|
||||
}
|
||||
Reference in New Issue
Block a user