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.
 
 
 
 
 
 

218 lines
6.5 KiB

package console
import (
"strings"
"time"
"yunyan/comm"
"yunyan/pb"
"github.com/gin-gonic/gin"
)
// ============================ 会议记录服务配置 (echomeetsvc) ============================
//
// admin「会议记录服务配置」页的后端:读写**应用业务库**里的 echomeet_svc_config 表
// (识别/翻译/总结三类,各自按优先级;识别可配支持语言;总结为具体模型条目)。全局一份、无 app_name。
//
// 业务库连接按部署 appId 从 registry 动态取(与 modulecfg 同机制);
// 密钥字段(是否密钥以 comm.EchoSvcCatalog 为准)AES-GCM 加密落库、返回前端脱敏为 comm.EncMask。
// 改配置后广播 ConfigKindEchomeetSvc;home 侧 echomeet 改配置需重启生效。
// echoSvcCatalog 返回会议记录服务字段目录(前端据此渲染各服务商表单、识别语言选择器)。与选中应用无关。
func (this *serverComp) echoSvcCatalog(c *gin.Context) {
writeOK(c, gin.H{
"catalog": comm.EchoSvcCatalog,
"categories": []gin.H{
{"value": comm.EchoSvcCatASR, "label": "识别"},
{"value": comm.EchoSvcCatMT, "label": "翻译"},
{"value": comm.EchoSvcCatLLM, "label": "总结"},
},
})
}
// echoSvcList 列出某部署业务库里的会议记录服务配置;密钥脱敏。
func (this *serverComp) echoSvcList(c *gin.Context) {
var req struct {
AppId uint32 `json:"app_id"`
}
_ = c.ShouldBindJSON(&req)
if req.AppId == 0 {
writeErr(c, pb.ErrorCode_ReqParameterError, "app_id 必填")
return
}
db, err := this.module.registry.getServiceDB(req.AppId)
if err != nil {
writeErr(c, pb.ErrorCode_DBError, "连接应用业务库失败: "+err.Error())
return
}
if err := comm.EnsureEchoSvcTable(db); err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
rows, err := comm.LoadEchoSvcConfigs(db)
if err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
for _, r := range rows {
r.Values = comm.MaskEchoValues(r.Category, r.Provider, r.Values)
}
writeOK(c, gin.H{"items": rows})
}
// echoSvcSave 保存单条服务配置:新增/更新(按 id)。
// 是否密钥以目录为准(不信前端);密钥字段回传占位/空则保留原密文,否则加密新明文;目录外的键忽略。
func (this *serverComp) echoSvcSave(c *gin.Context) {
var req struct {
AppId uint32 `json:"app_id"`
Config comm.EchoSvcConfig `json:"config"`
}
if err := c.ShouldBindJSON(&req); err != nil {
writeErr(c, pb.ErrorCode_ReqParameterError, err.Error())
return
}
cfg := req.Config
cfg.Id = strings.TrimSpace(cfg.Id)
cfg.Provider = strings.TrimSpace(cfg.Provider)
if req.AppId == 0 || cfg.Id == "" {
writeErr(c, pb.ErrorCode_ReqParameterError, "app_id 与 id 必填")
return
}
def := comm.FindEchoSvcDef(cfg.Category, cfg.Provider)
if def == nil {
writeErr(c, pb.ErrorCode_ReqParameterError, "未知的服务类别/服务商组合")
return
}
db, err := this.module.registry.getServiceDB(req.AppId)
if err != nil {
writeErr(c, pb.ErrorCode_DBError, "连接应用业务库失败: "+err.Error())
return
}
if err := comm.EnsureEchoSvcTable(db); err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
// 取旧行以保留密钥密文。
old := &comm.EchoSvcConfig{}
hasOld := db.FindOne(comm.TableEchomeetSvcConfig, old, "id=?", cfg.Id) == nil && old.Id != ""
// 合法字段过滤 + 密钥处理。
allowed := make(map[string]bool, len(def.Fields))
for _, f := range def.Fields {
allowed[f.Key] = true
}
encKey := this.options.EncryptKey
stored := make(map[string]string, len(cfg.Values))
for k, v := range cfg.Values {
if !allowed[k] {
continue
}
if comm.EchoSvcFieldIsSecret(cfg.Category, cfg.Provider, k) {
if v == "" || v == comm.EncMask {
if hasOld {
stored[k] = old.Values[k] // 保留原密文
}
continue
}
ct, ee := comm.Encrypt(encKey, v)
if ee != nil {
writeErr(c, pb.ErrorCode_SystemError, "加密失败: "+ee.Error())
return
}
stored[k] = ct
continue
}
stored[k] = v
}
cfg.Values = stored
now := time.Now().Unix()
cfg.Updatetime = now
if hasOld {
cfg.Createtime = old.Createtime
if e := db.Save(comm.TableEchomeetSvcConfig, &cfg); e != nil {
writeErr(c, pb.ErrorCode_DBError, e.Error())
return
}
} else {
cfg.Createtime = now
if e := db.Insert(comm.TableEchomeetSvcConfig, &cfg); e != nil {
writeErr(c, pb.ErrorCode_DBError, e.Error())
return
}
}
broadcastConfigChanged(comm.ConfigKindEchomeetSvc, "update", 0)
writeOK(c, gin.H{"id": cfg.Id})
}
// echoSvcDel 删除一条服务配置。
func (this *serverComp) echoSvcDel(c *gin.Context) {
var req struct {
AppId uint32 `json:"app_id"`
Id string `json:"id"`
}
_ = c.ShouldBindJSON(&req)
req.Id = strings.TrimSpace(req.Id)
if req.AppId == 0 || req.Id == "" {
writeErr(c, pb.ErrorCode_ReqParameterError, "app_id 与 id 必填")
return
}
db, err := this.module.registry.getServiceDB(req.AppId)
if err != nil {
writeErr(c, pb.ErrorCode_DBError, "连接应用业务库失败: "+err.Error())
return
}
if err := db.Delete(comm.TableEchomeetSvcConfig, "id=?", req.Id); err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
broadcastConfigChanged(comm.ConfigKindEchomeetSvc, "delete", 0)
writeOK(c, gin.H{"deleted": req.Id})
}
// echoSvcReorder 批量调整同类内优先级(拖拽排序后提交)。
func (this *serverComp) echoSvcReorder(c *gin.Context) {
var req struct {
AppId uint32 `json:"app_id"`
Items []struct {
Id string `json:"id"`
Priority int32 `json:"priority"`
} `json:"items"`
}
if err := c.ShouldBindJSON(&req); err != nil {
writeErr(c, pb.ErrorCode_ReqParameterError, err.Error())
return
}
if req.AppId == 0 {
writeErr(c, pb.ErrorCode_ReqParameterError, "app_id 必填")
return
}
db, err := this.module.registry.getServiceDB(req.AppId)
if err != nil {
writeErr(c, pb.ErrorCode_DBError, "连接应用业务库失败: "+err.Error())
return
}
now := time.Now().Unix()
updated := 0
for _, it := range req.Items {
id := strings.TrimSpace(it.Id)
if id == "" {
continue
}
row := &comm.EchoSvcConfig{}
if e := db.FindOne(comm.TableEchomeetSvcConfig, row, "id=?", id); e != nil || row.Id == "" {
continue
}
row.Priority = it.Priority
row.Updatetime = now
if e := db.Save(comm.TableEchomeetSvcConfig, row); e != nil {
writeErr(c, pb.ErrorCode_DBError, e.Error())
return
}
updated++
}
broadcastConfigChanged(comm.ConfigKindEchomeetSvc, "update", 0)
writeOK(c, gin.H{"updated": updated})
}