llm-chat/backend/services/ConversationService.js

351 lines
9.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const Conversation = require('../models/Conversation');
const Message = require('../models/Message');
const cacheManager = require('../utils/cache');
class ConversationService {
constructor() {
this.cachePrefix = 'conv_service';
}
/**
* 创建新对话
*/
async createConversation(title = '新对话') {
try {
const conversation = await Conversation.create(title);
// 清除对话列表缓存
this.clearConversationListCache();
console.log('创建新对话:', conversation.id);
return conversation;
} catch (error) {
console.error('ConversationService.createConversation错误:', error);
throw new Error('创建对话失败');
}
}
/**
* 获取对话列表
*/
async getConversations(limit = 50) {
try {
const cacheKey = `${this.cachePrefix}:list:${limit}`;
// 尝试从缓存获取
let conversations = cacheManager.get(cacheKey);
if (conversations) {
console.log('从缓存获取对话列表');
return conversations;
}
// 从数据库获取
conversations = await Conversation.findAll(limit);
// 缓存结果5分钟
cacheManager.set(cacheKey, conversations, { ttl: 300000 });
console.log(`获取对话列表: ${conversations.length}`);
return conversations;
} catch (error) {
console.error('ConversationService.getConversations错误:', error);
throw new Error('获取对话列表失败');
}
}
/**
* 根据ID获取对话详情
*/
async getConversationById(id) {
try {
if (!id || isNaN(id)) {
throw new Error('无效的对话ID');
}
const cacheKey = `${this.cachePrefix}:detail:${id}`;
// 尝试从缓存获取
let conversation = cacheManager.get(cacheKey);
if (conversation) {
console.log(`从缓存获取对话详情: ${id}`);
return conversation;
}
// 从数据库获取
conversation = await Conversation.findById(id);
if (!conversation) {
throw new Error('对话不存在');
}
// 缓存结果10分钟
cacheManager.set(cacheKey, conversation, { ttl: 600000 });
console.log(`获取对话详情: ${id}`);
return conversation;
} catch (error) {
console.error('ConversationService.getConversationById错误:', error);
throw error;
}
}
/**
* 获取对话的消息列表
*/
async getConversationMessages(conversationId, limit = 100, offset = 0) {
try {
if (!conversationId || isNaN(conversationId)) {
throw new Error('无效的对话ID');
}
// 确保对话存在
const conversation = await this.getConversationById(conversationId);
if (!conversation) {
throw new Error('对话不存在');
}
const cacheKey = `${this.cachePrefix}:messages:${conversationId}:${limit}:${offset}`;
// 尝试从缓存获取
let messages = cacheManager.get(cacheKey);
if (messages) {
console.log(`从缓存获取对话消息: ${conversationId}`);
return messages;
}
// 从数据库获取
messages = await Message.findByConversationId(conversationId, limit, offset);
// 缓存结果5分钟
cacheManager.set(cacheKey, messages, { ttl: 300000 });
console.log(`获取对话消息: ${conversationId}, ${messages.length}`);
return messages;
} catch (error) {
console.error('ConversationService.getConversationMessages错误:', error);
throw error;
}
}
/**
* 更新对话标题
*/
async updateConversationTitle(id, title) {
try {
if (!id || isNaN(id)) {
throw new Error('无效的对话ID');
}
if (!title || title.trim().length === 0) {
throw new Error('标题不能为空');
}
const conversation = await Conversation.updateTitle(id, title.trim());
// 清除相关缓存
this.clearConversationCache(id);
this.clearConversationListCache();
console.log(`更新对话标题: ${id} -> ${title}`);
return conversation;
} catch (error) {
console.error('ConversationService.updateConversationTitle错误:', error);
throw error;
}
}
/**
* 删除对话
*/
async deleteConversation(id) {
try {
if (!id || isNaN(id)) {
throw new Error('无效的对话ID');
}
// 确保对话存在
const conversation = await this.getConversationById(id);
if (!conversation) {
throw new Error('对话不存在');
}
await Conversation.delete(id);
// 清除相关缓存
this.clearConversationCache(id);
this.clearConversationListCache();
cacheManager.deleteConversationContext(id);
console.log(`删除对话: ${id}`);
return true;
} catch (error) {
console.error('ConversationService.deleteConversation错误:', error);
throw error;
}
}
/**
* 批量删除对话
*/
async deleteMultipleConversations(ids) {
try {
if (!Array.isArray(ids) || ids.length === 0) {
throw new Error('无效的对话ID列表');
}
// 验证所有ID
const validIds = ids.filter(id => id && !isNaN(id));
if (validIds.length === 0) {
throw new Error('没有有效的对话ID');
}
const deletedCount = await Conversation.deleteMultiple(validIds);
// 清除相关缓存
validIds.forEach(id => {
this.clearConversationCache(id);
cacheManager.deleteConversationContext(id);
});
this.clearConversationListCache();
console.log(`批量删除对话: ${deletedCount}`);
return deletedCount;
} catch (error) {
console.error('ConversationService.deleteMultipleConversations错误:', error);
throw error;
}
}
/**
* 获取对话统计信息
*/
async getConversationStats(conversationId) {
try {
if (!conversationId || isNaN(conversationId)) {
throw new Error('无效的对话ID');
}
const conversation = await this.getConversationById(conversationId);
if (!conversation) {
throw new Error('对话不存在');
}
const messageCount = await Message.getMessageCount(conversationId);
return {
id: conversation.id,
title: conversation.title,
created_at: conversation.created_at,
updated_at: conversation.updated_at,
message_count: messageCount
};
} catch (error) {
console.error('ConversationService.getConversationStats错误:', error);
throw error;
}
}
/**
* 搜索对话
*/
async searchConversations(query, limit = 20) {
try {
if (!query || query.trim().length === 0) {
throw new Error('搜索关键词不能为空');
}
// 搜索消息内容
const messages = await Message.search(query.trim(), null, limit);
// 提取关联的对话ID
const conversationIds = [...new Set(messages.map(msg => msg.conversation_id))];
// 获取对话详情
const conversations = await Promise.all(
conversationIds.map(id => this.getConversationById(id))
);
// 过滤掉不存在的对话
const validConversations = conversations.filter(conv => conv !== null);
console.log(`搜索对话: "${query}" -> ${validConversations.length}条结果`);
return validConversations;
} catch (error) {
console.error('ConversationService.searchConversations错误:', error);
throw error;
}
}
/**
* 自动生成对话标题(基于第一条用户消息)
*/
async generateConversationTitle(conversationId) {
try {
const messages = await Message.findByConversationId(conversationId, 5);
// 找到第一条用户消息
const firstUserMessage = messages.find(msg => msg.role === 'user');
if (!firstUserMessage) {
return '新对话';
}
// 从第一条用户消息生成标题取前20个字符
let title = firstUserMessage.content.trim();
if (title.length > 20) {
title = title.substring(0, 20) + '...';
}
// 更新对话标题
await this.updateConversationTitle(conversationId, title);
console.log(`自动生成对话标题: ${conversationId} -> ${title}`);
return title;
} catch (error) {
console.error('ConversationService.generateConversationTitle错误:', error);
return '新对话';
}
}
/**
* 清除对话详情缓存
*/
clearConversationCache(conversationId) {
const detailKey = `${this.cachePrefix}:detail:${conversationId}`;
cacheManager.delete(detailKey);
// 清除消息缓存(可能有多个分页)
for (let i = 0; i < 10; i++) {
const messageKey = `${this.cachePrefix}:messages:${conversationId}:100:${i * 100}`;
cacheManager.delete(messageKey);
}
}
/**
* 清除对话列表缓存
*/
clearConversationListCache() {
// 清除不同分页的列表缓存
for (let limit = 10; limit <= 100; limit += 10) {
const listKey = `${this.cachePrefix}:list:${limit}`;
cacheManager.delete(listKey);
}
}
/**
* 更新对话活动时间
*/
async updateLastActivity(conversationId) {
try {
await Conversation.updateLastActivity(conversationId);
// 清除相关缓存
this.clearConversationCache(conversationId);
this.clearConversationListCache();
} catch (error) {
console.error('ConversationService.updateLastActivity错误:', error);
// 这是一个辅助操作,失败不应该影响主流程
}
}
}
module.exports = ConversationService;