175 lines
5.1 KiB
Go
175 lines
5.1 KiB
Go
package skills
|
||
|
||
import (
|
||
"strings"
|
||
"unicode/utf8"
|
||
"yingce/backend/internal/kernel"
|
||
"yingce/backend/internal/model"
|
||
"yingce/backend/internal/repository"
|
||
)
|
||
|
||
type SkillCuration = repository.SkillCuration
|
||
type SkillCurationAssignmentInput struct {
|
||
RootID *string `json:"rootId,omitempty"`
|
||
SkillID string `json:"skillId"`
|
||
CategoryIDs []string `json:"categoryIds"`
|
||
}
|
||
type SkillCurationUpdate struct {
|
||
Root *model.SkillCurationRoot `json:"root,omitempty"`
|
||
ExpectedRevision *int64 `json:"expectedRevision"`
|
||
Enabled *bool `json:"enabled,omitempty"`
|
||
Category *model.SkillCurationCategory `json:"category,omitempty"`
|
||
Assignment *SkillCurationAssignmentInput `json:"assignment,omitempty"`
|
||
}
|
||
|
||
func (s *Service) SkillCuration(actor *model.User, admin bool) (*SkillCuration, error) {
|
||
if err := s.curationActor(actor, admin); err != nil {
|
||
return nil, err
|
||
}
|
||
return s.repo.SkillCuration(admin)
|
||
}
|
||
func (s *Service) curationActor(actor *model.User, admin bool) error {
|
||
if actor == nil || actor.ID == "" {
|
||
return kernel.Unauthorized("请先登录")
|
||
}
|
||
if admin {
|
||
current, err := s.repo.User(actor.ID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if current.Role != model.UserRoleAdmin || current.Status != model.UserStatusActive {
|
||
return kernel.Forbidden("需要管理员权限")
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
func (s *Service) UpdateSkillCuration(actor *model.User, req SkillCurationUpdate, requestID string) (*SkillCuration, error) {
|
||
if err := s.curationActor(actor, true); err != nil {
|
||
return nil, err
|
||
}
|
||
count := 0
|
||
if req.Root != nil {
|
||
count++
|
||
}
|
||
if req.Enabled != nil {
|
||
count++
|
||
}
|
||
if req.Category != nil {
|
||
count++
|
||
}
|
||
if req.Assignment != nil {
|
||
count++
|
||
}
|
||
if req.ExpectedRevision == nil || *req.ExpectedRevision < 0 || count != 1 {
|
||
return nil, kernel.BadAuthRequest("需要版本号和一项修改")
|
||
}
|
||
err := s.repo.UpdateSkillCuration(actor.ID, *req.ExpectedRevision, requestID, func(repo *repository.Repository) error {
|
||
if req.Enabled != nil {
|
||
return repo.SetSkillCurationEnabled(*req.Enabled)
|
||
}
|
||
state, err := repo.SkillCuration(true)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if req.Root != nil {
|
||
return saveCurationRoot(repo, state, *req.Root)
|
||
}
|
||
if req.Category != nil {
|
||
c := *req.Category
|
||
c.Name = strings.Join(strings.Fields(c.Name), " ")
|
||
c.NormalizedName = strings.ToLower(c.Name)
|
||
if utf8.RuneCountInString(c.Name) < 1 || utf8.RuneCountInString(c.Name) > 64 || !activeCurationRoot(state, c.RootTag) {
|
||
return kernel.BadAuthRequest("分类名称或一级分类无效")
|
||
}
|
||
exists := c.ID == ""
|
||
for _, old := range state.Categories {
|
||
if old.ID == c.ID {
|
||
exists = true
|
||
if old.RootTag != c.RootTag {
|
||
return kernel.BadAuthRequest("不能移动子分类")
|
||
}
|
||
}
|
||
if old.ID != c.ID && old.RootTag == c.RootTag && old.NormalizedName == c.NormalizedName {
|
||
return kernel.BadAuthRequest("同一级分类下名称不能重复")
|
||
}
|
||
}
|
||
if !exists {
|
||
return kernel.NotFound("分类不存在")
|
||
}
|
||
if c.ID == "" {
|
||
c.ID = kernel.NewID()
|
||
}
|
||
return repo.SaveSkillCurationCategory(&c)
|
||
}
|
||
a := req.Assignment
|
||
if a.CategoryIDs == nil || len(a.CategoryIDs) > 32 {
|
||
return kernel.BadAuthRequest("需要完整分类集合,最多32项")
|
||
}
|
||
skill, err := repo.CurationSkill(a.SkillID)
|
||
if err != nil {
|
||
return kernel.NotFound("公开技能不存在")
|
||
}
|
||
rootID := state.EffectiveRoot(skill)
|
||
if a.RootID != nil {
|
||
if *a.RootID != "" && !activeCurationRoot(state, *a.RootID) {
|
||
return kernel.BadAuthRequest("一级分类必须存在且启用")
|
||
}
|
||
rootID = *a.RootID
|
||
if rootID == "" {
|
||
rootID = skill.Tag
|
||
if !activeCurationRoot(state, rootID) {
|
||
rootID = repository.UnassignedSkillRoot
|
||
}
|
||
}
|
||
}
|
||
seen := map[string]bool{}
|
||
for _, id := range a.CategoryIDs {
|
||
valid := false
|
||
for _, c := range state.Categories {
|
||
if c.ID == id && c.Enabled && c.RootTag == rootID {
|
||
valid = true
|
||
}
|
||
}
|
||
if !valid || seen[id] {
|
||
return kernel.BadAuthRequest("分类必须启用、同根且不能重复")
|
||
}
|
||
seen[id] = true
|
||
}
|
||
if a.RootID != nil {
|
||
if err := repo.AssignSkillCurationRoot(a.SkillID, *a.RootID); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return repo.AssignSkillCuration(a.SkillID, a.CategoryIDs)
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return s.repo.SkillCuration(true)
|
||
}
|
||
|
||
func (s *Service) curationFilter(req SkillListRequest) (string, bool, error) {
|
||
if req.PlatformCategoryID == "" && !req.PlatformUncategorized {
|
||
return "", false, nil
|
||
}
|
||
state, err := s.repo.SkillCuration(false)
|
||
if err != nil {
|
||
return "", false, err
|
||
}
|
||
if !state.Enabled {
|
||
return "", false, nil
|
||
}
|
||
if req.PlatformCategoryID != "" && req.PlatformUncategorized {
|
||
return "", false, kernel.BadAuthRequest("分类和未细分不能同时筛选")
|
||
}
|
||
if req.PlatformCategoryID != "" {
|
||
for _, c := range state.Categories {
|
||
if c.ID == req.PlatformCategoryID {
|
||
return c.ID, false, nil
|
||
}
|
||
}
|
||
return "", false, kernel.BadAuthRequest("分类已停用或不存在,请重新选择")
|
||
}
|
||
return "", req.PlatformUncategorized, nil
|
||
}
|