You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
5.7 KiB
171 lines
5.7 KiB
package console
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"yunyan/comm"
|
|
"yunyan/lego/sys/log"
|
|
"yunyan/lego/sys/postgres"
|
|
"yunyan/pb"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// ============================ 第三方服务类别 CRUD ============================
|
|
//
|
|
// 后台「服务与环境配置 → 第三方服务 → 类别管理」的后端。规则见 model_svccategory.go 头部注释:
|
|
// 内置类别只能改展示属性,自定义类别可增删改,删除前查引用。
|
|
|
|
// seedSvcCategories 幂等 seed 内置类别:不存在则插入;已存在只补 builtin/voice 两个语义标记,
|
|
// 展示属性(label/short/color/sort/enabled)一律保留运营改过的值。启动时跑一次。
|
|
func seedSvcCategories() {
|
|
inserted := 0
|
|
for _, c := range builtinSvcCategories() {
|
|
exist := &SvcCategory{}
|
|
if err := postgres.FindOne(comm.TableSvcCategory, exist, "id=?", c.Id); err == nil {
|
|
if exist.Builtin != true || exist.Voice != c.Voice {
|
|
exist.Builtin = true
|
|
exist.Voice = c.Voice
|
|
_ = postgres.Save(comm.TableSvcCategory, exist)
|
|
}
|
|
continue
|
|
}
|
|
if err := postgres.Insert(comm.TableSvcCategory, &c); err != nil {
|
|
log.Warnf("console: seed 服务类别 %d 失败: %v", c.Id, err)
|
|
continue
|
|
}
|
|
inserted++
|
|
}
|
|
if inserted > 0 {
|
|
log.Infof("console: 内置服务类别 seed 完成 inserted=%d", inserted)
|
|
}
|
|
}
|
|
|
|
// getSvcCategories 全部类别(含停用的,前端自己按 enabled 决定在哪些地方展示),按 sort,id 升序。
|
|
func (this *serverComp) getSvcCategories(c *gin.Context) {
|
|
items := make([]*SvcCategory, 0, 16)
|
|
if err := postgres.Table(comm.TableSvcCategory).Order("sort ASC, id ASC").Find(&items).Error; err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, err.Error())
|
|
return
|
|
}
|
|
for _, it := range items {
|
|
fillSvcCategoryRuntime(it)
|
|
}
|
|
writeOK(c, gin.H{"items": items})
|
|
}
|
|
|
|
// saveSvcCategory 新增或更新一个类别。
|
|
// - id=0 → 新增自定义类别,id 由服务端从 100 起分配;
|
|
// - id>0 → 更新:内置类别只接受展示属性,builtin/id 不可改;voice 内置的也不让改
|
|
// (音色预填是按类别语义定的,改了新建 TTS 服务时 languages 就不预填了)。
|
|
func (this *serverComp) saveSvcCategory(c *gin.Context) {
|
|
var m SvcCategory
|
|
if err := c.ShouldBindJSON(&m); err != nil {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, err.Error())
|
|
return
|
|
}
|
|
m.Label = strings.TrimSpace(m.Label)
|
|
m.Short = strings.TrimSpace(m.Short)
|
|
if m.Label == "" || m.Short == "" {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, "名称与角标 必填")
|
|
return
|
|
}
|
|
if len([]rune(m.Short)) > 8 {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, "角标最多 8 个字符(卡片上放不下)")
|
|
return
|
|
}
|
|
if m.Color == "" {
|
|
m.Color = "#475569"
|
|
}
|
|
if m.Bg == "" {
|
|
m.Bg = "#f1f5f9"
|
|
}
|
|
|
|
if m.Id == 0 {
|
|
// 新增:分配 id。max(id, 99)+1 保证自定义从 100 起,与内置永不相撞。
|
|
var maxId int32
|
|
_ = postgres.Table(comm.TableSvcCategory).Select("COALESCE(MAX(id), 0)").Scan(&maxId).Error
|
|
if maxId < svcCategoryCustomIdStart-1 {
|
|
maxId = svcCategoryCustomIdStart - 1
|
|
}
|
|
m.Id = maxId + 1
|
|
m.Builtin = false
|
|
if err := postgres.Insert(comm.TableSvcCategory, &m); err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, err.Error())
|
|
return
|
|
}
|
|
fillSvcCategoryRuntime(&m)
|
|
writeOK(c, &m)
|
|
return
|
|
}
|
|
|
|
old := &SvcCategory{}
|
|
if err := postgres.FindOne(comm.TableSvcCategory, old, "id=?", m.Id); err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, "类别不存在: "+strconv.Itoa(int(m.Id)))
|
|
return
|
|
}
|
|
// 语义标记只认库里的:内置的 builtin/voice 不可改,自定义的 voice 可改、builtin 恒 false。
|
|
m.Builtin = old.Builtin
|
|
if old.Builtin {
|
|
m.Voice = old.Voice
|
|
}
|
|
if err := postgres.Save(comm.TableSvcCategory, &m); err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, err.Error())
|
|
return
|
|
}
|
|
fillSvcCategoryRuntime(&m)
|
|
writeOK(c, &m)
|
|
}
|
|
|
|
// delSvcCategory 删除自定义类别。内置类别拒绝;被服务或模板引用的也拒绝,并把引用列出来。
|
|
func (this *serverComp) delSvcCategory(c *gin.Context) {
|
|
var req struct {
|
|
Id int32 `json:"id"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil || req.Id == 0 {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, "id 必填")
|
|
return
|
|
}
|
|
old := &SvcCategory{}
|
|
if err := postgres.FindOne(comm.TableSvcCategory, old, "id=?", req.Id); err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, "类别不存在")
|
|
return
|
|
}
|
|
if old.Builtin {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError,
|
|
"内置类别不能删除:运行时与内置模板按这个 id 引用(如 MT=3 用于 user_translate、实名=11 决定不下发)。不想用可以停用。")
|
|
return
|
|
}
|
|
// 引用检查:服务实例的 categories 是逗号分隔多值,逐条拆开比对,别用 LIKE('1' 会命中 '10'/'11')。
|
|
svcs := make([]*ThirdSvcConfig, 0)
|
|
_ = postgres.Find(comm.TableSvcConfig, &svcs, "")
|
|
refs := make([]string, 0, 4)
|
|
for _, s := range svcs {
|
|
if categoriesHas(s.Categories, req.Id) {
|
|
scope := s.AppName
|
|
if scope == "" {
|
|
scope = "<全局>"
|
|
}
|
|
refs = append(refs, "服务 "+s.Id+"("+scope+")")
|
|
}
|
|
}
|
|
tpls := make([]*ThirdSvcTemplate, 0)
|
|
_ = postgres.Find(comm.TableThirdSvcTemplate, &tpls, "category=?", req.Id)
|
|
for _, t := range tpls {
|
|
refs = append(refs, "模板 "+t.Id)
|
|
}
|
|
if len(refs) > 0 {
|
|
if len(refs) > 6 {
|
|
refs = append(refs[:6], "…等 "+strconv.Itoa(len(refs))+" 处")
|
|
}
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, "该类别仍被引用,先把它们改到别的类别:"+strings.Join(refs, "、"))
|
|
return
|
|
}
|
|
if err := postgres.Delete(comm.TableSvcCategory, "id=?", req.Id); err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, err.Error())
|
|
return
|
|
}
|
|
writeOK(c, gin.H{"deleted": 1, "at": time.Now().Unix()})
|
|
}
|
|
|