252 lines
6.4 KiB
TypeScript
252 lines
6.4 KiB
TypeScript
/**
|
||
* 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)}`
|
||
}
|
||
}
|