refactor: 一次性佣金配置从套餐级别提升到系列级别
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m29s

主要变更:
- 新增 tb_shop_series_allocation 表,存储系列级别的一次性佣金配置
- ShopPackageAllocation 移除 one_time_commission_amount 字段
- PackageSeries 新增 enable_one_time_commission 字段控制是否启用一次性佣金
- 新增 /api/admin/shop-series-allocations CRUD 接口
- 佣金计算逻辑改为从 ShopSeriesAllocation 获取一次性佣金金额
- 删除废弃的 ShopSeriesOneTimeCommissionTier 模型
- OpenAPI Tag '系列分配' 和 '单套餐分配' 合并为 '套餐分配'

迁移脚本:
- 000042: 重构佣金套餐模型
- 000043: 简化佣金分配
- 000044: 一次性佣金分配重构
- 000045: PackageSeries 添加 enable_one_time_commission 字段

测试:
- 新增验收测试 (shop_series_allocation, commission_calculation)
- 新增流程测试 (one_time_commission_chain)
- 删除过时的单元测试(已被验收测试覆盖)
This commit is contained in:
2026-02-04 14:28:44 +08:00
parent fba8e9e76b
commit b18ecfeb55
106 changed files with 9899 additions and 6608 deletions

View File

@@ -203,3 +203,12 @@ func (s *DeviceStore) ListBySeriesID(ctx context.Context, seriesID uint) ([]*mod
}
return devices, nil
}
func (s *DeviceStore) UpdateRechargeTrackingFields(ctx context.Context, deviceID uint, accumulatedJSON, triggeredJSON string) error {
return s.db.WithContext(ctx).Model(&model.Device{}).
Where("id = ?", deviceID).
Updates(map[string]interface{}{
"accumulated_recharge_by_series": accumulatedJSON,
"first_recharge_triggered_by_series": triggeredJSON,
}).Error
}

View File

@@ -401,3 +401,12 @@ func (s *IotCardStore) ListBySeriesID(ctx context.Context, seriesID uint) ([]*mo
}
return cards, nil
}
func (s *IotCardStore) UpdateRechargeTrackingFields(ctx context.Context, cardID uint, accumulatedJSON, triggeredJSON string) error {
return s.db.WithContext(ctx).Model(&model.IotCard{}).
Where("id = ?", cardID).
Updates(map[string]interface{}{
"accumulated_recharge_by_series": accumulatedJSON,
"first_recharge_triggered_by_series": triggeredJSON,
}).Error
}

View File

@@ -69,6 +69,9 @@ func (s *PackageSeriesStore) List(ctx context.Context, opts *store.QueryOptions,
if status, ok := filters["status"]; ok {
query = query.Where("status = ?", status)
}
if enableOneTime, ok := filters["enable_one_time_commission"].(bool); ok {
query = query.Where("enable_one_time_commission = ?", enableOneTime)
}
if err := query.Count(&total).Error; err != nil {
return nil, 0, err

View File

@@ -18,17 +18,16 @@ func TestPackageStore_Create(t *testing.T) {
ctx := context.Background()
pkg := &model.Package{
PackageCode: "PKG_TEST_001",
PackageName: "测试套餐",
SeriesID: 1,
PackageType: "formal",
DurationMonths: 1,
DataType: "real",
RealDataMB: 1024,
DataAmountMB: 1024,
Price: 9900,
Status: constants.StatusEnabled,
ShelfStatus: 1,
PackageCode: "PKG_TEST_001",
PackageName: "测试套餐",
SeriesID: 1,
PackageType: "formal",
DurationMonths: 1,
RealDataMB: 1024,
CostPrice: 9900,
SuggestedRetailPrice: 12800,
Status: constants.StatusEnabled,
ShelfStatus: 1,
}
err := s.Create(ctx, pkg)
@@ -47,12 +46,12 @@ func TestPackageStore_GetByID(t *testing.T) {
SeriesID: 1,
PackageType: "formal",
DurationMonths: 1,
DataType: "real",
RealDataMB: 2048,
DataAmountMB: 2048,
Price: 19900,
Status: constants.StatusEnabled,
ShelfStatus: 1,
RealDataMB: 2048,
CostPrice: 19900,
SuggestedRetailPrice: 25800,
Status: constants.StatusEnabled,
ShelfStatus: 1,
}
require.NoError(t, s.Create(ctx, pkg))
@@ -79,12 +78,12 @@ func TestPackageStore_GetByCode(t *testing.T) {
SeriesID: 1,
PackageType: "formal",
DurationMonths: 1,
DataType: "real",
RealDataMB: 3072,
DataAmountMB: 3072,
Price: 29900,
Status: constants.StatusEnabled,
ShelfStatus: 1,
RealDataMB: 3072,
CostPrice: 29900,
SuggestedRetailPrice: 39800,
Status: constants.StatusEnabled,
ShelfStatus: 1,
}
require.NoError(t, s.Create(ctx, pkg))
@@ -111,24 +110,24 @@ func TestPackageStore_Update(t *testing.T) {
SeriesID: 1,
PackageType: "formal",
DurationMonths: 1,
DataType: "real",
RealDataMB: 4096,
DataAmountMB: 4096,
Price: 39900,
Status: constants.StatusEnabled,
ShelfStatus: 1,
RealDataMB: 4096,
CostPrice: 39900,
SuggestedRetailPrice: 49800,
Status: constants.StatusEnabled,
ShelfStatus: 1,
}
require.NoError(t, s.Create(ctx, pkg))
pkg.PackageName = "测试套餐4-更新"
pkg.Price = 49900
pkg.CostPrice = 49900
err := s.Update(ctx, pkg)
require.NoError(t, err)
updated, err := s.GetByID(ctx, pkg.ID)
require.NoError(t, err)
assert.Equal(t, "测试套餐4-更新", updated.PackageName)
assert.Equal(t, int64(49900), updated.Price)
assert.Equal(t, int64(49900), updated.CostPrice)
}
func TestPackageStore_Delete(t *testing.T) {
@@ -142,12 +141,12 @@ func TestPackageStore_Delete(t *testing.T) {
SeriesID: 1,
PackageType: "formal",
DurationMonths: 1,
DataType: "real",
RealDataMB: 1024,
DataAmountMB: 1024,
Price: 9900,
Status: constants.StatusEnabled,
ShelfStatus: 1,
RealDataMB: 1024,
CostPrice: 9900,
SuggestedRetailPrice: 12800,
Status: constants.StatusEnabled,
ShelfStatus: 1,
}
require.NoError(t, s.Create(ctx, pkg))
@@ -170,12 +169,12 @@ func TestPackageStore_List(t *testing.T) {
SeriesID: 1,
PackageType: "formal",
DurationMonths: 1,
DataType: "real",
RealDataMB: 1024,
DataAmountMB: 1024,
Price: 9900,
Status: constants.StatusEnabled,
ShelfStatus: 1,
RealDataMB: 1024,
CostPrice: 9900,
SuggestedRetailPrice: 12800,
Status: constants.StatusEnabled,
ShelfStatus: 1,
},
{
PackageCode: "LIST_P_002",
@@ -183,12 +182,12 @@ func TestPackageStore_List(t *testing.T) {
SeriesID: 2,
PackageType: "formal",
DurationMonths: 12,
DataType: "real",
RealDataMB: 10240,
DataAmountMB: 10240,
Price: 99900,
Status: constants.StatusEnabled,
ShelfStatus: 1,
RealDataMB: 10240,
CostPrice: 99900,
SuggestedRetailPrice: 129800,
Status: constants.StatusEnabled,
ShelfStatus: 1,
},
{
PackageCode: "LIST_P_003",
@@ -196,12 +195,12 @@ func TestPackageStore_List(t *testing.T) {
SeriesID: 3,
PackageType: "addon",
DurationMonths: 1,
DataType: "virtual",
VirtualDataMB: 5120,
DataAmountMB: 5120,
Price: 4900,
Status: constants.StatusEnabled,
ShelfStatus: 2,
VirtualDataMB: 5120,
CostPrice: 4900,
SuggestedRetailPrice: 6800,
Status: constants.StatusEnabled,
ShelfStatus: 2,
},
}
for _, pkg := range pkgList {
@@ -286,12 +285,12 @@ func TestPackageStore_UpdateStatus(t *testing.T) {
SeriesID: 1,
PackageType: "formal",
DurationMonths: 1,
DataType: "real",
RealDataMB: 1024,
DataAmountMB: 1024,
Price: 9900,
Status: constants.StatusEnabled,
ShelfStatus: 1,
RealDataMB: 1024,
CostPrice: 9900,
SuggestedRetailPrice: 12800,
Status: constants.StatusEnabled,
ShelfStatus: 1,
}
require.NoError(t, s.Create(ctx, pkg))
@@ -314,12 +313,12 @@ func TestPackageStore_UpdateShelfStatus(t *testing.T) {
SeriesID: 1,
PackageType: "formal",
DurationMonths: 1,
DataType: "real",
RealDataMB: 1024,
DataAmountMB: 1024,
Price: 9900,
Status: constants.StatusEnabled,
ShelfStatus: 1,
RealDataMB: 1024,
CostPrice: 9900,
SuggestedRetailPrice: 12800,
Status: constants.StatusEnabled,
ShelfStatus: 1,
}
require.NoError(t, s.Create(ctx, pkg))

View File

@@ -56,8 +56,11 @@ func (s *ShopPackageAllocationStore) List(ctx context.Context, opts *store.Query
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 seriesAllocationID, ok := filters["series_allocation_id"].(uint); ok && seriesAllocationID > 0 {
query = query.Where("series_allocation_id = ?", seriesAllocationID)
}
if allocatorShopID, ok := filters["allocator_shop_id"].(uint); ok {
query = query.Where("allocator_shop_id = ?", allocatorShopID)
}
if status, ok := filters["status"].(int); ok && status > 0 {
query = query.Where("status = ?", status)
@@ -102,8 +105,33 @@ func (s *ShopPackageAllocationStore) GetByShopID(ctx context.Context, shopID uin
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
func (s *ShopPackageAllocationStore) GetByShopAndPackages(ctx context.Context, shopID uint, packageIDs []uint) ([]*model.ShopPackageAllocation, error) {
var allocations []*model.ShopPackageAllocation
if err := s.db.WithContext(ctx).
Where("shop_id = ? AND package_id IN ? AND status = 1", shopID, packageIDs).
Find(&allocations).Error; err != nil {
return nil, err
}
return allocations, nil
}
func (s *ShopPackageAllocationStore) GetBySeriesAllocationID(ctx context.Context, seriesAllocationID uint) ([]*model.ShopPackageAllocation, error) {
var allocations []*model.ShopPackageAllocation
if err := s.db.WithContext(ctx).
Where("series_allocation_id = ? AND status = 1", seriesAllocationID).
Find(&allocations).Error; err != nil {
return nil, err
}
return allocations, nil
}
func (s *ShopPackageAllocationStore) CountBySeriesAllocationID(ctx context.Context, seriesAllocationID uint) (int64, error) {
var count int64
if err := s.db.WithContext(ctx).
Model(&model.ShopPackageAllocation{}).
Where("series_allocation_id = ?", seriesAllocationID).
Count(&count).Error; err != nil {
return 0, err
}
return count, nil
}

View File

@@ -18,11 +18,11 @@ func TestShopPackageAllocationStore_Create(t *testing.T) {
ctx := context.Background()
allocation := &model.ShopPackageAllocation{
ShopID: 1,
PackageID: 1,
AllocationID: 1,
CostPrice: 5000,
Status: constants.StatusEnabled,
ShopID: 1,
PackageID: 1,
AllocatorShopID: 0,
CostPrice: 5000,
Status: constants.StatusEnabled,
}
err := s.Create(ctx, allocation)
@@ -36,11 +36,11 @@ func TestShopPackageAllocationStore_GetByID(t *testing.T) {
ctx := context.Background()
allocation := &model.ShopPackageAllocation{
ShopID: 2,
PackageID: 2,
AllocationID: 1,
CostPrice: 6000,
Status: constants.StatusEnabled,
ShopID: 2,
PackageID: 2,
AllocatorShopID: 0,
CostPrice: 6000,
Status: constants.StatusEnabled,
}
require.NoError(t, s.Create(ctx, allocation))
@@ -64,11 +64,11 @@ func TestShopPackageAllocationStore_GetByShopAndPackage(t *testing.T) {
ctx := context.Background()
allocation := &model.ShopPackageAllocation{
ShopID: 3,
PackageID: 3,
AllocationID: 1,
CostPrice: 7000,
Status: constants.StatusEnabled,
ShopID: 3,
PackageID: 3,
AllocatorShopID: 0,
CostPrice: 7000,
Status: constants.StatusEnabled,
}
require.NoError(t, s.Create(ctx, allocation))
@@ -92,11 +92,11 @@ func TestShopPackageAllocationStore_Update(t *testing.T) {
ctx := context.Background()
allocation := &model.ShopPackageAllocation{
ShopID: 4,
PackageID: 4,
AllocationID: 1,
CostPrice: 5000,
Status: constants.StatusEnabled,
ShopID: 4,
PackageID: 4,
AllocatorShopID: 0,
CostPrice: 5000,
Status: constants.StatusEnabled,
}
require.NoError(t, s.Create(ctx, allocation))
@@ -115,11 +115,11 @@ func TestShopPackageAllocationStore_Delete(t *testing.T) {
ctx := context.Background()
allocation := &model.ShopPackageAllocation{
ShopID: 5,
PackageID: 5,
AllocationID: 1,
CostPrice: 5000,
Status: constants.StatusEnabled,
ShopID: 5,
PackageID: 5,
AllocatorShopID: 0,
CostPrice: 5000,
Status: constants.StatusEnabled,
}
require.NoError(t, s.Create(ctx, allocation))
@@ -136,9 +136,9 @@ func TestShopPackageAllocationStore_List(t *testing.T) {
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},
{ShopID: 10, PackageID: 10, AllocatorShopID: 0, CostPrice: 5000, Status: constants.StatusEnabled},
{ShopID: 11, PackageID: 11, AllocatorShopID: 0, CostPrice: 6000, Status: constants.StatusEnabled},
{ShopID: 12, PackageID: 12, AllocatorShopID: 10, CostPrice: 7000, Status: constants.StatusEnabled},
}
for _, a := range allocations {
require.NoError(t, s.Create(ctx, a))
@@ -173,16 +173,6 @@ func TestShopPackageAllocationStore_List(t *testing.T) {
}
})
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)
@@ -223,11 +213,11 @@ func TestShopPackageAllocationStore_UpdateStatus(t *testing.T) {
ctx := context.Background()
allocation := &model.ShopPackageAllocation{
ShopID: 20,
PackageID: 20,
AllocationID: 1,
CostPrice: 5000,
Status: constants.StatusEnabled,
ShopID: 20,
PackageID: 20,
AllocatorShopID: 0,
CostPrice: 5000,
Status: constants.StatusEnabled,
}
require.NoError(t, s.Create(ctx, allocation))

View File

@@ -1,65 +0,0 @@
package postgres
import (
"context"
"time"
"github.com/break/junhong_cmp_fiber/internal/model"
"gorm.io/gorm"
)
type ShopSeriesAllocationConfigStore struct {
db *gorm.DB
}
func NewShopSeriesAllocationConfigStore(db *gorm.DB) *ShopSeriesAllocationConfigStore {
return &ShopSeriesAllocationConfigStore{db: db}
}
func (s *ShopSeriesAllocationConfigStore) Create(ctx context.Context, config *model.ShopSeriesAllocationConfig) error {
return s.db.WithContext(ctx).Create(config).Error
}
func (s *ShopSeriesAllocationConfigStore) GetEffective(ctx context.Context, allocationID uint, at time.Time) (*model.ShopSeriesAllocationConfig, error) {
var config model.ShopSeriesAllocationConfig
err := s.db.WithContext(ctx).
Where("allocation_id = ?", allocationID).
Where("effective_from <= ?", at).
Where("effective_to IS NULL OR effective_to > ?", at).
First(&config).Error
if err != nil {
return nil, err
}
return &config, nil
}
func (s *ShopSeriesAllocationConfigStore) GetLatestVersion(ctx context.Context, allocationID uint) (*model.ShopSeriesAllocationConfig, error) {
var config model.ShopSeriesAllocationConfig
err := s.db.WithContext(ctx).
Where("allocation_id = ?", allocationID).
Order("version DESC").
First(&config).Error
if err != nil {
return nil, err
}
return &config, nil
}
func (s *ShopSeriesAllocationConfigStore) InvalidateCurrent(ctx context.Context, allocationID uint, effectiveTo time.Time) error {
return s.db.WithContext(ctx).
Model(&model.ShopSeriesAllocationConfig{}).
Where("allocation_id = ? AND effective_to IS NULL", allocationID).
Update("effective_to", effectiveTo).Error
}
func (s *ShopSeriesAllocationConfigStore) List(ctx context.Context, allocationID uint) ([]*model.ShopSeriesAllocationConfig, error) {
var configs []*model.ShopSeriesAllocationConfig
err := s.db.WithContext(ctx).
Where("allocation_id = ?", allocationID).
Order("version DESC").
Find(&configs).Error
if err != nil {
return nil, err
}
return configs, nil
}

View File

@@ -30,7 +30,9 @@ func (s *ShopSeriesAllocationStore) GetByID(ctx context.Context, id uint) (*mode
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 {
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
@@ -56,7 +58,7 @@ func (s *ShopSeriesAllocationStore) List(ctx context.Context, opts *store.QueryO
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 {
if allocatorShopID, ok := filters["allocator_shop_id"].(uint); ok {
query = query.Where("allocator_shop_id = ?", allocatorShopID)
}
if status, ok := filters["status"].(int); ok && status > 0 {
@@ -75,6 +77,8 @@ func (s *ShopSeriesAllocationStore) List(ctx context.Context, opts *store.QueryO
if opts.OrderBy != "" {
query = query.Order(opts.OrderBy)
} else {
query = query.Order("id DESC")
}
if err := query.Find(&allocations).Error; err != nil {
@@ -94,31 +98,58 @@ func (s *ShopSeriesAllocationStore) UpdateStatus(ctx context.Context, id uint, s
}).Error
}
func (s *ShopSeriesAllocationStore) HasDependentAllocations(ctx context.Context, allocatorShopID, seriesID uint) (bool, error) {
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) CountBySeriesID(ctx context.Context, seriesID uint) (int64, error) {
var count int64
err := s.db.WithContext(ctx).
if 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 {
Count(&count).Error; err != nil {
return 0, err
}
return count, nil
}
func (s *ShopSeriesAllocationStore) ExistsByShopAndSeries(ctx context.Context, shopID, seriesID uint) (bool, error) {
var count int64
if err := s.db.WithContext(ctx).
Model(&model.ShopSeriesAllocation{}).
Where("shop_id = ? AND series_id = ?", shopID, seriesID).
Count(&count).Error; err != nil {
return false, err
}
return count > 0, nil
}
func (s *ShopSeriesAllocationStore) GetByShopID(ctx context.Context, shopID uint) ([]*model.ShopSeriesAllocation, error) {
func (s *ShopSeriesAllocationStore) GetByAllocatorShopID(ctx context.Context, allocatorShopID 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 {
if err := s.db.WithContext(ctx).
Where("allocator_shop_id = ? AND status = 1", allocatorShopID).
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 {
func (s *ShopSeriesAllocationStore) GetIDsByShopIDsAndSeries(ctx context.Context, shopIDs []uint, seriesID uint) ([]uint, error) {
if len(shopIDs) == 0 {
return nil, nil
}
var ids []uint
if err := s.db.WithContext(ctx).
Model(&model.ShopSeriesAllocation{}).
Where("shop_id IN ? AND series_id = ? AND status = 1", shopIDs, seriesID).
Pluck("id", &ids).Error; err != nil {
return nil, err
}
return allocations, nil
return ids, nil
}

View File

@@ -1,114 +0,0 @@
package postgres
import (
"context"
"testing"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/tests/testutils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
)
func TestShopSeriesAllocationStore_GetByShopAndSeries(t *testing.T) {
tx := testutils.NewTestTransaction(t)
ctx := context.Background()
s := NewShopSeriesAllocationStore(tx)
// 创建测试数据
allocation := &model.ShopSeriesAllocation{
ShopID: 1,
SeriesID: 100,
AllocatorShopID: 0,
Status: 1,
}
require.NoError(t, s.Create(ctx, allocation))
t.Run("查询存在的分配", func(t *testing.T) {
result, err := s.GetByShopAndSeries(ctx, 1, 100)
require.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, uint(1), result.ShopID)
assert.Equal(t, uint(100), result.SeriesID)
})
t.Run("查询不存在的分配", func(t *testing.T) {
result, err := s.GetByShopAndSeries(ctx, 999, 999)
assert.Error(t, err)
assert.Equal(t, gorm.ErrRecordNotFound, err)
assert.Nil(t, result)
})
}
func TestShopSeriesAllocationStore_Create(t *testing.T) {
tx := testutils.NewTestTransaction(t)
ctx := context.Background()
s := NewShopSeriesAllocationStore(tx)
allocation := &model.ShopSeriesAllocation{
ShopID: 1,
SeriesID: 100,
AllocatorShopID: 0,
Status: 1,
}
err := s.Create(ctx, allocation)
require.NoError(t, err)
assert.NotZero(t, allocation.ID)
}
func TestShopSeriesAllocationStore_GetByID(t *testing.T) {
tx := testutils.NewTestTransaction(t)
ctx := context.Background()
s := NewShopSeriesAllocationStore(tx)
allocation := &model.ShopSeriesAllocation{
ShopID: 1,
SeriesID: 100,
AllocatorShopID: 0,
Status: 1,
}
require.NoError(t, s.Create(ctx, allocation))
result, err := s.GetByID(ctx, allocation.ID)
require.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, allocation.ID, result.ID)
}
func TestShopSeriesAllocationStore_List(t *testing.T) {
tx := testutils.NewTestTransaction(t)
ctx := context.Background()
s := NewShopSeriesAllocationStore(tx)
// 创建测试数据
allocations := []*model.ShopSeriesAllocation{
{ShopID: 1, SeriesID: 100, AllocatorShopID: 0, Status: 1},
{ShopID: 1, SeriesID: 101, AllocatorShopID: 0, Status: 1},
{ShopID: 2, SeriesID: 100, AllocatorShopID: 0, Status: 1},
}
for _, a := range allocations {
require.NoError(t, s.Create(ctx, a))
}
t.Run("按店铺ID过滤", func(t *testing.T) {
filters := map[string]interface{}{"shop_id": uint(1)}
result, total, err := s.List(ctx, nil, filters)
require.NoError(t, err)
assert.Equal(t, int64(2), total)
assert.Len(t, result, 2)
})
t.Run("按系列ID过滤", func(t *testing.T) {
filters := map[string]interface{}{"series_id": uint(100)}
result, total, err := s.List(ctx, nil, filters)
require.NoError(t, err)
assert.Equal(t, int64(2), total)
assert.Len(t, result, 2)
})
}

View File

@@ -68,3 +68,29 @@ func (s *ShopSeriesCommissionStatsStore) ListExpired(ctx context.Context, before
}
return stats, nil
}
func (s *ShopSeriesCommissionStatsStore) GetAggregatedStats(ctx context.Context, allocationIDs []uint, periodType string, now time.Time) (int64, int64, error) {
if len(allocationIDs) == 0 {
return 0, 0, nil
}
var result struct {
TotalSalesCount int64
TotalSalesAmount int64
}
err := s.db.WithContext(ctx).
Model(&model.ShopSeriesCommissionStats{}).
Select("COALESCE(SUM(total_sales_count), 0) as total_sales_count, COALESCE(SUM(total_sales_amount), 0) as total_sales_amount").
Where("allocation_id IN ?", allocationIDs).
Where("period_type = ?", periodType).
Where("period_start <= ? AND period_end >= ?", now, now).
Where("status = ?", model.StatsStatusActive).
Scan(&result).Error
if err != nil {
return 0, 0, err
}
return result.TotalSalesCount, result.TotalSalesAmount, nil
}

View File

@@ -1,61 +0,0 @@
package postgres
import (
"context"
"github.com/break/junhong_cmp_fiber/internal/model"
"gorm.io/gorm"
)
type ShopSeriesOneTimeCommissionTierStore struct {
db *gorm.DB
}
func NewShopSeriesOneTimeCommissionTierStore(db *gorm.DB) *ShopSeriesOneTimeCommissionTierStore {
return &ShopSeriesOneTimeCommissionTierStore{db: db}
}
func (s *ShopSeriesOneTimeCommissionTierStore) Create(ctx context.Context, tier *model.ShopSeriesOneTimeCommissionTier) error {
return s.db.WithContext(ctx).Create(tier).Error
}
func (s *ShopSeriesOneTimeCommissionTierStore) BatchCreate(ctx context.Context, tiers []*model.ShopSeriesOneTimeCommissionTier) error {
if len(tiers) == 0 {
return nil
}
return s.db.WithContext(ctx).Create(&tiers).Error
}
func (s *ShopSeriesOneTimeCommissionTierStore) GetByID(ctx context.Context, id uint) (*model.ShopSeriesOneTimeCommissionTier, error) {
var tier model.ShopSeriesOneTimeCommissionTier
if err := s.db.WithContext(ctx).First(&tier, id).Error; err != nil {
return nil, err
}
return &tier, nil
}
func (s *ShopSeriesOneTimeCommissionTierStore) Update(ctx context.Context, tier *model.ShopSeriesOneTimeCommissionTier) error {
return s.db.WithContext(ctx).Save(tier).Error
}
func (s *ShopSeriesOneTimeCommissionTierStore) Delete(ctx context.Context, id uint) error {
return s.db.WithContext(ctx).Delete(&model.ShopSeriesOneTimeCommissionTier{}, id).Error
}
func (s *ShopSeriesOneTimeCommissionTierStore) ListByAllocationID(ctx context.Context, allocationID uint) ([]*model.ShopSeriesOneTimeCommissionTier, error) {
var tiers []*model.ShopSeriesOneTimeCommissionTier
if err := s.db.WithContext(ctx).
Where("allocation_id = ?", allocationID).
Where("status = ?", 1).
Order("threshold_value ASC").
Find(&tiers).Error; err != nil {
return nil, err
}
return tiers, nil
}
func (s *ShopSeriesOneTimeCommissionTierStore) DeleteByAllocationID(ctx context.Context, allocationID uint) error {
return s.db.WithContext(ctx).
Where("allocation_id = ?", allocationID).
Delete(&model.ShopSeriesOneTimeCommissionTier{}).Error
}