update at 2025-10-16 12:45:05
This commit is contained in:
251
web/src/services/chat/MessageService.ts
Normal file
251
web/src/services/chat/MessageService.ts
Normal file
@@ -0,0 +1,251 @@
|
||||
/**
|
||||
* MessageService
|
||||
* 负责消息的 CRUD 操作
|
||||
*
|
||||
* 职责:
|
||||
* - 消息的创建、读取、更新、删除
|
||||
* - 消息状态管理
|
||||
* - 消息查询和过滤
|
||||
*/
|
||||
|
||||
import type { Message, Conversation } from '../../types/chat'
|
||||
import type { CreateMessageOptions, UpdateMessageOptions, MessageQueryResult } from './types'
|
||||
|
||||
export class MessageService {
|
||||
private conversations: Map<string, Conversation>
|
||||
|
||||
constructor(conversations: Map<string, Conversation>) {
|
||||
this.conversations = conversations
|
||||
}
|
||||
|
||||
/**
|
||||
* 在指定对话中创建新消息
|
||||
*/
|
||||
createMessage(conversationId: string, options: CreateMessageOptions): Message {
|
||||
const conversation = this.conversations.get(conversationId)
|
||||
if (!conversation) {
|
||||
throw new Error(`Conversation not found: ${conversationId}`)
|
||||
}
|
||||
|
||||
const message: Message = {
|
||||
id: this.generateId(),
|
||||
role: options.role,
|
||||
content: options.content,
|
||||
status: options.status || 'success',
|
||||
timestamp: new Date(),
|
||||
model: options.model,
|
||||
tokens: options.tokens
|
||||
}
|
||||
|
||||
conversation.messages.push(message)
|
||||
conversation.updatedAt = new Date()
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定对话的所有消息
|
||||
*/
|
||||
getMessages(conversationId: string): Message[] {
|
||||
const conversation = this.conversations.get(conversationId)
|
||||
return conversation?.messages || []
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 topicId 获取消息
|
||||
*/
|
||||
getMessagesByTopicId(topicId: string): Message[] {
|
||||
for (const conv of this.conversations.values()) {
|
||||
if (conv.topicId === topicId) {
|
||||
return conv.messages
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定消息
|
||||
*/
|
||||
getMessage(conversationId: string, messageId: string): Message | undefined {
|
||||
const conversation = this.conversations.get(conversationId)
|
||||
if (!conversation) return undefined
|
||||
|
||||
return conversation.messages.find(m => m.id === messageId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找消息及其位置信息
|
||||
*/
|
||||
findMessage(messageId: string, topicId?: string): MessageQueryResult {
|
||||
for (const conv of this.conversations.values()) {
|
||||
if (topicId && conv.topicId !== topicId) continue
|
||||
|
||||
const index = conv.messages.findIndex(m => m.id === messageId)
|
||||
if (index !== -1) {
|
||||
return {
|
||||
message: conv.messages[index],
|
||||
conversation: conv,
|
||||
index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
message: undefined,
|
||||
conversation: undefined,
|
||||
index: -1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新消息
|
||||
*/
|
||||
updateMessage(
|
||||
conversationId: string,
|
||||
messageId: string,
|
||||
options: UpdateMessageOptions
|
||||
): boolean {
|
||||
const conversation = this.conversations.get(conversationId)
|
||||
if (!conversation) return false
|
||||
|
||||
const message = conversation.messages.find(m => m.id === messageId)
|
||||
if (!message) return false
|
||||
|
||||
// 更新消息字段
|
||||
if (options.content !== undefined) {
|
||||
message.content = options.content
|
||||
}
|
||||
if (options.status !== undefined) {
|
||||
message.status = options.status
|
||||
}
|
||||
if (options.tokens !== undefined) {
|
||||
message.tokens = options.tokens
|
||||
}
|
||||
|
||||
conversation.updatedAt = new Date()
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新消息状态
|
||||
*/
|
||||
updateMessageStatus(
|
||||
conversationId: string,
|
||||
messageId: string,
|
||||
status: 'sending' | 'success' | 'error' | 'paused'
|
||||
): boolean {
|
||||
return this.updateMessage(conversationId, messageId, { status })
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加消息内容(用于流式响应)
|
||||
*/
|
||||
appendMessageContent(
|
||||
conversationId: string,
|
||||
messageId: string,
|
||||
content: string
|
||||
): boolean {
|
||||
const conversation = this.conversations.get(conversationId)
|
||||
if (!conversation) return false
|
||||
|
||||
const message = conversation.messages.find(m => m.id === messageId)
|
||||
if (!message) return false
|
||||
|
||||
message.content += content
|
||||
conversation.updatedAt = new Date()
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除消息
|
||||
*/
|
||||
deleteMessage(conversationId: string, messageId: string): boolean {
|
||||
const conversation = this.conversations.get(conversationId)
|
||||
if (!conversation) return false
|
||||
|
||||
const index = conversation.messages.findIndex(m => m.id === messageId)
|
||||
if (index === -1) return false
|
||||
|
||||
conversation.messages.splice(index, 1)
|
||||
conversation.updatedAt = new Date()
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定消息之后的所有消息(包括该消息)
|
||||
*/
|
||||
deleteMessagesAfter(conversationId: string, messageId: string): boolean {
|
||||
const conversation = this.conversations.get(conversationId)
|
||||
if (!conversation) return false
|
||||
|
||||
const index = conversation.messages.findIndex(m => m.id === messageId)
|
||||
if (index === -1) return false
|
||||
|
||||
conversation.messages.splice(index)
|
||||
conversation.updatedAt = new Date()
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对话中最后一条用户消息
|
||||
*/
|
||||
getLastUserMessage(conversationId: string): Message | undefined {
|
||||
const conversation = this.conversations.get(conversationId)
|
||||
if (!conversation) return undefined
|
||||
|
||||
for (let i = conversation.messages.length - 1; i >= 0; i--) {
|
||||
if (conversation.messages[i].role === 'user') {
|
||||
return conversation.messages[i]
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对话中最后一条消息
|
||||
*/
|
||||
getLastMessage(conversationId: string): Message | undefined {
|
||||
const conversation = this.conversations.get(conversationId)
|
||||
if (!conversation) return undefined
|
||||
|
||||
const messages = conversation.messages
|
||||
return messages.length > 0 ? messages[messages.length - 1] : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息预览文本
|
||||
*/
|
||||
getMessagePreview(content: string, maxLength = 50): string {
|
||||
if (!content) return ''
|
||||
const text = content.replace(/\n/g, ' ').trim()
|
||||
return text.length > maxLength ? text.slice(0, maxLength) + '...' : text
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对话中成功的消息(用于发送给 LLM)
|
||||
*/
|
||||
getSuccessMessages(conversationId: string): Message[] {
|
||||
const conversation = this.conversations.get(conversationId)
|
||||
if (!conversation) return []
|
||||
|
||||
return conversation.messages.filter(m =>
|
||||
m.status === 'success' || m.status === 'paused'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近的 N 条成功消息
|
||||
*/
|
||||
getRecentSuccessMessages(conversationId: string, limit: number): Message[] {
|
||||
const successMessages = this.getSuccessMessages(conversationId)
|
||||
return successMessages.slice(-limit)
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一 ID
|
||||
*/
|
||||
private generateId(): string {
|
||||
return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user