重构: 店铺套餐分配系统从加价模式改为返佣模式
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m18s

主要变更:
- 重构分配模型:从加价模式(pricing_mode/pricing_value)改为返佣模式(base_commission + tier_commission)
- 删除独立的 my_package 接口,统一到 /api/admin/packages(通过数据权限自动过滤)
- 新增批量分配和批量调价功能,支持事务和性能优化
- 新增配置版本管理,订单创建时锁定返佣配置
- 新增成本价历史记录,支持审计和纠纷处理
- 新增统计缓存系统(Redis + 异步任务),优化梯度返佣计算性能
- 删除冗余的梯度佣金独立 CRUD 接口(合并到分配配置中)
- 归档 3 个已完成的 OpenSpec changes 并同步 8 个新 capabilities 到 main specs

技术细节:
- 数据库迁移:000026_refactor_shop_package_allocation
- 新增 Store:AllocationConfigStore, PriceHistoryStore, CommissionStatsStore
- 新增 Service:BatchAllocationService, BatchPricingService, CommissionStatsService
- 新增异步任务:统计更新、定时同步、周期归档
- 测试覆盖:批量操作集成测试、梯度佣金 CRUD 清理验证

影响:
- API 变更:删除 4 个梯度 CRUD 接口(POST/GET/PUT/DELETE /:id/tiers)
- API 新增:批量分配、批量调价接口
- 数据模型:重构 shop_series_allocation 表结构
- 性能优化:批量操作使用 CreateInBatches,统计使用 Redis 缓存

相关文档:
- openspec/changes/archive/2026-01-28-refactor-shop-package-allocation/
- openspec/specs/agent-available-packages/
- openspec/specs/allocation-config-versioning/
- 等 8 个新 capability specs
This commit is contained in:
2026-01-28 17:11:55 +08:00
parent 23eb0307bb
commit 1da680a790
97 changed files with 6810 additions and 3622 deletions

View File

@@ -18,6 +18,7 @@ import (
type Service struct {
packageAllocationStore *postgres.ShopPackageAllocationStore
seriesAllocationStore *postgres.ShopSeriesAllocationStore
priceHistoryStore *postgres.ShopPackageAllocationPriceHistoryStore
shopStore *postgres.ShopStore
packageStore *postgres.PackageStore
}
@@ -25,12 +26,14 @@ type Service struct {
func New(
packageAllocationStore *postgres.ShopPackageAllocationStore,
seriesAllocationStore *postgres.ShopSeriesAllocationStore,
priceHistoryStore *postgres.ShopPackageAllocationPriceHistoryStore,
shopStore *postgres.ShopStore,
packageStore *postgres.PackageStore,
) *Service {
return &Service{
packageAllocationStore: packageAllocationStore,
seriesAllocationStore: seriesAllocationStore,
priceHistoryStore: priceHistoryStore,
shopStore: shopStore,
packageStore: packageStore,
}
@@ -271,3 +274,76 @@ func (s *Service) buildResponse(ctx context.Context, a *model.ShopPackageAllocat
UpdatedAt: a.UpdatedAt.Format(time.RFC3339),
}, nil
}
func (s *Service) UpdateCostPrice(ctx context.Context, id uint, newCostPrice int64, changeReason string) (*dto.ShopPackageAllocationResponse, error) {
currentUserID := middleware.GetUserIDFromContext(ctx)
if currentUserID == 0 {
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
}
allocation, err := s.packageAllocationStore.GetByID(ctx, id)
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, errors.New(errors.CodeNotFound, "分配记录不存在")
}
return nil, fmt.Errorf("获取分配记录失败: %w", err)
}
if allocation.CostPrice == newCostPrice {
return nil, errors.New(errors.CodeInvalidParam, "新成本价与当前成本价相同")
}
oldCostPrice := allocation.CostPrice
now := time.Now()
priceHistory := &model.ShopPackageAllocationPriceHistory{
AllocationID: allocation.ID,
OldCostPrice: oldCostPrice,
NewCostPrice: newCostPrice,
ChangeReason: changeReason,
ChangedBy: currentUserID,
EffectiveFrom: now,
}
if err := s.priceHistoryStore.Create(ctx, priceHistory); err != nil {
return nil, fmt.Errorf("创建价格历史记录失败: %w", err)
}
allocation.CostPrice = newCostPrice
allocation.Updater = currentUserID
if err := s.packageAllocationStore.Update(ctx, allocation); err != nil {
return nil, fmt.Errorf("更新成本价失败: %w", err)
}
shop, _ := s.shopStore.GetByID(ctx, allocation.ShopID)
pkg, _ := s.packageStore.GetByID(ctx, allocation.PackageID)
shopName := ""
packageName := ""
packageCode := ""
if shop != nil {
shopName = shop.ShopName
}
if pkg != nil {
packageName = pkg.PackageName
packageCode = pkg.PackageCode
}
return s.buildResponse(ctx, allocation, shopName, packageName, packageCode)
}
func (s *Service) GetPriceHistory(ctx context.Context, allocationID uint) ([]*model.ShopPackageAllocationPriceHistory, error) {
_, err := s.packageAllocationStore.GetByID(ctx, allocationID)
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, errors.New(errors.CodeNotFound, "分配记录不存在")
}
return nil, fmt.Errorf("获取分配记录失败: %w", err)
}
history, err := s.priceHistoryStore.ListByAllocation(ctx, allocationID)
if err != nil {
return nil, fmt.Errorf("获取价格历史失败: %w", err)
}
return history, nil
}