refactor: 将客服后端重构为宿主可嵌入模块
- 注入数据库、运行时配置、统一响应、文件存储和平台 AI 能力,补充业务读写工具与客户快捷操作契约。 - 移除模块内重复的组织、客户、工单、标签、技能、旧工作流、MCP 和迁移实现,将身份权限与业务主体交由宿主管理。 - 使用 libSQL 重构向量存储,并完善图片消息、访客身份、排队调度、企业微信和支持聊天页面。 - 统一 HTTP、DTO 与 WebSocket 的 snake_case 协议,补齐模块初始化、业务动作和公共载荷等回归测试。
This commit is contained in:
@@ -2,14 +2,9 @@ package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/ai/rag"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/ai/workflow/dsl"
|
||||
workflowregistry "code.tczkiot.com/wlw/ai-agent/internal/ai/workflow/registry"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/models"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/dto"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/dto/request"
|
||||
@@ -133,14 +128,6 @@ func (s *knowledgeBaseService) DeleteKnowledgeBase(id int64) error {
|
||||
return errorsx.InvalidParamI18n("error.e0283")
|
||||
}
|
||||
|
||||
referencingWorkflows := s.findWorkflowReferencesByKnowledgeBaseID(id)
|
||||
if len(referencingWorkflows) > 0 {
|
||||
if len(referencingWorkflows) == 1 {
|
||||
return errorsx.Forbidden(fmt.Sprintf("知识库正在被流程「%s」使用,请先从知识检索节点中移除", referencingWorkflows[0]))
|
||||
}
|
||||
return errorsx.Forbidden(fmt.Sprintf("知识库正在被 %d 个流程使用,请先从知识检索节点中移除", len(referencingWorkflows)))
|
||||
}
|
||||
|
||||
if err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
if err := repositories.KnowledgeDocumentRepository.DeleteByKnowledgeBaseID(ctx.Tx, id); err != nil {
|
||||
return err
|
||||
@@ -156,86 +143,6 @@ func (s *knowledgeBaseService) DeleteKnowledgeBase(id int64) error {
|
||||
return rag.Index.RemoveKnowledgeBaseIndex(context.Background(), id)
|
||||
}
|
||||
|
||||
func (s *knowledgeBaseService) findWorkflowReferencesByKnowledgeBaseID(id int64) []string {
|
||||
names := make(map[string]struct{})
|
||||
workflows := repositories.AIWorkflowRepository.Find(sqls.DB(), sqls.NewCnd().Eq("status", enums.StatusOk))
|
||||
workflowNames := make(map[int64]string, len(workflows))
|
||||
for _, workflow := range workflows {
|
||||
name := strings.TrimSpace(workflow.Name)
|
||||
if name == "" {
|
||||
name = fmt.Sprintf("ID %d", workflow.ID)
|
||||
}
|
||||
workflowNames[workflow.ID] = name
|
||||
if workflowDefinitionUsesKnowledgeBase(workflow.DraftDefinition, id) {
|
||||
names[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
versions := repositories.AIWorkflowVersionRepository.Find(sqls.DB(), sqls.NewCnd())
|
||||
for _, version := range versions {
|
||||
if !workflowDefinitionUsesKnowledgeBase(version.Definition, id) {
|
||||
continue
|
||||
}
|
||||
name := workflowNames[version.WorkflowID]
|
||||
if strings.TrimSpace(name) == "" {
|
||||
name = fmt.Sprintf("ID %d", version.WorkflowID)
|
||||
}
|
||||
names[name] = struct{}{}
|
||||
}
|
||||
ret := make([]string, 0, len(names))
|
||||
for name := range names {
|
||||
ret = append(ret, name)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func workflowDefinitionUsesKnowledgeBase(definition string, id int64) bool {
|
||||
definition = strings.TrimSpace(definition)
|
||||
if definition == "" {
|
||||
return false
|
||||
}
|
||||
var def dsl.Definition
|
||||
if err := json.Unmarshal([]byte(definition), &def); err != nil {
|
||||
return false
|
||||
}
|
||||
for _, node := range def.Nodes {
|
||||
if strings.TrimSpace(node.Type) != workflowregistry.NodeTypeKnowledgeRetrieve {
|
||||
continue
|
||||
}
|
||||
for _, knowledgeBaseID := range knowledgeBaseIDsFromWorkflowNodeConfig(node.Data.Config) {
|
||||
if knowledgeBaseID == id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func knowledgeBaseIDsFromWorkflowNodeConfig(raw json.RawMessage) []int64 {
|
||||
if len(raw) == 0 {
|
||||
return nil
|
||||
}
|
||||
var cfg map[string]any
|
||||
if err := json.Unmarshal(raw, &cfg); err != nil {
|
||||
return nil
|
||||
}
|
||||
items, ok := cfg["knowledgeBaseIds"].([]any)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
ret := make([]int64, 0, len(items))
|
||||
for _, item := range items {
|
||||
switch value := item.(type) {
|
||||
case float64:
|
||||
ret = append(ret, int64(value))
|
||||
case int64:
|
||||
ret = append(ret, value)
|
||||
case int:
|
||||
ret = append(ret, int64(value))
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *knowledgeBaseService) UpdateSort(ids []int64) error {
|
||||
return sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
for i, id := range ids {
|
||||
|
||||
Reference in New Issue
Block a user