package biz import ( "context" "fmt" "strings" "time" contextpkg "eino-project/internal/domain/context" "eino-project/internal/domain/monitor" "eino-project/internal/domain/vector" "github.com/go-kratos/kratos/v2/log" ) // CustomerRepo 智能客服数据仓库接口 type CustomerRepo interface { CheckSystemHealth(ctx context.Context) map[string]ServiceStatus } // CustomerUseCase 智能客服业务逻辑 type CustomerUseCase struct { repo CustomerRepo knowledgeSearcher vector.KnowledgeSearcher docProcessor vector.DocumentProcessor log *log.Helper contextManager contextpkg.ContextManager monitor monitor.Monitor } // NewCustomerUseCase 创建客户服务用例 func NewCustomerUseCase(repo CustomerRepo, logger log.Logger, knowledgeSearcher vector.KnowledgeSearcher, docProcessor vector.DocumentProcessor, contextManager contextpkg.ContextManager, monitor monitor.Monitor) *CustomerUseCase { return &CustomerUseCase{ repo: repo, log: log.NewHelper(logger), knowledgeSearcher: knowledgeSearcher, docProcessor: docProcessor, contextManager: contextManager, monitor: monitor, } } // SystemStatus 系统状态信息 type SystemStatus struct { Status bool `json:"status"` Services map[string]ServiceStatus `json:"services"` Version string `json:"version"` } // ServiceStatus 服务状态 type ServiceStatus struct { Name string `json:"name"` Status bool `json:"status"` Message string `json:"message"` } // Session 会话信息 type Session struct { SessionID string `json:"session_id"` Title string `json:"title"` CreatedAt time.Time `json:"created_at"` } // ChatMessage 聊天消息 type ChatMessage struct { SessionID string `json:"session_id"` Message string `json:"message"` Timestamp time.Time `json:"timestamp"` Type string `json:"type"` } // KnowledgeDocument 知识库文档 type KnowledgeDocument struct { ID string `json:"id"` FileName string `json:"file_name"` FileType string `json:"file_type"` ContentPreview string `json:"content_preview"` UploadTime time.Time `json:"upload_time"` Status string `json:"status"` } // OrderDetails 订单详情 type OrderDetails struct { OrderID string `json:"order_id"` Status string `json:"status"` Product string `json:"product"` Amount float64 `json:"amount"` CreateTime time.Time `json:"create_time"` NeedAI bool `json:"need_ai"` } // GetSystemStatus 获取系统状态 func (uc *CustomerUseCase) GetSystemStatus(ctx context.Context) (*SystemStatus, error) { uc.log.WithContext(ctx).Info("Getting system status") // 通过数据仓库检查系统健康状态 services := uc.repo.CheckSystemHealth(ctx) // 计算整体系统状态 overallStatus := true for _, service := range services { if !service.Status { overallStatus = false break } } return &SystemStatus{ Status: overallStatus, Services: services, Version: "1.0.0", }, nil } // CreateSession 创建新会话 func (uc *CustomerUseCase) CreateSession(ctx context.Context, title string) (*Session, error) { uc.log.WithContext(ctx).Infof("Creating new session with title: %s", title) // 生成会话ID(简单实现) sessionID := generateSessionID() session := &Session{ SessionID: sessionID, Title: title, CreatedAt: time.Now(), } return session, nil } // GetSessions 获取会话列表 func (uc *CustomerUseCase) GetSessions(ctx context.Context) ([]*Session, error) { uc.log.WithContext(ctx).Info("Getting sessions list") // Mock 数据 sessions := []*Session{ { SessionID: "session_001", Title: "产品咨询 - iPhone 15 Pro", CreatedAt: time.Now().Add(-2 * time.Hour), }, { SessionID: "session_002", Title: "订单问题 - 退换货流程", CreatedAt: time.Now().Add(-1 * time.Hour), }, { SessionID: "session_003", Title: "技术支持 - 软件安装问题", CreatedAt: time.Now().Add(-30 * time.Minute), }, } return sessions, nil } // ProcessChat 处理聊天消息 func (uc *CustomerUseCase) ProcessChat(ctx context.Context, sessionID, message string) (*ChatMessage, error) { uc.log.WithContext(ctx).Infof("Processing chat message for session: %s", sessionID) // 简单的消息处理逻辑 chatMessage := &ChatMessage{ SessionID: sessionID, Message: "收到您的消息:" + message + "。我正在为您处理,请稍候...", Timestamp: time.Now(), Type: "text", } return chatMessage, nil } // ListKnowledge 获取知识库文档列表 func (uc *CustomerUseCase) ListKnowledge(ctx context.Context) ([]*KnowledgeDocument, error) { uc.log.WithContext(ctx).Info("Getting knowledge documents list") // Mock 数据 documents := []*KnowledgeDocument{ { ID: "doc_001", FileName: "产品使用手册.pdf", FileType: "pdf", ContentPreview: "本手册详细介绍了产品的使用方法和注意事项...", UploadTime: time.Now().Add(-24 * time.Hour), Status: "active", }, { ID: "doc_002", FileName: "常见问题解答.txt", FileType: "txt", ContentPreview: "Q: 如何重置密码?A: 请点击登录页面的忘记密码链接...", UploadTime: time.Now().Add(-12 * time.Hour), Status: "active", }, { ID: "doc_003", FileName: "售后服务政策.docx", FileType: "docx", ContentPreview: "我们提供7天无理由退货,30天质量保证服务...", UploadTime: time.Now().Add(-6 * time.Hour), Status: "active", }, } return documents, nil } // QueryOrder 查询订单信息 func (uc *CustomerUseCase) QueryOrder(ctx context.Context, orderID string) (*OrderDetails, error) { uc.log.WithContext(ctx).Infof("Querying order: %s", orderID) // Mock 订单数据 orders := map[string]*OrderDetails{ "ORD001": { OrderID: "ORD001", Status: "已发货", Product: "iPhone 15 Pro 256GB 深空黑色", Amount: 8999.00, CreateTime: time.Now().Add(-48 * time.Hour), NeedAI: false, }, "ORD002": { OrderID: "ORD002", Status: "处理中", Product: "MacBook Pro 14英寸 M3芯片", Amount: 14999.00, CreateTime: time.Now().Add(-24 * time.Hour), NeedAI: true, }, "ORD003": { OrderID: "ORD003", Status: "已完成", Product: "AirPods Pro 第二代", Amount: 1899.00, CreateTime: time.Now().Add(-72 * time.Hour), NeedAI: false, }, } order, exists := orders[orderID] if !exists { return nil, ErrOrderNotFound } return order, nil } // generateSessionID 生成会话ID func generateSessionID() string { return "session_" + time.Now().Format("20060102150405") } // SearchKnowledge 搜索知识库 func (uc *CustomerUseCase) SearchKnowledge(ctx context.Context, query string, limit int) ([]vector.SearchResult, error) { uc.log.WithContext(ctx).Infof("Searching knowledge for query: %s", query) if limit <= 0 { limit = 5 } results, err := uc.knowledgeSearcher.SearchKnowledge(ctx, query, limit) if err != nil { uc.log.WithContext(ctx).Errorf("Failed to search knowledge: %v", err) return nil, fmt.Errorf("知识库搜索失败: %w", err) } uc.log.WithContext(ctx).Infof("Found %d knowledge results", len(results)) return results, nil } // ProcessKnowledgeUpload 处理知识库文档上传 func (uc *CustomerUseCase) ProcessKnowledgeUpload(ctx context.Context, content, title, category string) error { uc.log.WithContext(ctx).Infof("Processing knowledge upload: %s", title) if strings.TrimSpace(content) == "" { return fmt.Errorf("文档内容不能为空") } if strings.TrimSpace(title) == "" { return fmt.Errorf("文档标题不能为空") } // 处理文档并存储到向量数据库 documents, err := uc.docProcessor.ProcessKnowledgeDocument(ctx, content, title, category) if err != nil { uc.log.WithContext(ctx).Errorf("Failed to process knowledge document: %v", err) return fmt.Errorf("文档处理失败: %w", err) } uc.log.WithContext(ctx).Infof("Successfully processed knowledge document '%s' into %d chunks", title, len(documents)) return nil } // GetKnowledgeSummary 获取知识摘要 func (uc *CustomerUseCase) GetKnowledgeSummary(ctx context.Context, query string) (string, error) { uc.log.WithContext(ctx).Infof("Getting knowledge summary for query: %s", query) summary, err := uc.knowledgeSearcher.GetKnowledgeSummary(ctx, query) if err != nil { uc.log.WithContext(ctx).Errorf("Failed to get knowledge summary: %v", err) return "", fmt.Errorf("获取知识摘要失败: %w", err) } return summary, nil } // AnalyzeUserIntent 已废弃:改为使用 AIService.AnalyzeIntent(qwen3:8b) // 保留函数以避免外部调用崩溃,但将其标记为 deprecated,并直接返回 general_inquiry // Deprecated: use AIService.AnalyzeIntent instead. func (uc *CustomerUseCase) AnalyzeUserIntent(ctx context.Context, message string) (string, error) { uc.log.WithContext(ctx).Warn("AnalyzeUserIntent (rule-based) is deprecated, use AIService.AnalyzeIntent (LLM) instead") return "general_inquiry", nil } // ProcessIntelligentChat 处理智能聊天(结合知识库) func (uc *CustomerUseCase) ProcessIntelligentChat(ctx context.Context, message, sessionID string) (string, error) { startTime := time.Now() // 记录监控指标 defer func() { duration := time.Since(startTime) uc.monitor.RecordRequest(ctx, "intelligent_chat", duration, true) uc.monitor.RecordKnowledgeOperation(ctx, "query") }() // 获取或创建对话上下文 convCtx, err := uc.contextManager.GetContext(ctx, sessionID) if err != nil { // 创建新的上下文 convCtx, err = uc.contextManager.CreateContext(ctx, sessionID, "default_user") if err != nil { uc.log.Errorf("Failed to create context: %v", err) } } // 添加用户消息到上下文 if convCtx != nil { userMsg := contextpkg.Message{ Role: "user", Content: message, } uc.contextManager.AddMessage(ctx, sessionID, userMsg) } // 搜索相关知识 searchResults, err := uc.knowledgeSearcher.SearchKnowledge(ctx, message, 3) if err != nil { uc.log.Errorf("Failed to search knowledge: %v", err) uc.monitor.RecordVectorOperation(ctx, "search", false) return "抱歉,暂时无法获取相关信息,请稍后再试。", nil } uc.monitor.RecordVectorOperation(ctx, "search", true) // 构建上下文信息 var contextInfo string if len(searchResults) > 0 { contextInfo = "基于以下相关信息:\n" for _, result := range searchResults { contextInfo += fmt.Sprintf("- %s (相关度: %.2f)\n", result.Document.Content, result.Score) } contextInfo += "\n" } // 获取历史对话上下文 var historyContext string if convCtx != nil { recentMessages, err := uc.contextManager.GetRecentMessages(ctx, sessionID, 5) if err == nil && len(recentMessages) > 1 { historyContext = "基于我们之前的对话:\n" for _, msg := range recentMessages[:len(recentMessages)-1] { // 排除当前消息 historyContext += fmt.Sprintf("%s: %s\n", msg.Role, msg.Content) } historyContext += "\n" } } // 生成智能回复 response := "" if historyContext != "" { response += historyContext } if contextInfo != "" { response += contextInfo } response += "根据您的问题,我建议您:" // 基于意图和知识库内容生成回复 if strings.Contains(message, "订单") { response += "\n1. 检查订单状态和物流信息\n2. 如有问题可申请退换货\n3. 联系客服获取详细帮助" } else if strings.Contains(message, "技术") || strings.Contains(message, "问题") || strings.Contains(message, "错误") { response += "\n1. 查看相关技术文档和FAQ\n2. 尝试重启相关服务或清除缓存\n3. 检查网络连接和系统配置\n4. 如问题持续,请联系技术支持" } else if strings.Contains(message, "产品") || strings.Contains(message, "功能") { response += "\n1. 查看产品说明和使用指南\n2. 观看相关教程视频\n3. 尝试产品演示功能\n4. 联系销售顾问了解更多" } else { response += "\n1. 查看帮助文档和常见问题\n2. 使用搜索功能查找相关信息\n3. 联系在线客服获取实时帮助" } // 添加AI回复到上下文 if convCtx != nil { assistantMsg := contextpkg.Message{ Role: "assistant", Content: response, } uc.contextManager.AddMessage(ctx, sessionID, assistantMsg) } return response, nil } // handleOrderInquiry 处理订单咨询 func (uc *CustomerUseCase) handleOrderInquiry(ctx context.Context, message string) string { // 尝试从消息中提取订单号 orderID := extractOrderID(message) if orderID != "" { order, err := uc.QueryOrder(ctx, orderID) if err == nil { return fmt.Sprintf("您的订单 %s 状态为:%s。产品:%s,金额:%.2f元。", order.OrderID, order.Status, order.Product, order.Amount) } } return "请提供您的订单号,我来帮您查询订单状态。订单号通常以 ORD 开头。" } // handleProductInquiry 处理产品咨询 func (uc *CustomerUseCase) handleProductInquiry(ctx context.Context, message string) string { // 搜索相关知识 results, err := uc.SearchKnowledge(ctx, message, 3) if err != nil || len(results) == 0 { return "抱歉,我暂时没有找到相关的产品信息。请您详细描述您的问题,我会尽力为您解答。" } // 组合知识库内容 var knowledgeContent []string for _, result := range results { if result.Score > 0.6 { // 只使用高相关性的内容 knowledgeContent = append(knowledgeContent, result.Document.Content) } } if len(knowledgeContent) == 0 { return "抱歉,我没有找到与您问题高度相关的信息。请您提供更多详细信息。" } response := "根据我们的产品知识库,为您找到以下相关信息:\n\n" response += strings.Join(knowledgeContent, "\n\n") if len(response) > 500 { response = response[:500] + "...\n\n如需了解更多详细信息,请告诉我您具体想了解哪个方面。" } return response } // handleTechnicalSupport 处理技术支持 func (uc *CustomerUseCase) handleTechnicalSupport(ctx context.Context, message string) string { // 搜索技术相关知识 results, err := uc.SearchKnowledge(ctx, message+" 技术支持 解决方案", 3) if err != nil || len(results) == 0 { return "我理解您遇到了技术问题。请详细描述您的问题,包括:\n1. 具体的错误信息\n2. 操作步骤\n3. 使用的设备和系统\n我会为您提供针对性的解决方案。" } // 组合技术支持内容 var supportContent []string for _, result := range results { if result.Score > 0.5 { supportContent = append(supportContent, result.Document.Content) } } if len(supportContent) == 0 { return "请提供更多关于您技术问题的详细信息,我会为您查找相应的解决方案。" } response := "根据您的问题,我为您找到以下解决方案:\n\n" response += strings.Join(supportContent, "\n\n") if len(response) > 600 { response = response[:600] + "...\n\n如果以上方案无法解决您的问题,请联系我们的技术支持团队。" } return response } // handleGeneralInquiry 处理一般咨询 func (uc *CustomerUseCase) handleGeneralInquiry(ctx context.Context, message string) string { // 搜索相关知识 results, err := uc.SearchKnowledge(ctx, message, 2) if err != nil || len(results) == 0 { return "您好!我是智能客服助手,很高兴为您服务。请告诉我您需要什么帮助,我可以协助您:\n• 查询订单状态\n• 产品使用指导\n• 技术问题解答\n• 售后服务咨询" } // 使用最相关的知识回答 if results[0].Score > 0.7 { return "根据您的问题,我为您找到以下信息:\n\n" + results[0].Document.Content } return "我理解您的问题。请您提供更多详细信息,这样我能为您提供更准确的帮助。" } // extractOrderID 从消息中提取订单号 func extractOrderID(message string) string { // 简单的订单号提取逻辑 words := strings.Fields(strings.ToUpper(message)) for _, word := range words { if strings.HasPrefix(word, "ORD") && len(word) >= 6 { return word } } return "" }