diff --git a/internal/pkg/dto/request/conversation_request.go b/internal/pkg/dto/request/conversation_request.go index 09e80ea..41b5601 100644 --- a/internal/pkg/dto/request/conversation_request.go +++ b/internal/pkg/dto/request/conversation_request.go @@ -3,10 +3,11 @@ package request type AgentConversationFilter string const ( - AgentConversationFilterMine AgentConversationFilter = "mine" - AgentConversationFilterActive AgentConversationFilter = "active" - AgentConversationFilterPending AgentConversationFilter = "pending" - AgentConversationFilterClosed AgentConversationFilter = "closed" + AgentConversationFilterAIServing AgentConversationFilter = "ai_serving" + AgentConversationFilterMine AgentConversationFilter = "mine" + AgentConversationFilterActive AgentConversationFilter = "active" + AgentConversationFilterPending AgentConversationFilter = "pending" + AgentConversationFilterClosed AgentConversationFilter = "closed" ) type ConversationListRequest struct { diff --git a/internal/pkg/enums/im.go b/internal/pkg/enums/im.go index 3df47ac..244a4c1 100644 --- a/internal/pkg/enums/im.go +++ b/internal/pkg/enums/im.go @@ -3,18 +3,21 @@ package enums type IMConversationStatus int const ( - IMConversationStatusPending IMConversationStatus = 1 - IMConversationStatusActive IMConversationStatus = 2 - IMConversationStatusClosed IMConversationStatus = 3 + IMConversationStatusAIServing IMConversationStatus = 1 + IMConversationStatusPending IMConversationStatus = 2 + IMConversationStatusActive IMConversationStatus = 3 + IMConversationStatusClosed IMConversationStatus = 4 ) var imConversationStatusLabelMap = map[IMConversationStatus]string{ - IMConversationStatusPending: "待接入", - IMConversationStatusActive: "处理中", - IMConversationStatusClosed: "已关闭", + IMConversationStatusAIServing: "AI接待中", + IMConversationStatusPending: "待接入", + IMConversationStatusActive: "处理中", + IMConversationStatusClosed: "已关闭", } var IMConversationStatusValues = []IMConversationStatus{ + IMConversationStatusAIServing, IMConversationStatusPending, IMConversationStatusActive, IMConversationStatusClosed, diff --git a/internal/services/conversation_service.go b/internal/services/conversation_service.go index 98f05f4..40d251f 100644 --- a/internal/services/conversation_service.go +++ b/internal/services/conversation_service.go @@ -61,6 +61,8 @@ func (s *conversationService) ListConversations(userID int64, filter request.Age } switch filter { + case request.AgentConversationFilterAIServing: + cnd.Eq("current_assignee_id", 0).Eq("status", enums.IMConversationStatusAIServing).Desc("last_active_at").Desc("id") case request.AgentConversationFilterMine: cnd.Eq("current_assignee_id", userID).Desc("last_active_at").Desc("id") case request.AgentConversationFilterActive: @@ -86,6 +88,7 @@ func (s *conversationService) getLatestNotFinished(externalInfo openidentity.Ext cnd.Eq("external_id", externalInfo.ExternalID) cnd.Eq("external_source", externalInfo.ExternalSource) cnd.In("status", []enums.IMConversationStatus{ + enums.IMConversationStatusAIServing, enums.IMConversationStatusPending, enums.IMConversationStatusActive, }) @@ -110,7 +113,7 @@ func (s *conversationService) Create(externalInfo openidentity.ExternalInfo, aiA AIAgentID: aiAgentID, ExternalSource: externalInfo.ExternalSource, Subject: subject, - Status: enums.IMConversationStatusPending, + Status: s.resolveInitialStatus(aiAgent.ServiceMode), ServiceMode: aiAgent.ServiceMode, Priority: 0, ExternalID: externalInfo.ExternalID, @@ -350,7 +353,9 @@ func (s *conversationService) closeConversation(conversationID int64, senderType if conversation.Status == enums.IMConversationStatusClosed { return nil } - if conversation.Status != enums.IMConversationStatusPending && conversation.Status != enums.IMConversationStatusActive { + if conversation.Status != enums.IMConversationStatusAIServing && + conversation.Status != enums.IMConversationStatusPending && + conversation.Status != enums.IMConversationStatusActive { return errorsx.InvalidParam("当前状态不允许关闭会话") } var ( @@ -747,6 +752,8 @@ func (s *conversationService) canLinkConversationCustomer(conv *models.Conversat return true } switch conv.Status { + case enums.IMConversationStatusAIServing: + return true case enums.IMConversationStatusPending: return true case enums.IMConversationStatusActive: @@ -771,3 +778,14 @@ func hashUUID(uuid string) string { h := md5.Sum([]byte(uuid)) return hex.EncodeToString(h[:])[:8] } + +func (s *conversationService) resolveInitialStatus(serviceMode enums.IMConversationServiceMode) enums.IMConversationStatus { + switch serviceMode { + case enums.IMConversationServiceModeHumanOnly: + return enums.IMConversationStatusPending + case enums.IMConversationServiceModeAIOnly, enums.IMConversationServiceModeAIFirst: + return enums.IMConversationStatusAIServing + default: + return enums.IMConversationStatusAIServing + } +} diff --git a/internal/services/dashboard_service.go b/internal/services/dashboard_service.go index d228e9b..bb083f0 100644 --- a/internal/services/dashboard_service.go +++ b/internal/services/dashboard_service.go @@ -34,7 +34,10 @@ func (s *dashboardService) GetOverview(rangeValue string) response.DashboardOver return tx.Where("created_at >= ?", todayStart) }) processingConversationCount := repositories.DashboardRepository.CountConversations(db, func(tx *gorm.DB) *gorm.DB { - return tx.Where("status = ?", enums.IMConversationStatusActive) + return tx.Where("status IN ?", []enums.IMConversationStatus{ + enums.IMConversationStatusAIServing, + enums.IMConversationStatusActive, + }) }) pendingConversationCount := repositories.DashboardRepository.CountConversations(db, func(tx *gorm.DB) *gorm.DB { return tx.Where("status = ?", enums.IMConversationStatusPending) @@ -45,6 +48,7 @@ func (s *dashboardService) GetOverview(rangeValue string) response.DashboardOver activeSchedules := repositories.DashboardRepository.ListActiveTeamSchedules(db, now, now) activeConversations := repositories.DashboardRepository.ListConversations(db, func(tx *gorm.DB) *gorm.DB { return tx.Where("status IN ?", []enums.IMConversationStatus{ + enums.IMConversationStatusAIServing, enums.IMConversationStatusPending, enums.IMConversationStatusActive, }) @@ -168,6 +172,8 @@ func (s *dashboardService) buildAgentStats(now time.Time, teams []models.AgentTe teamCounters[item.CurrentTeamID] = counter } switch item.Status { + case enums.IMConversationStatusAIServing: + counter.processingConversations++ case enums.IMConversationStatusPending: counter.waitingConversations++ case enums.IMConversationStatusActive: @@ -229,7 +235,10 @@ func (s *dashboardService) buildAlerts(now time.Time, db *gorm.DB, activeConvers } staleProcessingCount := repositories.DashboardRepository.CountConversations(db, func(tx *gorm.DB) *gorm.DB { - return tx.Where("status = ? AND (last_message_at IS NULL OR last_message_at < ?)", enums.IMConversationStatusActive, activeTimeout) + return tx.Where("status IN ? AND (last_message_at IS NULL OR last_message_at < ?)", []enums.IMConversationStatus{ + enums.IMConversationStatusAIServing, + enums.IMConversationStatusActive, + }, activeTimeout) }) if staleProcessingCount > 0 { alerts = append(alerts, response.DashboardAlertResponse{ diff --git a/internal/services/message_service.go b/internal/services/message_service.go index cfccd15..9fb422f 100644 --- a/internal/services/message_service.go +++ b/internal/services/message_service.go @@ -514,6 +514,9 @@ func (s *messageService) ValidateConversationSender(conversationID int64, sender if operator == nil { return nil, errorsx.Unauthorized("未登录或登录已过期") } + if conversation.Status != enums.IMConversationStatusAIServing { + return nil, errorsx.Forbidden("当前会话不处于 AI 接待状态") + } if conversation.CurrentAssigneeID != 0 { return nil, errorsx.Forbidden("当前会话已由人工客服接管") } diff --git a/internal/services/wxwork_kf_inbound_service.go b/internal/services/wxwork_kf_inbound_service.go index 19b6149..b2e7bde 100644 --- a/internal/services/wxwork_kf_inbound_service.go +++ b/internal/services/wxwork_kf_inbound_service.go @@ -382,6 +382,7 @@ func (s *wxWorkKFInboundService) ensureConversation(base syncmsg.BaseMessage, pr Eq("external_source", external.ExternalSource). Eq("external_id", external.ExternalID). In("status", []enums.IMConversationStatus{ + enums.IMConversationStatusAIServing, enums.IMConversationStatusPending, enums.IMConversationStatusActive, }). diff --git a/web/app/(console)/conversation-monitor/_components/detail.tsx b/web/app/(console)/conversation-monitor/_components/detail.tsx index 3900cdc..f98de41 100644 --- a/web/app/(console)/conversation-monitor/_components/detail.tsx +++ b/web/app/(console)/conversation-monitor/_components/detail.tsx @@ -49,13 +49,13 @@ type ConversationDetailDialogProps = { function getStatusMeta(status: number) { switch (status) { case 1: - return { label: "待接入", variant: "outline" as const }; + return { label: "AI接待中", variant: "secondary" as const }; case 2: - return { label: "处理中", variant: "secondary" as const }; + return { label: "待接入", variant: "outline" as const }; case 3: - return { label: "已关闭", variant: "outline" as const }; + return { label: "处理中", variant: "secondary" as const }; case 4: - return { label: "已归档", variant: "outline" as const }; + return { label: "已关闭", variant: "outline" as const }; default: return { label: "未知", variant: "outline" as const }; } @@ -151,8 +151,8 @@ export function ConversationDetailDialog({ onOpenClose, }: ConversationDetailDialogProps) { const currentConversation = detail ?? item; - const isClosedConversation = currentConversation?.status === 3; - const isPendingConversation = currentConversation?.status === 1; + const isClosedConversation = currentConversation?.status === 4; + const isPendingConversation = currentConversation?.status === 2; const statusMeta = currentConversation ? getStatusMeta(currentConversation.status) : null; @@ -274,7 +274,7 @@ export function ConversationDetailDialog({