fix(api): enforce project identifier uniqueness
This commit is contained in:
@@ -151,15 +151,6 @@ func (s *Service) CreateProjectWithInput(ownerID uint, input CreateProjectReques
|
||||
if identifier == "" {
|
||||
identifier = projectInitials(name)
|
||||
}
|
||||
var count int64
|
||||
if err := models.DBService.Model(&models.SenlinAgentProject{}).
|
||||
Where("owner_id = ? AND identifier = ?", ownerID, identifier).
|
||||
Count(&count).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
return nil, ErrProjectIdentifierConflict
|
||||
}
|
||||
project := &models.SenlinAgentProject{
|
||||
OwnerID: ownerID,
|
||||
Name: name,
|
||||
@@ -168,7 +159,10 @@ func (s *Service) CreateProjectWithInput(ownerID uint, input CreateProjectReques
|
||||
Background: strings.TrimSpace(input.Background),
|
||||
Description: strings.TrimSpace(input.Description),
|
||||
}
|
||||
return project, models.DBService.Create(project).Error
|
||||
if err := projectWriteError(models.DBService.Create(project).Error); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return project, nil
|
||||
}
|
||||
|
||||
// ListProjects 只返回当前所有者的公开 DTO,不让 handler 接触或序列化数据库模型。
|
||||
@@ -199,6 +193,14 @@ var (
|
||||
ErrProjectIdentifierConflict = errors.New("project identifier already exists")
|
||||
)
|
||||
|
||||
// projectWriteError 将 Postgres/SQLite 经 Gorm 翻译后的唯一约束错误收敛为稳定业务冲突。
|
||||
func projectWriteError(err error) error {
|
||||
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||
return ErrProjectIdentifierConflict
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateProject 仅更新项目设置白名单字段,查询和冲突检查均限定在当前所有者内。
|
||||
func (s *Service) UpdateProject(ownerID uint, identity string, input UpdateProjectRequest) error {
|
||||
project, err := FindOwnedProject(ownerID, identity)
|
||||
@@ -219,15 +221,6 @@ func (s *Service) UpdateProject(ownerID uint, identity string, input UpdateProje
|
||||
if identifier == "" {
|
||||
return ErrProjectIdentifierRequired
|
||||
}
|
||||
var count int64
|
||||
if err := models.DBService.Model(&models.SenlinAgentProject{}).
|
||||
Where("owner_id = ? AND identifier = ? AND id <> ?", ownerID, identifier, project.ID).
|
||||
Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return ErrProjectIdentifierConflict
|
||||
}
|
||||
updates["identifier"] = identifier
|
||||
}
|
||||
if input.Icon != nil {
|
||||
@@ -242,7 +235,7 @@ func (s *Service) UpdateProject(ownerID uint, identity string, input UpdateProje
|
||||
if len(updates) == 0 {
|
||||
return nil
|
||||
}
|
||||
return models.DBService.Model(project).Updates(updates).Error
|
||||
return projectWriteError(models.DBService.Model(project).Updates(updates).Error)
|
||||
}
|
||||
|
||||
func (s *Service) CreateTask(ownerID uint, projectID uint, input CreateTaskInput) (*models.SenlinAgentTask, error) {
|
||||
|
||||
Reference in New Issue
Block a user