refactor: 将客服后端重构为宿主可嵌入模块
- 注入数据库、运行时配置、统一响应、文件存储和平台 AI 能力,补充业务读写工具与客户快捷操作契约。 - 移除模块内重复的组织、客户、工单、标签、技能、旧工作流、MCP 和迁移实现,将身份权限与业务主体交由宿主管理。 - 使用 libSQL 重构向量存储,并完善图片消息、访客身份、排队调度、企业微信和支持聊天页面。 - 统一 HTTP、DTO 与 WebSocket 的 snake_case 协议,补齐模块初始化、业务动作和公共载荷等回归测试。
This commit is contained in:
@@ -23,7 +23,9 @@ func BuildConversationWithLocale(item *models.Conversation, locale string) respo
|
||||
ID: item.ID,
|
||||
AIAgentID: item.AIAgentID,
|
||||
ChannelID: item.ChannelID,
|
||||
CustomerType: item.CustomerType,
|
||||
CustomerID: item.CustomerID,
|
||||
CustomerExternalID: item.CustomerExternalID,
|
||||
CustomerName: item.CustomerName,
|
||||
Status: item.Status,
|
||||
ServiceMode: item.ServiceMode,
|
||||
@@ -47,6 +49,18 @@ func BuildConversationWithLocale(item *models.Conversation, locale string) respo
|
||||
if identity := services.ConversationService.GetConversationExternalIdentity(item); identity != nil {
|
||||
ret.CustomerOnline = services.WsService.IsGuestOnline(identity.ExternalID)
|
||||
}
|
||||
queueSnapshot := services.ConversationQueueService.GetSnapshot(item)
|
||||
if queueSnapshot.Queued {
|
||||
ret.QueueEnteredAt = utils.FormatTimePtr(queueSnapshot.EnteredAt)
|
||||
ret.QueuePosition = queueSnapshot.Position
|
||||
ret.QueueAheadCount = queueSnapshot.AheadCount
|
||||
ret.QueueWaitingCount = queueSnapshot.WaitingCount
|
||||
ret.QueueWaitSeconds = queueSnapshot.WaitSeconds
|
||||
ret.QueueEstimatedWaitSeconds = queueSnapshot.EstimatedWaitSeconds
|
||||
ret.QueueEscalationLevel = queueSnapshot.EscalationLevel
|
||||
ret.EffectivePriority = queueSnapshot.EffectivePriority
|
||||
ret.QueueServiceOnline = queueSnapshot.ServiceOnline
|
||||
}
|
||||
if item.CurrentAssigneeID > 0 {
|
||||
if user := services.UserService.Get(item.CurrentAssigneeID); user != nil {
|
||||
ret.CurrentAssigneeName = user.Nickname
|
||||
@@ -120,11 +134,11 @@ func BuildMessagesWithLocale(list []models.Message, locale string) []response.Me
|
||||
return nil
|
||||
}
|
||||
agentReadState, customerReadState := services.ConversationReadStateService.GetConversationReadStates(list[0].ConversationID)
|
||||
aiSenderNames, userSenderNames := collectMessageSenderNameMaps(list)
|
||||
aiSenders, userSenderNames := collectMessageSenderMaps(list)
|
||||
agentProfiles := collectAgentProfilesByMessages(list)
|
||||
ret := make([]response.MessageResponse, 0, len(list))
|
||||
for i := range list {
|
||||
ret = append(ret, BuildMessageWithReadStatesAndLocale(&list[i], agentReadState, customerReadState, aiSenderNames, userSenderNames, agentProfiles, locale))
|
||||
ret = append(ret, BuildMessageWithReadStatesAndLocale(&list[i], agentReadState, customerReadState, aiSenders, userSenderNames, agentProfiles, locale))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@@ -138,17 +152,16 @@ func BuildMessageWithLocale(item *models.Message, locale string) response.Messag
|
||||
return BuildMessageWithReadStatesAndLocale(item, agentReadState, customerReadState, nil, nil, nil, locale)
|
||||
}
|
||||
|
||||
func BuildMessageWithReadStates(item *models.Message, agentReadState, customerReadState *models.ConversationReadState, aiSenderNames, userSenderNames map[int64]string, agentProfiles map[int64]*models.AgentProfile) response.MessageResponse {
|
||||
return BuildMessageWithReadStatesAndLocale(item, agentReadState, customerReadState, aiSenderNames, userSenderNames, agentProfiles, i18nx.DefaultLocale)
|
||||
func BuildMessageWithReadStates(item *models.Message, agentReadState, customerReadState *models.ConversationReadState, aiSenders map[int64]*models.AIAgent, userSenderNames map[int64]string, agentProfiles map[int64]*models.AgentProfile) response.MessageResponse {
|
||||
return BuildMessageWithReadStatesAndLocale(item, agentReadState, customerReadState, aiSenders, userSenderNames, agentProfiles, i18nx.DefaultLocale)
|
||||
}
|
||||
|
||||
func BuildMessageWithReadStatesAndLocale(item *models.Message, agentReadState, customerReadState *models.ConversationReadState, aiSenderNames, userSenderNames map[int64]string, agentProfiles map[int64]*models.AgentProfile, locale string) response.MessageResponse {
|
||||
func BuildMessageWithReadStatesAndLocale(item *models.Message, agentReadState, customerReadState *models.ConversationReadState, aiSenders map[int64]*models.AIAgent, userSenderNames map[int64]string, agentProfiles map[int64]*models.AgentProfile, locale string) response.MessageResponse {
|
||||
content, payload := utils.BuildRenderableMessage(item)
|
||||
ret := response.MessageResponse{
|
||||
ID: item.ID,
|
||||
ConversationID: item.ConversationID,
|
||||
RequestID: item.RequestID,
|
||||
WorkflowRunID: item.WorkflowRunID,
|
||||
ClientMsgID: item.ClientMsgID,
|
||||
SenderType: item.SenderType,
|
||||
SenderID: item.SenderID,
|
||||
@@ -168,10 +181,13 @@ func BuildMessageWithReadStatesAndLocale(item *models.Message, agentReadState, c
|
||||
}
|
||||
if item.SenderID > 0 {
|
||||
if item.SenderType == enums.IMSenderTypeAI {
|
||||
if aiSenderNames != nil {
|
||||
ret.SenderName = aiSenderNames[item.SenderID]
|
||||
} else if aiAgent := services.AIAgentService.Get(item.SenderID); aiAgent != nil {
|
||||
aiAgent := aiSenders[item.SenderID]
|
||||
if aiAgent == nil {
|
||||
aiAgent = services.AIAgentService.Get(item.SenderID)
|
||||
}
|
||||
if aiAgent != nil {
|
||||
ret.SenderName = aiAgent.Name
|
||||
ret.SenderAvatar = strings.TrimSpace(aiAgent.Avatar)
|
||||
}
|
||||
} else if item.SenderType == enums.IMSenderTypeAgent {
|
||||
profile := agentProfiles[item.SenderID]
|
||||
@@ -240,8 +256,8 @@ func collectAgentProfilesByMessages(list []models.Message) map[int64]*models.Age
|
||||
return out
|
||||
}
|
||||
|
||||
func collectMessageSenderNameMaps(list []models.Message) (aiNames map[int64]string, userNames map[int64]string) {
|
||||
aiNames = make(map[int64]string)
|
||||
func collectMessageSenderMaps(list []models.Message) (aiSenders map[int64]*models.AIAgent, userNames map[int64]string) {
|
||||
aiSenders = make(map[int64]*models.AIAgent)
|
||||
userNames = make(map[int64]string)
|
||||
var aiIDs, userIDs []int64
|
||||
seenAI := make(map[int64]struct{})
|
||||
@@ -266,7 +282,8 @@ func collectMessageSenderNameMaps(list []models.Message) (aiNames map[int64]stri
|
||||
userIDs = append(userIDs, m.SenderID)
|
||||
}
|
||||
for _, a := range services.AIAgentService.FindByIds(aiIDs) {
|
||||
aiNames[a.ID] = a.Name
|
||||
agent := a
|
||||
aiSenders[a.ID] = &agent
|
||||
}
|
||||
for _, u := range services.UserService.FindByIds(userIDs) {
|
||||
name := u.Nickname
|
||||
@@ -275,7 +292,7 @@ func collectMessageSenderNameMaps(list []models.Message) (aiNames map[int64]stri
|
||||
}
|
||||
userNames[u.ID] = name
|
||||
}
|
||||
return aiNames, userNames
|
||||
return aiSenders, userNames
|
||||
}
|
||||
|
||||
func isMessageRead(item *models.Message, state *models.ConversationReadState) bool {
|
||||
|
||||
Reference in New Issue
Block a user