feat: 新增代理分配套餐上架状态(shelf_status)功能
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m56s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m56s
- 新增数据库迁移:为 shop_package_allocation 表添加 shelf_status 字段 - 更新模型/DTO:ShopPackageAllocation 增加 ShelfStatus 字段及相关枚举 - 更新套餐分配 Service:支持上架/下架状态管理逻辑 - 更新套餐 Store/Service:根据 shelf_status 过滤可售套餐 - 更新购买验证 Service:引入上架状态校验逻辑 - 归档 OpenSpec 变更:2026-03-02-agent-allocation-shelf-status - 同步更新主规范文档:allocation-shelf-status、package-management、purchase-validation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -252,7 +252,9 @@ func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error {
|
||||
return errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
_, err := s.packageAllocationStore.GetByID(ctx, id)
|
||||
// 任务 3.2:所有者校验 —— 代理只能修改自己创建的分配记录的 status
|
||||
// 使用系统级查询(不带数据权限过滤),再做业务层权限判断以返回正确的 403
|
||||
allocation, err := s.packageAllocationStore.GetByIDForSystem(ctx, id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "分配记录不存在")
|
||||
@@ -260,6 +262,15 @@ func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
||||
}
|
||||
|
||||
userType := middleware.GetUserTypeFromContext(ctx)
|
||||
if userType == constants.UserTypeAgent {
|
||||
callerShopID := middleware.GetShopIDFromContext(ctx)
|
||||
// 代理用户只能修改自己作为 allocator 的记录
|
||||
if allocation.AllocatorShopID != callerShopID {
|
||||
return errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.packageAllocationStore.UpdateStatus(ctx, id, status, currentUserID); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "更新状态失败")
|
||||
}
|
||||
@@ -267,6 +278,43 @@ func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateShelfStatus 更新分配记录的上下架状态
|
||||
// 代理独立控制自己客户侧套餐可见性,不影响平台全局状态
|
||||
func (s *Service) UpdateShelfStatus(ctx context.Context, allocationID uint, shelfStatus int) error {
|
||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
||||
if currentUserID == 0 {
|
||||
return errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
allocation, err := s.packageAllocationStore.GetByID(ctx, allocationID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "分配记录不存在")
|
||||
}
|
||||
return errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
||||
}
|
||||
|
||||
// 上架时检查套餐全局状态:禁用的套餐不允许上架
|
||||
if shelfStatus == constants.ShelfStatusOn {
|
||||
pkg, err := s.packageStore.GetByID(ctx, allocation.PackageID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "套餐不存在")
|
||||
}
|
||||
return errors.Wrap(errors.CodeInternalError, err, "获取套餐失败")
|
||||
}
|
||||
if pkg.Status == constants.StatusDisabled {
|
||||
return errors.New(errors.CodeInvalidStatus, "套餐已禁用,无法上架")
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.packageAllocationStore.UpdateShelfStatus(ctx, allocationID, shelfStatus, currentUserID); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "更新上下架状态失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) buildResponse(ctx context.Context, a *model.ShopPackageAllocation, shopName, packageName, packageCode string) (*dto.ShopPackageAllocationResponse, error) {
|
||||
var seriesID uint
|
||||
seriesName := ""
|
||||
@@ -304,6 +352,7 @@ func (s *Service) buildResponse(ctx context.Context, a *model.ShopPackageAllocat
|
||||
AllocatorShopName: allocatorShopName,
|
||||
CostPrice: a.CostPrice,
|
||||
Status: a.Status,
|
||||
ShelfStatus: a.ShelfStatus,
|
||||
CreatedAt: a.CreatedAt.Format(time.RFC3339),
|
||||
UpdatedAt: a.UpdatedAt.Format(time.RFC3339),
|
||||
}, nil
|
||||
|
||||
Reference in New Issue
Block a user