feat(skills): 技能库 - 内置技能目录迁移为 Markdown 包
This commit is contained in:
1 parent
eb43d3754f
commit
1080b22196
107 files changed
+15402
-3667
No files matched your search
@@ -25,6 +25,7 @@ tags:
|
||||
- name: models
|
||||
- name: agent
|
||||
- name: admin
|
||||
- name: skills
|
||||
paths:
|
||||
/admin/settings/appearance/live2d:
|
||||
post:
|
||||
@@ -123,6 +124,79 @@ paths:
|
||||
"200":
|
||||
description: 标准信封 data 为 presets 数组;预设只引用公开且启用的种子技能
|
||||
"500": {$ref: "#/components/responses/Error"}
|
||||
/skills:
|
||||
get:
|
||||
tags: [skills]
|
||||
summary: 列出技能;在个人技能视图按技能库分类筛选
|
||||
parameters:
|
||||
- {in: query, name: scope, schema: {type: string, enum: [public, mine, created, favorites], default: public}}
|
||||
- {in: query, name: libraryCategoryId, description: 用户技能库分类 ID,仅支持 mine 或 created 视图;与 libraryUncategorized 互斥, schema: {type: string}}
|
||||
- {in: query, name: libraryUncategorized, description: 仅列出未归类技能,仅支持 mine 或 created 视图;与 libraryCategoryId 互斥, schema: {type: boolean}}
|
||||
responses:
|
||||
"200": {description: 标准信封 data 为技能列表及分页信息;categories 含共享分类 value、label 和技能广场公开技能 count}
|
||||
"400": {$ref: "#/components/responses/Error"}
|
||||
"401": {$ref: "#/components/responses/Error"}
|
||||
"403": {$ref: "#/components/responses/Error"}
|
||||
"404": {$ref: "#/components/responses/Error"}
|
||||
/skills/library-categories:
|
||||
get:
|
||||
tags: [skills]
|
||||
summary: 获取当前用户可见的技能库分类和数量
|
||||
parameters:
|
||||
- {in: query, name: scope, schema: {type: string, enum: [mine, created], default: mine}}
|
||||
responses:
|
||||
"200": {description: 标准信封 data 为 categories、totalCount、uncategorizedCount;分类含 id、name、scope、count}
|
||||
"401": {$ref: "#/components/responses/Error"}
|
||||
post:
|
||||
tags: [skills]
|
||||
summary: 创建个人或平台技能库分类
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [name]
|
||||
properties:
|
||||
name: {type: string, minLength: 1, maxLength: 64}
|
||||
scope: {type: string, enum: [personal, platform], default: personal}
|
||||
responses:
|
||||
"200": {description: 标准信封 data.category 为新分类}
|
||||
"400": {$ref: "#/components/responses/Error"}
|
||||
"401": {$ref: "#/components/responses/Error"}
|
||||
"403": {$ref: "#/components/responses/Error"}
|
||||
/skills/library-categories/{id}:
|
||||
parameters:
|
||||
- {in: path, name: id, required: true, schema: {type: string}}
|
||||
delete:
|
||||
tags: [skills]
|
||||
summary: 删除技能库分类并解除相关技能归类,不删除技能
|
||||
responses:
|
||||
"200": {description: "标准信封 data 为 {deleted: true}"}
|
||||
"401": {$ref: "#/components/responses/Error"}
|
||||
"403": {$ref: "#/components/responses/Error"}
|
||||
"404": {$ref: "#/components/responses/Error"}
|
||||
/skills/{id}/library-category:
|
||||
parameters:
|
||||
- {in: path, name: id, required: true, description: 技能 ID, schema: {type: string}}
|
||||
patch:
|
||||
tags: [skills]
|
||||
summary: 为当前用户已加入或自建的技能设置/清除技能库分类
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [categoryId]
|
||||
properties:
|
||||
categoryId: {type: string, description: 个人分类或可见平台分类 ID;空字符串表示取消归类}
|
||||
responses:
|
||||
"200": {description: 标准信封 data.skill 为更新后的技能}
|
||||
"400": {$ref: "#/components/responses/Error"}
|
||||
"401": {$ref: "#/components/responses/Error"}
|
||||
"403": {$ref: "#/components/responses/Error"}
|
||||
"404": {$ref: "#/components/responses/Error"}
|
||||
/tools/{id}:
|
||||
parameters:
|
||||
- {in: path, name: id, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"mime"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"infinite-canvas/backend/internal/model"
|
||||
"infinite-canvas/backend/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -80,9 +82,15 @@ func RegisterSkillRoutes(r *gin.RouterGroup, svc *service.Service) {
|
||||
fail(c, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
libraryUncategorized, err := parseOptionalBool(c.Query("libraryUncategorized"))
|
||||
if err != nil {
|
||||
failService(c, service.BadAuthRequest("libraryUncategorized 必须是布尔值"))
|
||||
return
|
||||
}
|
||||
result, err := svc.Skills(user.ID, service.SkillListRequest{
|
||||
Page: page, PageSize: pageSize, Scope: c.DefaultQuery("scope", "public"),
|
||||
Search: c.Query("search"), Tag: c.Query("tag"), Sort: c.DefaultQuery("sort", "popular"),
|
||||
LibraryCategoryID: c.Query("libraryCategoryId"), LibraryUncategorized: libraryUncategorized,
|
||||
})
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
@@ -91,6 +99,52 @@ func RegisterSkillRoutes(r *gin.RouterGroup, svc *service.Service) {
|
||||
ok(c, result)
|
||||
})
|
||||
|
||||
r.GET("/skills/library-categories", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
result, err := svc.SkillLibraryCategories(user.ID, c.DefaultQuery("scope", "mine"))
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
ok(c, result)
|
||||
})
|
||||
|
||||
r.POST("/skills/library-categories", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
var req service.SkillLibraryCategoryMutationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
failService(c, service.BadAuthRequest("技能库分类数据格式无效"))
|
||||
return
|
||||
}
|
||||
category, err := svc.CreateSkillLibraryCategory(user, req)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
ok(c, gin.H{"category": category})
|
||||
})
|
||||
|
||||
r.DELETE("/skills/library-categories/:id", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
if err := svc.DeleteSkillLibraryCategory(user, c.Param("id")); err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
ok(c, gin.H{"deleted": true})
|
||||
})
|
||||
|
||||
r.GET("/skills/presets", func(c *gin.Context) {
|
||||
// 场景预设目录:公开只读的市场元数据(与 /openapi.yaml 同模式,无用户数据)。
|
||||
presets, err := svc.SkillPresets()
|
||||
@@ -129,6 +183,25 @@ func RegisterSkillRoutes(r *gin.RouterGroup, svc *service.Service) {
|
||||
ok(c, gin.H{"skill": skill})
|
||||
})
|
||||
|
||||
r.PATCH("/skills/:id/library-category", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
var req service.SkillLibraryCategoryAssignmentRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
failService(c, service.BadAuthRequest("技能库分类数据格式无效"))
|
||||
return
|
||||
}
|
||||
skill, err := svc.SetSkillLibraryCategory(user.ID, c.Param("id"), req.CategoryID)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
ok(c, gin.H{"skill": skill})
|
||||
})
|
||||
|
||||
r.GET("/skills/:id/files", func(c *gin.Context) {
|
||||
user, err := currentUser(c, svc)
|
||||
if err != nil {
|
||||
@@ -262,6 +335,15 @@ func RegisterSkillRoutes(r *gin.RouterGroup, svc *service.Service) {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
if user.Role == model.UserRoleAdmin {
|
||||
if err := svc.DeleteBuiltinSkill(user, c.Param("id")); err == nil {
|
||||
ok(c, gin.H{"deleted": true})
|
||||
return
|
||||
} else if !errors.Is(err, service.ErrNotBuiltinSkill) {
|
||||
failService(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := svc.DeleteSkill(user.ID, c.Param("id")); err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
|
||||
Reference in new issue
Block a user