diff --git a/internal/ai/runtime/graphs/analyze_conversation_graph.go b/internal/ai/runtime/graphs/analyze_conversation_graph.go index d7fd9e6..1406016 100644 --- a/internal/ai/runtime/graphs/analyze_conversation_graph.go +++ b/internal/ai/runtime/graphs/analyze_conversation_graph.go @@ -84,9 +84,6 @@ func buildAnalyzeConversationResult(conversation models.Conversation, messages [ func buildConversationSummary(conversation models.Conversation, messages []models.Message, input AnalyzeConversationInput) string { parts := make([]string, 0, 4) - if strings.TrimSpace(conversation.Subject) != "" { - parts = append(parts, "会话主题:"+strings.TrimSpace(conversation.Subject)) - } if input.ObservedIssue != "" { parts = append(parts, "当前问题:"+input.ObservedIssue) } else if strings.TrimSpace(conversation.LastMessageSummary) != "" { @@ -103,7 +100,6 @@ func buildConversationSummary(conversation models.Conversation, messages []model func buildConversationCorpus(conversation models.Conversation, messages []models.Message, input AnalyzeConversationInput) string { parts := make([]string, 0, len(messages)+4) - parts = append(parts, strings.TrimSpace(conversation.Subject)) parts = append(parts, strings.TrimSpace(conversation.LastMessageSummary)) parts = append(parts, input.Goal, input.ObservedIssue, input.AdditionalContext) for i := range messages { @@ -200,9 +196,6 @@ func recommendQuestions(intent string, signals []string, input AnalyzeConversati func buildConversationAnalysisFacts(conversation models.Conversation, messages []models.Message) []string { facts := make([]string, 0, 4) - if strings.TrimSpace(conversation.Subject) != "" { - facts = append(facts, "会话主题:"+strings.TrimSpace(conversation.Subject)) - } if strings.TrimSpace(conversation.LastMessageSummary) != "" { facts = append(facts, "最近摘要:"+strings.TrimSpace(conversation.LastMessageSummary)) } diff --git a/internal/ai/runtime/graphs/analyze_conversation_graph_test.go b/internal/ai/runtime/graphs/analyze_conversation_graph_test.go index 14e6f9d..6eecd41 100644 --- a/internal/ai/runtime/graphs/analyze_conversation_graph_test.go +++ b/internal/ai/runtime/graphs/analyze_conversation_graph_test.go @@ -9,7 +9,6 @@ import ( func TestBuildAnalyzeConversationResult_RecommendsHandoffForComplaint(t *testing.T) { conversation := models.Conversation{ - Subject: "用户投诉扣费异常", LastMessageSummary: "用户反馈被重复扣费,并要求人工处理", } messages := []models.Message{ @@ -33,7 +32,6 @@ func TestBuildAnalyzeConversationResult_RecommendsHandoffForComplaint(t *testing func TestBuildAnalyzeConversationResult_RecommendsPrepareTicket(t *testing.T) { conversation := models.Conversation{ - Subject: "订单无法支付", LastMessageSummary: "用户要求登记问题并尽快处理", } messages := []models.Message{ diff --git a/internal/ai/runtime/graphs/create_ticket_graph.go b/internal/ai/runtime/graphs/create_ticket_graph.go index 454ced0..e55b8fa 100644 --- a/internal/ai/runtime/graphs/create_ticket_graph.go +++ b/internal/ai/runtime/graphs/create_ticket_graph.go @@ -114,7 +114,7 @@ func (g *CreateTicketGraph) buildCreateRequest(argumentsInJSON string) (request. req.Priority = args.Priority req.Severity = args.Severity if req.Title == "" { - req.Title = strings.TrimSpace(g.conversation.Subject) + req.Title = strings.TrimSpace(g.conversation.LastMessageSummary) } if req.Description == "" { req.Description = strings.TrimSpace(g.conversation.LastMessageSummary) diff --git a/internal/ai/runtime/graphs/prepare_ticket_draft_graph.go b/internal/ai/runtime/graphs/prepare_ticket_draft_graph.go index 3efd219..4291e9f 100644 --- a/internal/ai/runtime/graphs/prepare_ticket_draft_graph.go +++ b/internal/ai/runtime/graphs/prepare_ticket_draft_graph.go @@ -100,8 +100,6 @@ func buildDraftTitle(conversation models.Conversation, input PrepareTicketDraftI return limitText(input.Title, 80) case input.Issue != "": return limitText(input.Issue, 80) - case strings.TrimSpace(conversation.Subject) != "": - return limitText(conversation.Subject, 80) case strings.TrimSpace(conversation.LastMessageSummary) != "": return limitText(conversation.LastMessageSummary, 80) default: @@ -144,9 +142,6 @@ func hasSufficientIssueContext(input PrepareTicketDraftInput, description string func buildConversationFacts(conversation models.Conversation, messages []models.Message) []string { facts := make([]string, 0, 4) - if strings.TrimSpace(conversation.Subject) != "" { - facts = append(facts, "会话主题:"+strings.TrimSpace(conversation.Subject)) - } if strings.TrimSpace(conversation.LastMessageSummary) != "" { facts = append(facts, "最近摘要:"+strings.TrimSpace(conversation.LastMessageSummary)) } diff --git a/internal/ai/runtime/graphs/prepare_ticket_draft_graph_test.go b/internal/ai/runtime/graphs/prepare_ticket_draft_graph_test.go index 8a3d35f..6361ca7 100644 --- a/internal/ai/runtime/graphs/prepare_ticket_draft_graph_test.go +++ b/internal/ai/runtime/graphs/prepare_ticket_draft_graph_test.go @@ -9,7 +9,6 @@ import ( func TestBuildPrepareTicketDraftResult_UsesConversationFallbacks(t *testing.T) { conversation := models.Conversation{ - Subject: "企业微信登录异常", LastMessageSummary: "用户反馈企业微信扫码后页面空白,无法进入工作台", } messages := []models.Message{ @@ -38,7 +37,6 @@ func TestBuildPrepareTicketDraftResult_UsesConversationFallbacks(t *testing.T) { func TestBuildPrepareTicketDraftResult_ReadyWithExplicitIssue(t *testing.T) { conversation := models.Conversation{ - Subject: "订单支付失败", LastMessageSummary: "用户反馈连续支付失败", } diff --git a/internal/ai/runtime/graphs/triage_service_request_graph_test.go b/internal/ai/runtime/graphs/triage_service_request_graph_test.go index bf74b6a..6dd0a8a 100644 --- a/internal/ai/runtime/graphs/triage_service_request_graph_test.go +++ b/internal/ai/runtime/graphs/triage_service_request_graph_test.go @@ -9,7 +9,6 @@ import ( func TestTriageServiceRequestResult_PrepareTicket(t *testing.T) { conversation := models.Conversation{ - Subject: "支付失败需要登记工单", LastMessageSummary: "用户要求建单跟进支付失败问题", } messages := []models.Message{ @@ -31,7 +30,6 @@ func TestTriageServiceRequestResult_PrepareTicket(t *testing.T) { func TestTriageServiceRequestResult_Handoff(t *testing.T) { conversation := models.Conversation{ - Subject: "投诉转人工", LastMessageSummary: "用户要求人工处理扣费投诉", } messages := []models.Message{ diff --git a/internal/builders/conversation_builder.go b/internal/builders/conversation_builder.go index 667f4b4..b4c722c 100644 --- a/internal/builders/conversation_builder.go +++ b/internal/builders/conversation_builder.go @@ -19,7 +19,7 @@ func BuildConversation(item *models.Conversation) response.ConversationResponse AIAgentID: item.AIAgentID, ChannelID: item.ChannelID, CustomerID: item.CustomerID, - Subject: item.Subject, + CustomerName: buildConversationCustomerName(item.CustomerID), Status: item.Status, ServiceMode: item.ServiceMode, Priority: item.Priority, @@ -62,6 +62,16 @@ func BuildConversation(item *models.Conversation) response.ConversationResponse return ret } +func buildConversationCustomerName(customerID int64) string { + if customerID <= 0 { + return "" + } + if customer := services.CustomerService.Get(customerID); customer != nil { + return strings.TrimSpace(customer.Name) + } + return "" +} + func BuildParticipantResponses(conversationID int64) []response.ConversationParticipantResponse { list := services.ConversationParticipantService.Find(sqls.NewCnd().Eq("conversation_id", conversationID).Asc("id")) if len(list) == 0 { diff --git a/internal/controllers/dashboard/conversation_controller.go b/internal/controllers/dashboard/conversation_controller.go index 67b307e..e1be192 100644 --- a/internal/controllers/dashboard/conversation_controller.go +++ b/internal/controllers/dashboard/conversation_controller.go @@ -36,7 +36,7 @@ func (c *ConversationController) AnyList() *web.JsonResult { if keyword, _ := params.Get(c.Ctx, "keyword"); strs.IsNotBlank(keyword) { keywordLike := "%" + strings.TrimSpace(keyword) + "%" - cnd.Where("subject LIKE ? OR last_message_summary LIKE ?", keywordLike, keywordLike) + cnd.Where("last_message_summary LIKE ? OR customer_id IN (SELECT id FROM t_customer WHERE name LIKE ?)", keywordLike, keywordLike) } // 标签搜索 diff --git a/internal/models/models.go b/internal/models/models.go index cf29209..80287b0 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -327,7 +327,6 @@ type Conversation struct { AIAgentID int64 `gorm:"type:bigint;not null;default:0;index"` // AIAgentID 为当前会话绑定的 AI Agent ID。 ChannelID int64 `gorm:"type:bigint;not null;default:0;index"` // ChannelID 为该会话来源接入渠道ID。 CustomerID int64 `gorm:"type:bigint;not null;default:0;index"` // CustomerID 为会话所属客户 ID。 - Subject string `gorm:"type:varchar(255);not null;default:''"` // Subject 为会话标题或摘要。 Status enums.IMConversationStatus `gorm:"type:int;not null;default:1;index"` // Status 为会话状态,如待接入、处理中、已关闭。 ServiceMode enums.IMConversationServiceMode `gorm:"type:int;not null;default:3;index"` // ServiceMode 为服务模式,如仅AI、仅人工、AI优先人工接管。 Priority int `gorm:"type:int;not null;default:0;index"` // Priority 为会话优先级。 diff --git a/internal/pkg/dto/response/conversation_response.go b/internal/pkg/dto/response/conversation_response.go index ed315ec..23053f0 100644 --- a/internal/pkg/dto/response/conversation_response.go +++ b/internal/pkg/dto/response/conversation_response.go @@ -22,7 +22,7 @@ type ConversationResponse struct { AIAgentID int64 `json:"aiAgentId"` ChannelID int64 `json:"channelId"` CustomerID int64 `json:"customerId"` - Subject string `json:"subject"` + CustomerName string `json:"customerName"` Status enums.IMConversationStatus `json:"status"` ServiceMode enums.IMConversationServiceMode `json:"serviceMode"` Priority int `json:"priority"` diff --git a/internal/services/conversation_service.go b/internal/services/conversation_service.go index 69250d2..8cf4309 100644 --- a/internal/services/conversation_service.go +++ b/internal/services/conversation_service.go @@ -2,10 +2,7 @@ package services import ( "context" - "crypto/md5" - "encoding/hex" "encoding/json" - "fmt" "log/slog" "cs-agent/internal/events" @@ -61,7 +58,8 @@ func (s *conversationService) ListConversations(userID int64, filter request.Age if strs.IsNotBlank(keyword) { keyword = strings.TrimSpace(keyword) - cnd.Where("subject LIKE ? OR last_message_summary LIKE ?", "%"+keyword+"%", "%"+keyword+"%") + keywordLike := "%" + keyword + "%" + cnd.Where("last_message_summary LIKE ? OR customer_id IN (SELECT id FROM t_customer WHERE name LIKE ?)", keywordLike, keywordLike) } switch filter { @@ -103,8 +101,6 @@ func (s *conversationService) getLatestNotFinishedByCustomerID(db *gorm.DB, cust } func (s *conversationService) Create(externalInfo openidentity.ExternalInfo, channelID, aiAgentID int64) (*models.Conversation, error) { - subject := s.buildDefaultSubject(externalInfo) - aiAgent := AIAgentService.Get(aiAgentID) if aiAgent == nil || aiAgent.Status != enums.StatusOk { return nil, errorsx.InvalidParam("AI Agent not found") @@ -127,7 +123,6 @@ func (s *conversationService) Create(externalInfo openidentity.ExternalInfo, cha AIAgentID: aiAgentID, ChannelID: channelID, CustomerID: customerID, - Subject: subject, Status: s.resolveInitialStatus(aiAgent.ServiceMode), ServiceMode: aiAgent.ServiceMode, Priority: 0, @@ -667,7 +662,10 @@ func (s *conversationService) BuildConversationSummary(conversation *models.Conv if strings.TrimSpace(conversation.LastMessageSummary) != "" { return conversation.LastMessageSummary } - return strings.TrimSpace(conversation.Subject) + if customer := CustomerService.Get(conversation.CustomerID); customer != nil { + return strings.TrimSpace(customer.Name) + } + return "" } func (s *conversationService) canCloseConversation(conversation *models.Conversation, operator *dto.AuthPrincipal) bool { @@ -806,22 +804,6 @@ func (s *conversationService) canLinkConversationCustomer(conv *models.Conversat } } -func (s *conversationService) buildDefaultSubject(externalInfo openidentity.ExternalInfo) string { - if strs.IsNotBlank(externalInfo.ExternalName) { - return externalInfo.ExternalName - } - return fmt.Sprintf("访客%s", hashUUID(externalInfo.ExternalID)) -} - -func hashUUID(uuid string) string { - if uuid == "" { - return "unknown" - } - - h := md5.Sum([]byte(uuid)) - return hex.EncodeToString(h[:])[:8] -} - func (s *conversationService) resolveInitialStatus(serviceMode enums.IMConversationServiceMode) enums.IMConversationStatus { switch serviceMode { case enums.IMConversationServiceModeHumanOnly: diff --git a/internal/services/customer_service.go b/internal/services/customer_service.go index 6f9d2d9..614f44b 100644 --- a/internal/services/customer_service.go +++ b/internal/services/customer_service.go @@ -1,6 +1,8 @@ package services import ( + "crypto/md5" + "encoding/hex" "log/slog" "cs-agent/internal/models" @@ -154,6 +156,15 @@ func buildExternalCustomerName(externalInfo openidentity.ExternalInfo) string { return "访客" + hashUUID(externalInfo.ExternalID) } +func hashUUID(uuid string) string { + if uuid == "" { + return "unknown" + } + + h := md5.Sum([]byte(uuid)) + return hex.EncodeToString(h[:])[:8] +} + func (s *customerService) CreateCustomer(req request.CreateCustomerRequest, operator *dto.AuthPrincipal) (*models.Customer, error) { if operator == nil { return nil, errorsx.Unauthorized("未登录或登录已过期") diff --git a/internal/services/event_handlers/conversation_assigned_event_handler.go b/internal/services/event_handlers/conversation_assigned_event_handler.go index 65de9c8..3c0a449 100644 --- a/internal/services/event_handlers/conversation_assigned_event_handler.go +++ b/internal/services/event_handlers/conversation_assigned_event_handler.go @@ -55,7 +55,7 @@ func buildConversationAssignedNotifyBody(conversation *models.Conversation, assi } lines := []string{ fmt.Sprintf("会话ID: #%d", conversation.ID), - fmt.Sprintf("会话主题: %s", strs.DefaultIfBlank(conversation.Subject, "-")), + fmt.Sprintf("会话摘要: %s", strs.DefaultIfBlank(services.ConversationService.BuildConversationSummary(conversation), "-")), fmt.Sprintf("接入渠道: %s", resolveConversationChannelLabel(conversation)), fmt.Sprintf("当前状态: %s", enums.GetIMConversationStatusLabel(conversation.Status)), fmt.Sprintf("处理人: %s", resolveNotifyUserLabel(assigneeID)), diff --git a/internal/services/ticket_service.go b/internal/services/ticket_service.go index 87d78fe..6fc085a 100644 --- a/internal/services/ticket_service.go +++ b/internal/services/ticket_service.go @@ -852,7 +852,7 @@ func (s *ticketService) CreateFromConversation(req request.CreateTicketFromConve } title := strings.TrimSpace(req.Title) if title == "" { - title = strings.TrimSpace(conversation.Subject) + title = ConversationService.BuildConversationSummary(conversation) } description := strings.TrimSpace(req.Description) if description == "" { diff --git a/web/app/dashboard/conversation-monitor/_components/detail.tsx b/web/app/dashboard/conversation-monitor/_components/detail.tsx index 12d2aad..f5f253d 100644 --- a/web/app/dashboard/conversation-monitor/_components/detail.tsx +++ b/web/app/dashboard/conversation-monitor/_components/detail.tsx @@ -249,7 +249,7 @@ export function ConversationDetailDialog({ onOpenChange={onOpenChange} title={
- {conversation.subject} + {conversation.customerName || `客户 #${conversation.customerId || conversation.id}`}