refactor: 将客服后端重构为宿主可嵌入模块

- 注入数据库、运行时配置、统一响应、文件存储和平台 AI 能力,补充业务读写工具与客户快捷操作契约。

- 移除模块内重复的组织、客户、工单、标签、技能、旧工作流、MCP 和迁移实现,将身份权限与业务主体交由宿主管理。

- 使用 libSQL 重构向量存储,并完善图片消息、访客身份、排队调度、企业微信和支持聊天页面。

- 统一 HTTP、DTO 与 WebSocket 的 snake_case 协议,补齐模块初始化、业务动作和公共载荷等回归测试。
This commit is contained in:
t
2026-08-28 22:23:13 +08:00
parent 6845c728f8
commit 18c9354095
377 changed files with 13199 additions and 22881 deletions
+26 -24
View File
@@ -17,25 +17,23 @@ import (
"gorm.io/gorm/schema"
)
func TestAgentRunServiceFindsWorkflowAuditDetail(t *testing.T) {
func TestAgentRunServiceFindsAuditDetail(t *testing.T) {
db := setupAgentRunServiceTestDB(t)
now := time.Now()
endedAt := now.Add(time.Second)
run := &models.AgentRun{
ConversationID: 11,
AIAgentID: 12,
WorkflowRunID: 13,
Status: "completed",
StartedAt: now,
EndedAt: &endedAt,
CreatedAt: now,
UpdatedAt: now,
Status: "completed",
StartedAt: now,
EndedAt: &endedAt,
CreatedAt: now,
UpdatedAt: now,
}
if err := db.Create(run).Error; err != nil {
t.Fatalf("create agent run: %v", err)
}
if err := db.Create(&models.AgentStep{AgentRunID: run.ID, StepType: "workflow", Status: "completed", StartedAt: now, EndedAt: &endedAt, CreatedAt: now}).Error; err != nil {
if err := db.Create(&models.AgentStep{AgentRunID: run.ID, StepType: "tool", Status: "completed", StartedAt: now, EndedAt: &endedAt, CreatedAt: now}).Error; err != nil {
t.Fatalf("create agent step: %v", err)
}
if err := db.Create(&models.AgentToolCall{AgentRunID: run.ID, ToolCode: "knowledge.retrieve", Status: "completed", CreatedAt: now}).Error; err != nil {
@@ -75,26 +73,30 @@ func TestAgentRunServiceRecordsAgentLoopToolCall(t *testing.T) {
}
}
func TestAgentRunServiceRecordsResumedToolCall(t *testing.T) {
func TestAgentRunServiceRecallsLatestBusinessToolResultPerConversation(t *testing.T) {
db := setupAgentRunServiceTestDB(t)
now := time.Now()
run := &models.AgentRun{Status: "interrupted", StartedAt: now, CreatedAt: now, UpdatedAt: now}
if err := db.Create(run).Error; err != nil {
t.Fatalf("create interrupted run: %v", err)
runs := []models.AgentRun{
{ConversationID: 21, Status: "completed", StartedAt: now, CreatedAt: now, UpdatedAt: now},
{ConversationID: 21, Status: "completed", StartedAt: now.Add(time.Second), CreatedAt: now, UpdatedAt: now},
{ConversationID: 22, Status: "completed", StartedAt: now, CreatedAt: now, UpdatedAt: now},
}
err := AgentRunService.RecordResume(db, run.ID, 0, "completed", "操作已执行", &AgentLoopToolCallInput{
ToolCode: "crm/update_customer", RiskLevel: "write", RequireConfirm: true,
Status: "completed", ArgumentsPreview: `{"name":"Ada"}`, ResultPreview: "updated",
})
if err != nil {
t.Fatalf("RecordResume returned error: %v", err)
if err := db.Create(&runs).Error; err != nil {
t.Fatalf("create runs: %v", err)
}
item, steps, toolCalls := AgentRunService.GetDetail(run.ID)
if item == nil || item.Status != "completed" || len(steps) != 1 || steps[0].StepType != "resume" {
t.Fatalf("unexpected resumed run audit: item=%#v steps=%#v", item, steps)
calls := []models.AgentToolCall{
{AgentRunID: runs[0].ID, ToolCode: "business/card_package_catalog", Status: "completed", ResultPreview: `[{"sequence":1,"current_start_at":"old"}]`, CreatedAt: now},
{AgentRunID: runs[1].ID, ToolCode: "business/card_package_catalog", Status: "completed", ResultPreview: `[{"sequence":1,"current_start_at":"new"}]`, CreatedAt: now},
{AgentRunID: runs[1].ID, ToolCode: "builtin/conversation_context", Status: "completed", ResultPreview: "ignore", CreatedAt: now},
{AgentRunID: runs[1].ID, ToolCode: "business/card_auto_renewal_catalog", Status: "completed", ResultPreview: "stale", CreatedAt: now.Add(-20 * time.Minute)},
{AgentRunID: runs[2].ID, ToolCode: "business/card_package_catalog", Status: "completed", ResultPreview: "other conversation", CreatedAt: now},
}
if len(toolCalls) != 1 || toolCalls[0].AgentStepID != steps[0].ID || !toolCalls[0].RequireConfirm || toolCalls[0].Status != "completed" {
t.Fatalf("unexpected resumed tool audit: %#v", toolCalls)
if err := db.Create(&calls).Error; err != nil {
t.Fatalf("create calls: %v", err)
}
memory := AgentRunService.FindRecentBusinessToolMemory(21, 4)
if len(memory) != 1 || memory[0].ToolCode != "business/card_package_catalog" || !strings.Contains(memory[0].Result, "new") {
t.Fatalf("unexpected business memory: %#v", memory)
}
}