修复反馈
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m30s

This commit is contained in:
2026-08-10 16:30:38 +08:00
parent 7aa03e91fb
commit f15a64395f
25 changed files with 527 additions and 90 deletions

View File

@@ -741,6 +741,22 @@ func (s *Service) batchGetAllocationsForShop(ctx context.Context, shopID uint, p
}
func (s *Service) toResponseWithAllocation(_ context.Context, pkg *model.Package, allocationMap map[uint]*model.ShopPackageAllocation, seriesAllocationMap map[uint]*model.ShopSeriesAllocation, seriesConfigMap map[uint]*model.OneTimeCommissionConfig) *dto.PackageResponse {
var allocation *model.ShopPackageAllocation
if allocationMap != nil {
allocation = allocationMap[pkg.ID]
}
resp := BuildResponseForAllocation(pkg, allocation)
// 填充返佣信息(仅代理用户可见)
if pkg.SeriesID > 0 && seriesAllocationMap != nil && seriesConfigMap != nil {
s.fillCommissionInfo(resp, pkg.SeriesID, seriesAllocationMap, seriesConfigMap)
}
return resp
}
// BuildResponseForAllocation 构建套餐列表字段,并按店铺授权覆盖价格和上架状态。
func BuildResponseForAllocation(pkg *model.Package, allocation *model.ShopPackageAllocation) *dto.PackageResponse {
var seriesID *uint
if pkg.SeriesID > 0 {
seriesID = &pkg.SeriesID
@@ -778,29 +794,21 @@ func (s *Service) toResponseWithAllocation(_ context.Context, pkg *model.Package
}
initPackageExpiryBaseFields(resp, pkg)
if allocationMap != nil {
if allocation, ok := allocationMap[pkg.ID]; ok {
resp.CostPrice = 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
applyAllocationExpiryBase(resp, pkg, allocation)
resp.ShelfStatus = allocation.ShelfStatus
}
if allocation != nil {
resp.CostPrice = 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
applyAllocationExpiryBase(resp, pkg, allocation)
resp.ShelfStatus = allocation.ShelfStatus
} else {
effectiveRetailPrice := packageprice.PackageEffectiveRetailPrice(pkg)
resp.EffectiveRetailPrice = &effectiveRetailPrice
}
// 填充返佣信息(仅代理用户可见)
if pkg.SeriesID > 0 && seriesAllocationMap != nil && seriesConfigMap != nil {
s.fillCommissionInfo(resp, pkg.SeriesID, seriesAllocationMap, seriesConfigMap)
}
return resp
}

View File

@@ -652,6 +652,83 @@ func (s *Service) List(ctx context.Context, req *dto.ShopSeriesGrantListRequest)
}, nil
}
// ListPackageOptions 返回授权页面可选择套餐与目标店铺已有授权状态。
func (s *Service) ListPackageOptions(ctx context.Context, req *dto.ShopSeriesGrantPackageOptionRequest) (*dto.ShopSeriesGrantPackageOptionResult, error) {
if req == nil || req.ShopID == 0 || req.SeriesID == 0 {
return nil, errors.New(errors.CodeInvalidParam, "店铺ID和套餐系列ID不能为空")
}
if err := middleware.CanManageShop(ctx, req.ShopID); err != nil {
return nil, errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
}
targetShop, err := s.shopStore.GetByID(ctx, req.ShopID)
if err != nil {
return nil, errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
}
series, err := s.packageSeriesStore.GetByID(ctx, req.SeriesID)
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, errors.New(errors.CodeNotFound, "套餐系列不存在")
}
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询套餐系列失败")
}
operatorType := middleware.GetUserTypeFromContext(ctx)
operatorShopID := middleware.GetShopIDFromContext(ctx)
query := s.db.WithContext(ctx).Model(&model.Package{}).
Where("tb_package.series_id = ? AND tb_package.is_gift = ?", req.SeriesID, false)
if operatorType == constants.UserTypeAgent {
if operatorShopID == 0 || targetShop.ParentID == nil || *targetShop.ParentID != operatorShopID {
return nil, errors.New(errors.CodeForbidden, "只能授权直属下级店铺")
}
var parentSeriesAllocation model.ShopSeriesAllocation
if err := s.db.WithContext(ctx).
Where("shop_id = ? AND series_id = ? AND status = ?", operatorShopID, req.SeriesID, constants.StatusEnabled).
First(&parentSeriesAllocation).Error; err != nil {
return nil, errors.New(errors.CodeForbidden, "当前账号无此系列授权,无法向下分配")
}
query = query.Joins("INNER JOIN tb_shop_package_allocation parent_allocation ON parent_allocation.package_id = tb_package.id AND parent_allocation.deleted_at IS NULL").
Where("parent_allocation.shop_id = ? AND parent_allocation.status = ?", operatorShopID, constants.StatusEnabled)
} else if operatorType != constants.UserTypePlatform && operatorType != constants.UserTypeSuperAdmin {
return nil, errors.New(errors.CodeForbidden, "无权限查询授权套餐")
}
var packages []model.Package
if err := query.Select("tb_package.*").Order("tb_package.id ASC").Find(&packages).Error; err != nil {
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询授权套餐候选项失败")
}
packageIDs := make([]uint, 0, len(packages))
for _, pkg := range packages {
packageIDs = append(packageIDs, pkg.ID)
}
authorized := make(map[uint]bool, len(packageIDs))
if len(packageIDs) > 0 {
var allocations []model.ShopPackageAllocation
if err := s.db.WithContext(ctx).Where("shop_id = ? AND package_id IN ? AND status = ?", req.ShopID, packageIDs, constants.StatusEnabled).Find(&allocations).Error; err != nil {
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询目标店铺套餐授权失败")
}
for _, allocation := range allocations {
authorized[allocation.PackageID] = true
}
}
parentAllocations := make(map[uint]*model.ShopPackageAllocation)
if operatorType == constants.UserTypeAgent && len(packageIDs) > 0 {
allocations, err := s.shopPackageAllocationStore.GetByShopAndPackages(ctx, operatorShopID, packageIDs)
if err != nil {
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询上级套餐授权失败")
}
for _, allocation := range allocations {
parentAllocations[allocation.PackageID] = allocation
}
}
items := make([]dto.ShopSeriesGrantPackageOption, 0, len(packages))
for i := range packages {
resp := packagepkg.BuildResponseForAllocation(&packages[i], parentAllocations[packages[i].ID])
resp.SeriesName = &series.SeriesName
items = append(items, dto.ShopSeriesGrantPackageOption{PackageResponse: *resp, Authorized: authorized[packages[i].ID]})
}
return &dto.ShopSeriesGrantPackageOptionResult{Items: items}, nil
}
// Update 更新系列授权
// PUT /api/admin/shop-series-grants/:id
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateShopSeriesGrantRequest) (_ *dto.ShopSeriesGrantResponse, retErr error) {