This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
"github.com/break/junhong_cmp_fiber/internal/service/packageprice"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
@@ -42,6 +43,9 @@ func (s *Service) Create(ctx context.Context, req *dto.CreatePackageRequest) (*d
|
||||
if currentUserID == 0 {
|
||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
if err := validateGiftPackagePermission(ctx, req.IsGift); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
existing, _ := s.packageStore.GetByCode(ctx, req.PackageCode)
|
||||
if existing != nil {
|
||||
@@ -91,12 +95,20 @@ func (s *Service) Create(ctx context.Context, req *dto.CreatePackageRequest) (*d
|
||||
seriesName = &series.SeriesName
|
||||
}
|
||||
|
||||
priceConfigStatus, storedRetailPrice, err := packageprice.ResolvePackagePriceState(req.IsGift, req.SuggestedRetailPrice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkg := &model.Package{
|
||||
PackageCode: req.PackageCode,
|
||||
PackageName: req.PackageName,
|
||||
PackageType: req.PackageType,
|
||||
IsGift: req.IsGift,
|
||||
DurationMonths: req.DurationMonths,
|
||||
CostPrice: req.CostPrice,
|
||||
PriceConfigStatus: priceConfigStatus,
|
||||
SuggestedRetailPrice: storedRetailPrice,
|
||||
EnableVirtualData: req.EnableVirtualData,
|
||||
CalendarType: calendarType,
|
||||
Status: constants.StatusEnabled,
|
||||
@@ -111,9 +123,6 @@ func (s *Service) Create(ctx context.Context, req *dto.CreatePackageRequest) (*d
|
||||
if req.VirtualDataMB != nil {
|
||||
pkg.VirtualDataMB = *req.VirtualDataMB
|
||||
}
|
||||
if req.SuggestedRetailPrice != nil {
|
||||
pkg.SuggestedRetailPrice = *req.SuggestedRetailPrice
|
||||
}
|
||||
if req.DurationDays != nil {
|
||||
pkg.DurationDays = *req.DurationDays
|
||||
}
|
||||
@@ -220,6 +229,13 @@ func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdatePackageReq
|
||||
if req.PackageType != nil {
|
||||
pkg.PackageType = *req.PackageType
|
||||
}
|
||||
nextIsGift := pkg.IsGift
|
||||
if req.IsGift != nil {
|
||||
nextIsGift = *req.IsGift
|
||||
}
|
||||
if err := validateGiftPackagePermission(ctx, nextIsGift); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.DurationMonths != nil {
|
||||
pkg.DurationMonths = *req.DurationMonths
|
||||
}
|
||||
@@ -235,9 +251,6 @@ func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdatePackageReq
|
||||
if req.CostPrice != nil {
|
||||
pkg.CostPrice = *req.CostPrice
|
||||
}
|
||||
if req.SuggestedRetailPrice != nil {
|
||||
pkg.SuggestedRetailPrice = *req.SuggestedRetailPrice
|
||||
}
|
||||
if req.CalendarType != nil {
|
||||
pkg.CalendarType = *req.CalendarType
|
||||
}
|
||||
@@ -251,6 +264,23 @@ func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdatePackageReq
|
||||
pkg.ExpiryBase = *req.ExpiryBase
|
||||
}
|
||||
|
||||
var rawSuggestedRetailPrice *int64
|
||||
switch {
|
||||
case req.ClearSuggestedRetailPrice:
|
||||
rawSuggestedRetailPrice = nil
|
||||
case req.SuggestedRetailPrice != nil:
|
||||
rawSuggestedRetailPrice = req.SuggestedRetailPrice
|
||||
default:
|
||||
rawSuggestedRetailPrice = packageprice.PackageRawSuggestedRetailPrice(pkg)
|
||||
}
|
||||
priceConfigStatus, storedRetailPrice, err := packageprice.ResolvePackagePriceState(nextIsGift, rawSuggestedRetailPrice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkg.IsGift = nextIsGift
|
||||
pkg.PriceConfigStatus = priceConfigStatus
|
||||
pkg.SuggestedRetailPrice = storedRetailPrice
|
||||
|
||||
// 校验套餐周期类型和时长配置
|
||||
if pkg.CalendarType == constants.PackageCalendarTypeNaturalMonth {
|
||||
if pkg.DurationMonths <= 0 {
|
||||
@@ -503,11 +533,25 @@ func (s *Service) UpdateRetailPrice(ctx context.Context, packageID uint, retailP
|
||||
return errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
||||
}
|
||||
|
||||
pkg, err := s.packageStore.GetByID(ctx, packageID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "套餐不存在")
|
||||
}
|
||||
return errors.Wrap(errors.CodeInternalError, err, "获取套餐失败")
|
||||
}
|
||||
if pkg.IsGift {
|
||||
return errors.New(errors.CodeForbidden, "赠送套餐不允许代理修改零售价")
|
||||
}
|
||||
priceConfigStatus, storedRetailPrice, err := packageprice.ResolveAllocationPriceState(&retailPrice)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if retailPrice < allocation.CostPrice {
|
||||
return errors.New(errors.CodeInvalidParam, "零售价不能低于成本价")
|
||||
}
|
||||
|
||||
if err := s.packageAllocationStore.UpdateRetailPrice(ctx, allocation.ID, retailPrice, currentUserID); err != nil {
|
||||
if err := s.packageAllocationStore.UpdateRetailPrice(ctx, allocation.ID, storedRetailPrice, priceConfigStatus, currentUserID); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "更新零售价失败")
|
||||
}
|
||||
|
||||
@@ -568,13 +612,16 @@ func (s *Service) toResponse(ctx context.Context, pkg *model.Package) *dto.Packa
|
||||
PackageName: pkg.PackageName,
|
||||
SeriesID: seriesID,
|
||||
PackageType: pkg.PackageType,
|
||||
IsGift: pkg.IsGift,
|
||||
DurationMonths: pkg.DurationMonths,
|
||||
RealDataMB: pkg.RealDataMB,
|
||||
VirtualDataMB: pkg.VirtualDataMB,
|
||||
EnableVirtualData: pkg.EnableVirtualData,
|
||||
VirtualRatio: calculateVirtualRatio(pkg.EnableVirtualData, pkg.RealDataMB, pkg.VirtualDataMB),
|
||||
CostPrice: pkg.CostPrice,
|
||||
SuggestedRetailPrice: pkg.SuggestedRetailPrice,
|
||||
SuggestedRetailPrice: packageprice.PackageRawSuggestedRetailPrice(pkg),
|
||||
PriceConfigStatus: pkg.PriceConfigStatus,
|
||||
PriceConfigStatusName: packagePriceConfigStatusName(pkg.PriceConfigStatus),
|
||||
CalendarType: pkg.CalendarType,
|
||||
DurationDays: durationDays,
|
||||
DataResetCycle: pkg.DataResetCycle,
|
||||
@@ -591,11 +638,18 @@ func (s *Service) toResponse(ctx context.Context, pkg *model.Package) *dto.Packa
|
||||
allocation, err := s.packageAllocationStore.GetByShopAndPackage(ctx, shopID, pkg.ID)
|
||||
if err == nil && allocation != nil {
|
||||
resp.CostPrice = allocation.CostPrice
|
||||
resp.RetailPrice = &allocation.RetailPrice
|
||||
profitMargin := allocation.RetailPrice - allocation.CostPrice
|
||||
resp.RetailPrice = packageprice.AllocationRawRetailPrice(allocation)
|
||||
retailPriceConfigStatus := allocation.RetailPriceConfigStatus
|
||||
resp.RetailPriceConfigStatus = &retailPriceConfigStatus
|
||||
effectiveRetailPrice := packageprice.AllocationEffectiveRetailPrice(allocation)
|
||||
resp.EffectiveRetailPrice = &effectiveRetailPrice
|
||||
profitMargin := effectiveRetailPrice - allocation.CostPrice
|
||||
resp.ProfitMargin = &profitMargin
|
||||
resp.ShelfStatus = allocation.ShelfStatus
|
||||
}
|
||||
} else {
|
||||
effectiveRetailPrice := packageprice.PackageEffectiveRetailPrice(pkg)
|
||||
resp.EffectiveRetailPrice = &effectiveRetailPrice
|
||||
}
|
||||
|
||||
resp.StatusName = constants.GetStatusName(resp.Status)
|
||||
@@ -635,13 +689,16 @@ func (s *Service) toResponseWithAllocation(_ context.Context, pkg *model.Package
|
||||
PackageName: pkg.PackageName,
|
||||
SeriesID: seriesID,
|
||||
PackageType: pkg.PackageType,
|
||||
IsGift: pkg.IsGift,
|
||||
DurationMonths: pkg.DurationMonths,
|
||||
RealDataMB: pkg.RealDataMB,
|
||||
VirtualDataMB: pkg.VirtualDataMB,
|
||||
EnableVirtualData: pkg.EnableVirtualData,
|
||||
VirtualRatio: calculateVirtualRatio(pkg.EnableVirtualData, pkg.RealDataMB, pkg.VirtualDataMB),
|
||||
CostPrice: pkg.CostPrice,
|
||||
SuggestedRetailPrice: pkg.SuggestedRetailPrice,
|
||||
SuggestedRetailPrice: packageprice.PackageRawSuggestedRetailPrice(pkg),
|
||||
PriceConfigStatus: pkg.PriceConfigStatus,
|
||||
PriceConfigStatusName: packagePriceConfigStatusName(pkg.PriceConfigStatus),
|
||||
CalendarType: pkg.CalendarType,
|
||||
DurationDays: durationDays,
|
||||
DataResetCycle: pkg.DataResetCycle,
|
||||
@@ -655,11 +712,18 @@ func (s *Service) toResponseWithAllocation(_ context.Context, pkg *model.Package
|
||||
if allocationMap != nil {
|
||||
if allocation, ok := allocationMap[pkg.ID]; ok {
|
||||
resp.CostPrice = allocation.CostPrice
|
||||
resp.RetailPrice = &allocation.RetailPrice
|
||||
profitMargin := allocation.RetailPrice - allocation.CostPrice
|
||||
resp.RetailPrice = packageprice.AllocationRawRetailPrice(allocation)
|
||||
retailPriceConfigStatus := allocation.RetailPriceConfigStatus
|
||||
resp.RetailPriceConfigStatus = &retailPriceConfigStatus
|
||||
effectiveRetailPrice := packageprice.AllocationEffectiveRetailPrice(allocation)
|
||||
resp.EffectiveRetailPrice = &effectiveRetailPrice
|
||||
profitMargin := effectiveRetailPrice - allocation.CostPrice
|
||||
resp.ProfitMargin = &profitMargin
|
||||
resp.ShelfStatus = allocation.ShelfStatus
|
||||
}
|
||||
} else {
|
||||
effectiveRetailPrice := packageprice.PackageEffectiveRetailPrice(pkg)
|
||||
resp.EffectiveRetailPrice = &effectiveRetailPrice
|
||||
}
|
||||
|
||||
// 填充返佣信息(仅代理用户可见)
|
||||
@@ -728,6 +792,28 @@ func (s *Service) buildTierInfo(tiers []model.OneTimeCommissionTier, currentAmou
|
||||
return tierInfo
|
||||
}
|
||||
|
||||
func validateGiftPackagePermission(ctx context.Context, isGift bool) error {
|
||||
if !isGift {
|
||||
return nil
|
||||
}
|
||||
userType := middleware.GetUserTypeFromContext(ctx)
|
||||
if userType != constants.UserTypeSuperAdmin && userType != constants.UserTypePlatform {
|
||||
return errors.New(errors.CodeForbidden, "仅平台侧可创建或维护赠送套餐")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func packagePriceConfigStatusName(status int) string {
|
||||
switch status {
|
||||
case constants.PackagePriceConfigStatusGiftZero:
|
||||
return "赠送0价"
|
||||
case constants.PackagePriceConfigStatusConfigured:
|
||||
return "已配置非0"
|
||||
default:
|
||||
return "未配置"
|
||||
}
|
||||
}
|
||||
|
||||
// formatAmount 格式化金额为可读字符串(分转元)
|
||||
func formatAmount(amountFen int64) string {
|
||||
yuan := float64(amountFen) / 100
|
||||
|
||||
Reference in New Issue
Block a user