refactor: 一次性佣金配置从套餐级别提升到系列级别
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m29s
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:
@@ -10,34 +10,29 @@ import (
|
||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
pkggorm "github.com/break/junhong_cmp_fiber/pkg/gorm"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/middleware"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
allocationStore *postgres.ShopSeriesAllocationStore
|
||||
configStore *postgres.ShopSeriesAllocationConfigStore
|
||||
oneTimeCommissionTierStore *postgres.ShopSeriesOneTimeCommissionTierStore
|
||||
shopStore *postgres.ShopStore
|
||||
packageSeriesStore *postgres.PackageSeriesStore
|
||||
packageStore *postgres.PackageStore
|
||||
seriesAllocationStore *postgres.ShopSeriesAllocationStore
|
||||
packageAllocationStore *postgres.ShopPackageAllocationStore
|
||||
shopStore *postgres.ShopStore
|
||||
packageSeriesStore *postgres.PackageSeriesStore
|
||||
}
|
||||
|
||||
func New(
|
||||
allocationStore *postgres.ShopSeriesAllocationStore,
|
||||
configStore *postgres.ShopSeriesAllocationConfigStore,
|
||||
oneTimeCommissionTierStore *postgres.ShopSeriesOneTimeCommissionTierStore,
|
||||
seriesAllocationStore *postgres.ShopSeriesAllocationStore,
|
||||
packageAllocationStore *postgres.ShopPackageAllocationStore,
|
||||
shopStore *postgres.ShopStore,
|
||||
packageSeriesStore *postgres.PackageSeriesStore,
|
||||
packageStore *postgres.PackageStore,
|
||||
) *Service {
|
||||
return &Service{
|
||||
allocationStore: allocationStore,
|
||||
configStore: configStore,
|
||||
oneTimeCommissionTierStore: oneTimeCommissionTierStore,
|
||||
shopStore: shopStore,
|
||||
packageSeriesStore: packageSeriesStore,
|
||||
packageStore: packageStore,
|
||||
seriesAllocationStore: seriesAllocationStore,
|
||||
packageAllocationStore: packageAllocationStore,
|
||||
shopStore: shopStore,
|
||||
packageSeriesStore: packageSeriesStore,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,16 +57,9 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateShopSeriesAllocatio
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取店铺失败")
|
||||
}
|
||||
|
||||
isPlatformUser := userType == constants.UserTypeSuperAdmin || userType == constants.UserTypePlatform
|
||||
isFirstLevelShop := targetShop.ParentID == nil
|
||||
|
||||
if isPlatformUser {
|
||||
if !isFirstLevelShop {
|
||||
return nil, errors.New(errors.CodeForbidden, "平台只能为一级店铺分配套餐")
|
||||
}
|
||||
} else {
|
||||
if isFirstLevelShop || *targetShop.ParentID != allocatorShopID {
|
||||
return nil, errors.New(errors.CodeForbidden, "只能为直属下级分配套餐")
|
||||
if userType == constants.UserTypeAgent {
|
||||
if targetShop.ParentID == nil || *targetShop.ParentID != allocatorShopID {
|
||||
return nil, errors.New(errors.CodeForbidden, "只能为直属下级分配套餐系列")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,49 +71,54 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateShopSeriesAllocatio
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取套餐系列失败")
|
||||
}
|
||||
|
||||
if userType == constants.UserTypeAgent {
|
||||
myAllocation, err := s.allocationStore.GetByShopAndSeries(ctx, allocatorShopID, req.SeriesID)
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "检查分配权限失败")
|
||||
}
|
||||
if myAllocation == nil || myAllocation.Status != constants.StatusEnabled {
|
||||
return nil, errors.New(errors.CodeForbidden, "您没有该套餐系列的分配权限")
|
||||
}
|
||||
// 检查是否已存在分配(跳过数据权限过滤,避免误判)
|
||||
skipCtx := pkggorm.SkipDataPermission(ctx)
|
||||
exists, err := s.seriesAllocationStore.ExistsByShopAndSeries(skipCtx, req.ShopID, req.SeriesID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "检查分配记录失败")
|
||||
}
|
||||
|
||||
existing, _ := s.allocationStore.GetByShopAndSeries(ctx, req.ShopID, req.SeriesID)
|
||||
if existing != nil {
|
||||
if exists {
|
||||
return nil, errors.New(errors.CodeConflict, "该店铺已分配此套餐系列")
|
||||
}
|
||||
|
||||
if err := s.validateOneTimeCommissionConfig(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allocation := &model.ShopSeriesAllocation{
|
||||
ShopID: req.ShopID,
|
||||
SeriesID: req.SeriesID,
|
||||
AllocatorShopID: allocatorShopID,
|
||||
BaseCommissionMode: req.BaseCommission.Mode,
|
||||
BaseCommissionValue: req.BaseCommission.Value,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
|
||||
// 处理一次性佣金配置
|
||||
allocation.EnableOneTimeCommission = req.EnableOneTimeCommission
|
||||
if req.EnableOneTimeCommission && req.OneTimeCommissionConfig != nil {
|
||||
cfg := req.OneTimeCommissionConfig
|
||||
allocation.OneTimeCommissionType = cfg.Type
|
||||
allocation.OneTimeCommissionTrigger = cfg.Trigger
|
||||
allocation.OneTimeCommissionThreshold = cfg.Threshold
|
||||
// fixed 类型需要保存 mode 和 value
|
||||
if cfg.Type == model.OneTimeCommissionTypeFixed {
|
||||
allocation.OneTimeCommissionMode = cfg.Mode
|
||||
allocation.OneTimeCommissionValue = cfg.Value
|
||||
// 代理用户:检查自己是否有该系列的分配权限,且金额不能超过上级给的上限
|
||||
// 平台用户:无上限限制,可自由设定金额
|
||||
if userType == constants.UserTypeAgent {
|
||||
allocatorAllocation, err := s.seriesAllocationStore.GetByShopAndSeries(skipCtx, allocatorShopID, req.SeriesID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeForbidden, "您没有该套餐系列的分配权限")
|
||||
}
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取分配权限失败")
|
||||
}
|
||||
if req.OneTimeCommissionAmount > allocatorAllocation.OneTimeCommissionAmount {
|
||||
return nil, errors.New(errors.CodeInvalidParam, "一次性佣金金额不能超过您的分配上限")
|
||||
}
|
||||
}
|
||||
|
||||
// 处理强充配置
|
||||
allocation := &model.ShopSeriesAllocation{
|
||||
ShopID: req.ShopID,
|
||||
SeriesID: req.SeriesID,
|
||||
AllocatorShopID: allocatorShopID,
|
||||
OneTimeCommissionAmount: req.OneTimeCommissionAmount,
|
||||
EnableOneTimeCommission: false,
|
||||
OneTimeCommissionTrigger: "",
|
||||
OneTimeCommissionThreshold: 0,
|
||||
EnableForceRecharge: false,
|
||||
ForceRechargeAmount: 0,
|
||||
ForceRechargeTriggerType: 2,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
|
||||
if req.EnableOneTimeCommission != nil {
|
||||
allocation.EnableOneTimeCommission = *req.EnableOneTimeCommission
|
||||
}
|
||||
if req.OneTimeCommissionTrigger != "" {
|
||||
allocation.OneTimeCommissionTrigger = req.OneTimeCommissionTrigger
|
||||
}
|
||||
if req.OneTimeCommissionThreshold != nil {
|
||||
allocation.OneTimeCommissionThreshold = *req.OneTimeCommissionThreshold
|
||||
}
|
||||
if req.EnableForceRecharge != nil {
|
||||
allocation.EnableForceRecharge = *req.EnableForceRecharge
|
||||
}
|
||||
@@ -138,23 +131,15 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateShopSeriesAllocatio
|
||||
|
||||
allocation.Creator = currentUserID
|
||||
|
||||
if err := s.allocationStore.Create(ctx, allocation); err != nil {
|
||||
if err := s.seriesAllocationStore.Create(ctx, allocation); err != nil {
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "创建分配失败")
|
||||
}
|
||||
|
||||
// 如果是梯度类型,保存梯度配置
|
||||
if req.EnableOneTimeCommission && req.OneTimeCommissionConfig != nil &&
|
||||
req.OneTimeCommissionConfig.Type == model.OneTimeCommissionTypeTiered {
|
||||
if err := s.saveOneTimeCommissionTiers(ctx, allocation.ID, req.OneTimeCommissionConfig.Tiers, currentUserID); err != nil {
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "创建一次性佣金梯度配置失败")
|
||||
}
|
||||
}
|
||||
|
||||
return s.buildResponse(ctx, allocation, targetShop.ShopName, series.SeriesName)
|
||||
return s.buildResponse(ctx, allocation, targetShop.ShopName, series.SeriesName, series.SeriesCode)
|
||||
}
|
||||
|
||||
func (s *Service) Get(ctx context.Context, id uint) (*dto.ShopSeriesAllocationResponse, error) {
|
||||
allocation, err := s.allocationStore.GetByID(ctx, id)
|
||||
allocation, err := s.seriesAllocationStore.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeNotFound, "分配记录不存在")
|
||||
@@ -167,14 +152,16 @@ func (s *Service) Get(ctx context.Context, id uint) (*dto.ShopSeriesAllocationRe
|
||||
|
||||
shopName := ""
|
||||
seriesName := ""
|
||||
seriesCode := ""
|
||||
if shop != nil {
|
||||
shopName = shop.ShopName
|
||||
}
|
||||
if series != nil {
|
||||
seriesName = series.SeriesName
|
||||
seriesCode = series.SeriesCode
|
||||
}
|
||||
|
||||
return s.buildResponse(ctx, allocation, shopName, seriesName)
|
||||
return s.buildResponse(ctx, allocation, shopName, seriesName, seriesCode)
|
||||
}
|
||||
|
||||
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateShopSeriesAllocationRequest) (*dto.ShopSeriesAllocationResponse, error) {
|
||||
@@ -183,7 +170,10 @@ func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateShopSeries
|
||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
allocation, err := s.allocationStore.GetByID(ctx, id)
|
||||
userType := middleware.GetUserTypeFromContext(ctx)
|
||||
allocatorShopID := middleware.GetShopIDFromContext(ctx)
|
||||
|
||||
allocation, err := s.seriesAllocationStore.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeNotFound, "分配记录不存在")
|
||||
@@ -191,52 +181,27 @@ func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateShopSeries
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
||||
}
|
||||
|
||||
configChanged := false
|
||||
if req.BaseCommission != nil {
|
||||
if allocation.BaseCommissionMode != req.BaseCommission.Mode ||
|
||||
allocation.BaseCommissionValue != req.BaseCommission.Value {
|
||||
configChanged = true
|
||||
if req.OneTimeCommissionAmount != nil {
|
||||
newAmount := *req.OneTimeCommissionAmount
|
||||
if userType == constants.UserTypeAgent {
|
||||
allocatorAllocation, err := s.seriesAllocationStore.GetByShopAndSeries(ctx, allocatorShopID, allocation.SeriesID)
|
||||
if err == nil && allocatorAllocation != nil {
|
||||
if newAmount > allocatorAllocation.OneTimeCommissionAmount {
|
||||
return nil, errors.New(errors.CodeInvalidParam, "一次性佣金金额不能超过您的分配上限")
|
||||
}
|
||||
}
|
||||
}
|
||||
allocation.BaseCommissionMode = req.BaseCommission.Mode
|
||||
allocation.BaseCommissionValue = req.BaseCommission.Value
|
||||
allocation.OneTimeCommissionAmount = newAmount
|
||||
}
|
||||
|
||||
enableOneTimeCommission := allocation.EnableOneTimeCommission
|
||||
if req.EnableOneTimeCommission != nil {
|
||||
enableOneTimeCommission = *req.EnableOneTimeCommission
|
||||
}
|
||||
if err := s.validateOneTimeCommissionConfigForUpdate(enableOneTimeCommission, req.OneTimeCommissionConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oneTimeCommissionChanged := false
|
||||
if req.EnableOneTimeCommission != nil {
|
||||
if allocation.EnableOneTimeCommission != *req.EnableOneTimeCommission {
|
||||
oneTimeCommissionChanged = true
|
||||
}
|
||||
allocation.EnableOneTimeCommission = *req.EnableOneTimeCommission
|
||||
}
|
||||
if req.OneTimeCommissionConfig != nil && allocation.EnableOneTimeCommission {
|
||||
cfg := req.OneTimeCommissionConfig
|
||||
if allocation.OneTimeCommissionType != cfg.Type ||
|
||||
allocation.OneTimeCommissionTrigger != cfg.Trigger ||
|
||||
allocation.OneTimeCommissionThreshold != cfg.Threshold ||
|
||||
allocation.OneTimeCommissionMode != cfg.Mode ||
|
||||
allocation.OneTimeCommissionValue != cfg.Value {
|
||||
oneTimeCommissionChanged = true
|
||||
}
|
||||
allocation.OneTimeCommissionType = cfg.Type
|
||||
allocation.OneTimeCommissionTrigger = cfg.Trigger
|
||||
allocation.OneTimeCommissionThreshold = cfg.Threshold
|
||||
if cfg.Type == model.OneTimeCommissionTypeFixed {
|
||||
allocation.OneTimeCommissionMode = cfg.Mode
|
||||
allocation.OneTimeCommissionValue = cfg.Value
|
||||
} else {
|
||||
allocation.OneTimeCommissionMode = ""
|
||||
allocation.OneTimeCommissionValue = 0
|
||||
}
|
||||
if req.OneTimeCommissionTrigger != nil {
|
||||
allocation.OneTimeCommissionTrigger = *req.OneTimeCommissionTrigger
|
||||
}
|
||||
if req.OneTimeCommissionThreshold != nil {
|
||||
allocation.OneTimeCommissionThreshold = *req.OneTimeCommissionThreshold
|
||||
}
|
||||
|
||||
if req.EnableForceRecharge != nil {
|
||||
allocation.EnableForceRecharge = *req.EnableForceRecharge
|
||||
}
|
||||
@@ -246,46 +211,36 @@ func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateShopSeries
|
||||
if req.ForceRechargeTriggerType != nil {
|
||||
allocation.ForceRechargeTriggerType = *req.ForceRechargeTriggerType
|
||||
}
|
||||
if req.Status != nil {
|
||||
allocation.Status = *req.Status
|
||||
}
|
||||
|
||||
allocation.Updater = currentUserID
|
||||
|
||||
if configChanged {
|
||||
if err := s.createNewConfigVersion(ctx, allocation); err != nil {
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "创建配置版本失败")
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.allocationStore.Update(ctx, allocation); err != nil {
|
||||
if err := s.seriesAllocationStore.Update(ctx, allocation); err != nil {
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "更新分配失败")
|
||||
}
|
||||
|
||||
if oneTimeCommissionChanged && req.OneTimeCommissionConfig != nil &&
|
||||
req.OneTimeCommissionConfig.Type == model.OneTimeCommissionTypeTiered {
|
||||
if err := s.oneTimeCommissionTierStore.DeleteByAllocationID(ctx, allocation.ID); err != nil {
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "清理旧梯度配置失败")
|
||||
}
|
||||
if err := s.saveOneTimeCommissionTiers(ctx, allocation.ID, req.OneTimeCommissionConfig.Tiers, currentUserID); err != nil {
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "更新一次性佣金梯度配置失败")
|
||||
}
|
||||
}
|
||||
|
||||
shop, _ := s.shopStore.GetByID(ctx, allocation.ShopID)
|
||||
series, _ := s.packageSeriesStore.GetByID(ctx, allocation.SeriesID)
|
||||
|
||||
shopName := ""
|
||||
seriesName := ""
|
||||
seriesCode := ""
|
||||
if shop != nil {
|
||||
shopName = shop.ShopName
|
||||
}
|
||||
if series != nil {
|
||||
seriesName = series.SeriesName
|
||||
seriesCode = series.SeriesCode
|
||||
}
|
||||
|
||||
return s.buildResponse(ctx, allocation, shopName, seriesName)
|
||||
return s.buildResponse(ctx, allocation, shopName, seriesName, seriesCode)
|
||||
}
|
||||
|
||||
func (s *Service) Delete(ctx context.Context, id uint) error {
|
||||
allocation, err := s.allocationStore.GetByID(ctx, id)
|
||||
skipCtx := pkggorm.SkipDataPermission(ctx)
|
||||
_, err := s.seriesAllocationStore.GetByID(skipCtx, id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "分配记录不存在")
|
||||
@@ -293,15 +248,15 @@ func (s *Service) Delete(ctx context.Context, id uint) error {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
||||
}
|
||||
|
||||
hasDependent, err := s.allocationStore.HasDependentAllocations(ctx, allocation.ShopID, allocation.SeriesID)
|
||||
count, err := s.packageAllocationStore.CountBySeriesAllocationID(skipCtx, id)
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "检查依赖关系失败")
|
||||
return errors.Wrap(errors.CodeInternalError, err, "检查关联套餐分配失败")
|
||||
}
|
||||
if hasDependent {
|
||||
return errors.New(errors.CodeConflict, "存在下级依赖,无法删除")
|
||||
if count > 0 {
|
||||
return errors.New(errors.CodeInvalidParam, "存在关联的套餐分配,无法删除")
|
||||
}
|
||||
|
||||
if err := s.allocationStore.Delete(ctx, id); err != nil {
|
||||
if err := s.seriesAllocationStore.Delete(skipCtx, id); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "删除分配失败")
|
||||
}
|
||||
|
||||
@@ -309,9 +264,6 @@ func (s *Service) Delete(ctx context.Context, id uint) error {
|
||||
}
|
||||
|
||||
func (s *Service) List(ctx context.Context, req *dto.ShopSeriesAllocationListRequest) ([]*dto.ShopSeriesAllocationResponse, int64, error) {
|
||||
userType := middleware.GetUserTypeFromContext(ctx)
|
||||
shopID := middleware.GetShopIDFromContext(ctx)
|
||||
|
||||
opts := &store.QueryOptions{
|
||||
Page: req.Page,
|
||||
PageSize: req.PageSize,
|
||||
@@ -331,14 +283,14 @@ func (s *Service) List(ctx context.Context, req *dto.ShopSeriesAllocationListReq
|
||||
if req.SeriesID != nil {
|
||||
filters["series_id"] = *req.SeriesID
|
||||
}
|
||||
if req.AllocatorShopID != nil {
|
||||
filters["allocator_shop_id"] = *req.AllocatorShopID
|
||||
}
|
||||
if req.Status != nil {
|
||||
filters["status"] = *req.Status
|
||||
}
|
||||
if shopID > 0 && userType == constants.UserTypeAgent {
|
||||
filters["allocator_shop_id"] = shopID
|
||||
}
|
||||
|
||||
allocations, total, err := s.allocationStore.List(ctx, opts, filters)
|
||||
allocations, total, err := s.seriesAllocationStore.List(ctx, opts, filters)
|
||||
if err != nil {
|
||||
return nil, 0, errors.Wrap(errors.CodeInternalError, err, "查询分配列表失败")
|
||||
}
|
||||
@@ -350,233 +302,55 @@ func (s *Service) List(ctx context.Context, req *dto.ShopSeriesAllocationListReq
|
||||
|
||||
shopName := ""
|
||||
seriesName := ""
|
||||
seriesCode := ""
|
||||
if shop != nil {
|
||||
shopName = shop.ShopName
|
||||
}
|
||||
if series != nil {
|
||||
seriesName = series.SeriesName
|
||||
seriesCode = series.SeriesCode
|
||||
}
|
||||
|
||||
resp, _ := s.buildResponse(ctx, a, shopName, seriesName)
|
||||
resp, _ := s.buildResponse(ctx, a, shopName, seriesName, seriesCode)
|
||||
responses[i] = resp
|
||||
}
|
||||
|
||||
return responses, total, nil
|
||||
}
|
||||
|
||||
func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error {
|
||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
||||
if currentUserID == 0 {
|
||||
return errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
_, err := s.allocationStore.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "分配记录不存在")
|
||||
}
|
||||
return errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
||||
}
|
||||
|
||||
if err := s.allocationStore.UpdateStatus(ctx, id, status, currentUserID); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "更新状态失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) GetParentCostPrice(ctx context.Context, shopID, packageID uint) (int64, error) {
|
||||
pkg, err := s.packageStore.GetByID(ctx, packageID)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(errors.CodeInternalError, err, "获取套餐失败")
|
||||
}
|
||||
|
||||
shop, err := s.shopStore.GetByID(ctx, shopID)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(errors.CodeInternalError, err, "获取店铺失败")
|
||||
}
|
||||
|
||||
if shop.ParentID == nil || *shop.ParentID == 0 {
|
||||
return pkg.SuggestedCostPrice, nil
|
||||
}
|
||||
|
||||
return 0, errors.New(errors.CodeInvalidParam, "自动计算成本价功能已移除,请手动设置成本价")
|
||||
}
|
||||
|
||||
func (s *Service) buildResponse(ctx context.Context, a *model.ShopSeriesAllocation, shopName, seriesName string) (*dto.ShopSeriesAllocationResponse, error) {
|
||||
allocatorShop, _ := s.shopStore.GetByID(ctx, a.AllocatorShopID)
|
||||
func (s *Service) buildResponse(ctx context.Context, a *model.ShopSeriesAllocation, shopName, seriesName, seriesCode string) (*dto.ShopSeriesAllocationResponse, error) {
|
||||
allocatorShopName := ""
|
||||
if allocatorShop != nil {
|
||||
allocatorShopName = allocatorShop.ShopName
|
||||
}
|
||||
|
||||
resp := &dto.ShopSeriesAllocationResponse{
|
||||
ID: a.ID,
|
||||
ShopID: a.ShopID,
|
||||
ShopName: shopName,
|
||||
SeriesID: a.SeriesID,
|
||||
SeriesName: seriesName,
|
||||
AllocatorShopID: a.AllocatorShopID,
|
||||
AllocatorShopName: allocatorShopName,
|
||||
BaseCommission: dto.BaseCommissionConfig{
|
||||
Mode: a.BaseCommissionMode,
|
||||
Value: a.BaseCommissionValue,
|
||||
},
|
||||
EnableOneTimeCommission: a.EnableOneTimeCommission,
|
||||
EnableForceRecharge: a.EnableForceRecharge,
|
||||
ForceRechargeAmount: a.ForceRechargeAmount,
|
||||
ForceRechargeTriggerType: a.ForceRechargeTriggerType,
|
||||
Status: a.Status,
|
||||
CreatedAt: a.CreatedAt.Format(time.RFC3339),
|
||||
UpdatedAt: a.UpdatedAt.Format(time.RFC3339),
|
||||
}
|
||||
|
||||
if a.EnableOneTimeCommission {
|
||||
cfg := &dto.OneTimeCommissionConfig{
|
||||
Type: a.OneTimeCommissionType,
|
||||
Trigger: a.OneTimeCommissionTrigger,
|
||||
Threshold: a.OneTimeCommissionThreshold,
|
||||
Mode: a.OneTimeCommissionMode,
|
||||
Value: a.OneTimeCommissionValue,
|
||||
if a.AllocatorShopID > 0 {
|
||||
allocatorShop, _ := s.shopStore.GetByID(ctx, a.AllocatorShopID)
|
||||
if allocatorShop != nil {
|
||||
allocatorShopName = allocatorShop.ShopName
|
||||
}
|
||||
if a.OneTimeCommissionType == model.OneTimeCommissionTypeTiered {
|
||||
tiers, err := s.oneTimeCommissionTierStore.ListByAllocationID(ctx, a.ID)
|
||||
if err == nil && len(tiers) > 0 {
|
||||
cfg.Tiers = make([]dto.OneTimeCommissionTierEntry, len(tiers))
|
||||
for i, t := range tiers {
|
||||
cfg.Tiers[i] = dto.OneTimeCommissionTierEntry{
|
||||
TierType: t.TierType,
|
||||
Threshold: t.ThresholdValue,
|
||||
Mode: t.CommissionMode,
|
||||
Value: t.CommissionValue,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
resp.OneTimeCommissionConfig = cfg
|
||||
} else {
|
||||
allocatorShopName = "平台"
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
return &dto.ShopSeriesAllocationResponse{
|
||||
ID: a.ID,
|
||||
ShopID: a.ShopID,
|
||||
ShopName: shopName,
|
||||
SeriesID: a.SeriesID,
|
||||
SeriesName: seriesName,
|
||||
SeriesCode: seriesCode,
|
||||
AllocatorShopID: a.AllocatorShopID,
|
||||
AllocatorShopName: allocatorShopName,
|
||||
OneTimeCommissionAmount: a.OneTimeCommissionAmount,
|
||||
EnableOneTimeCommission: a.EnableOneTimeCommission,
|
||||
OneTimeCommissionTrigger: a.OneTimeCommissionTrigger,
|
||||
OneTimeCommissionThreshold: a.OneTimeCommissionThreshold,
|
||||
EnableForceRecharge: a.EnableForceRecharge,
|
||||
ForceRechargeAmount: a.ForceRechargeAmount,
|
||||
ForceRechargeTriggerType: a.ForceRechargeTriggerType,
|
||||
Status: a.Status,
|
||||
CreatedAt: a.CreatedAt.Format(time.RFC3339),
|
||||
UpdatedAt: a.UpdatedAt.Format(time.RFC3339),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) createNewConfigVersion(ctx context.Context, allocation *model.ShopSeriesAllocation) error {
|
||||
now := time.Now()
|
||||
|
||||
if err := s.configStore.InvalidateCurrent(ctx, allocation.ID, now); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "失效当前配置版本失败")
|
||||
}
|
||||
|
||||
latestVersion, err := s.configStore.GetLatestVersion(ctx, allocation.ID)
|
||||
newVersion := 1
|
||||
if err == nil && latestVersion != nil {
|
||||
newVersion = latestVersion.Version + 1
|
||||
}
|
||||
|
||||
newConfig := &model.ShopSeriesAllocationConfig{
|
||||
AllocationID: allocation.ID,
|
||||
Version: newVersion,
|
||||
BaseCommissionMode: allocation.BaseCommissionMode,
|
||||
BaseCommissionValue: allocation.BaseCommissionValue,
|
||||
EffectiveFrom: now,
|
||||
}
|
||||
|
||||
if err := s.configStore.Create(ctx, newConfig); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "创建新配置版本失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) validateOneTimeCommissionConfig(req *dto.CreateShopSeriesAllocationRequest) error {
|
||||
if !req.EnableOneTimeCommission {
|
||||
return nil
|
||||
}
|
||||
if req.OneTimeCommissionConfig == nil {
|
||||
return errors.New(errors.CodeInvalidParam, "启用一次性佣金时必须提供配置")
|
||||
}
|
||||
cfg := req.OneTimeCommissionConfig
|
||||
if cfg.Type == model.OneTimeCommissionTypeFixed {
|
||||
if cfg.Mode == "" {
|
||||
return errors.New(errors.CodeInvalidParam, "固定类型一次性佣金必须指定返佣模式")
|
||||
}
|
||||
if cfg.Value <= 0 {
|
||||
return errors.New(errors.CodeInvalidParam, "固定类型一次性佣金必须指定返佣金额")
|
||||
}
|
||||
} else if cfg.Type == model.OneTimeCommissionTypeTiered {
|
||||
if len(cfg.Tiers) == 0 {
|
||||
return errors.New(errors.CodeInvalidParam, "梯度类型一次性佣金必须提供梯度档位")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) validateOneTimeCommissionConfigForUpdate(enableOneTimeCommission bool, cfg *dto.OneTimeCommissionConfig) error {
|
||||
if !enableOneTimeCommission {
|
||||
return nil
|
||||
}
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
if cfg.Type == model.OneTimeCommissionTypeFixed {
|
||||
if cfg.Mode == "" {
|
||||
return errors.New(errors.CodeInvalidParam, "固定类型一次性佣金必须指定返佣模式")
|
||||
}
|
||||
if cfg.Value <= 0 {
|
||||
return errors.New(errors.CodeInvalidParam, "固定类型一次性佣金必须指定返佣金额")
|
||||
}
|
||||
} else if cfg.Type == model.OneTimeCommissionTypeTiered {
|
||||
if len(cfg.Tiers) == 0 {
|
||||
return errors.New(errors.CodeInvalidParam, "梯度类型一次性佣金必须提供梯度档位")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) saveOneTimeCommissionTiers(ctx context.Context, allocationID uint, tiers []dto.OneTimeCommissionTierEntry, userID uint) error {
|
||||
if len(tiers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
tierModels := make([]*model.ShopSeriesOneTimeCommissionTier, len(tiers))
|
||||
for i, t := range tiers {
|
||||
tierModels[i] = &model.ShopSeriesOneTimeCommissionTier{
|
||||
AllocationID: allocationID,
|
||||
TierType: t.TierType,
|
||||
ThresholdValue: t.Threshold,
|
||||
CommissionMode: t.Mode,
|
||||
CommissionValue: t.Value,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
tierModels[i].Creator = userID
|
||||
}
|
||||
|
||||
return s.oneTimeCommissionTierStore.BatchCreate(ctx, tierModels)
|
||||
}
|
||||
|
||||
func (s *Service) GetEffectiveConfig(ctx context.Context, allocationID uint, at time.Time) (*model.ShopSeriesAllocationConfig, error) {
|
||||
config, err := s.configStore.GetEffective(ctx, allocationID, at)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeNotFound, "未找到生效的配置版本")
|
||||
}
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取生效配置失败")
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (s *Service) ListConfigVersions(ctx context.Context, allocationID uint) ([]*model.ShopSeriesAllocationConfig, error) {
|
||||
_, err := s.allocationStore.GetByID(ctx, allocationID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeNotFound, "分配记录不存在")
|
||||
}
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
||||
}
|
||||
|
||||
configs, err := s.configStore.List(ctx, allocationID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取配置版本列表失败")
|
||||
}
|
||||
|
||||
return configs, nil
|
||||
func (s *Service) GetByShopAndSeries(ctx context.Context, shopID, seriesID uint) (*model.ShopSeriesAllocation, error) {
|
||||
return s.seriesAllocationStore.GetByShopAndSeries(ctx, shopID, seriesID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user