miniprogramme/utils/message-search-manager.js
2025-09-12 16:08:17 +08:00

484 lines
13 KiB
JavaScript
Raw 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 apiClient = require('./api-client.js');
/**
* 消息搜索管理器
* 功能:
* 1. 全局消息搜索
* 2. 会话内搜索
* 3. 搜索结果高亮
* 4. 搜索历史管理
* 5. 本地缓存优化
* 6. 分页加载
*/
class MessageSearchManager {
constructor() {
this.isInitialized = false;
// 搜索配置
this.searchConfig = {
// 最小搜索关键词长度
minKeywordLength: 1,
// 搜索结果每页数量
pageSize: 20,
// 最大搜索历史数量
maxSearchHistory: 50,
// 本地缓存过期时间(毫秒)
cacheExpireTime: 30 * 60 * 1000, // 30分钟
// 搜索防抖延迟(毫秒)
debounceDelay: 300,
// 支持的搜索类型
searchTypes: ['text', 'image', 'file', 'all']
};
// 搜索缓存
this.searchCache = new Map();
// 搜索历史
this.searchHistory = [];
// 当前搜索状态
this.currentSearch = {
keyword: '',
type: 'all',
conversationId: null,
page: 1,
hasMore: true,
loading: false,
results: []
};
// 防抖定时器
this.debounceTimer = null;
this.init();
}
// 初始化搜索管理器
async init() {
if (this.isInitialized) return;
console.log('🔍 初始化消息搜索管理器...');
try {
// 加载搜索历史
await this.loadSearchHistory();
// 清理过期缓存
this.cleanupExpiredCache();
this.isInitialized = true;
console.log('✅ 消息搜索管理器初始化完成');
} catch (error) {
console.error('❌ 消息搜索管理器初始化失败:', error);
}
}
// 全局搜索消息
async searchMessages(keyword, options = {}) {
try {
// 验证搜索关键词
if (!this.validateKeyword(keyword)) {
return { success: false, error: '搜索关键词无效' };
}
console.log('🔍 搜索消息:', keyword);
// 设置搜索参数
const searchParams = {
keyword: keyword.trim(),
type: options.type || 'all',
conversationId: options.conversationId || null,
page: options.page || 1,
pageSize: options.pageSize || this.searchConfig.pageSize
};
// 检查缓存
const cacheKey = this.generateCacheKey(searchParams);
const cachedResult = this.getFromCache(cacheKey);
if (cachedResult) {
console.log('🔍 使用缓存搜索结果');
return cachedResult;
}
// 更新搜索状态
this.updateSearchState(searchParams);
// 执行搜索
const result = await this.performSearch(searchParams);
// 缓存结果
if (result.success) {
this.saveToCache(cacheKey, result);
// 添加到搜索历史
this.addToSearchHistory(keyword);
}
return result;
} catch (error) {
console.error('❌ 搜索消息失败:', error);
return { success: false, error: error.message };
}
}
// 会话内搜索
async searchInConversation(conversationId, keyword, options = {}) {
return await this.searchMessages(keyword, {
...options,
conversationId: conversationId
});
}
// 防抖搜索
searchWithDebounce(keyword, options = {}, callback) {
// 清除之前的定时器
if (this.debounceTimer) {
clearTimeout(this.debounceTimer);
}
// 设置新的定时器
this.debounceTimer = setTimeout(async () => {
try {
const result = await this.searchMessages(keyword, options);
if (callback) {
callback(result);
}
} catch (error) {
console.error('❌ 防抖搜索失败:', error);
if (callback) {
callback({ success: false, error: error.message });
}
}
}, this.searchConfig.debounceDelay);
}
// 执行搜索
async performSearch(searchParams) {
try {
this.currentSearch.loading = true;
// 构建搜索请求
const requestData = {
keyword: searchParams.keyword,
messageType: searchParams.type === 'all' ? null : this.getMessageTypeCode(searchParams.type),
page: searchParams.page,
pageSize: searchParams.pageSize
};
// 如果指定了会话ID则进行会话内搜索
if (searchParams.conversationId) {
requestData.conversationId = searchParams.conversationId;
}
// 调用搜索API按统一客户端签名
const response = await apiClient.post('/api/v1/messages/search', requestData);
if (response && (response.code === 0 || response.code === 200 || response.success)) {
const searchResult = {
success: true,
data: {
messages: response.data?.messages || [],
total: response.data?.total || 0,
page: searchParams.page,
pageSize: searchParams.pageSize,
hasMore: response.data?.hasMore || false,
keyword: searchParams.keyword,
searchTime: Date.now()
}
};
// 处理搜索结果
searchResult.data.messages = this.processSearchResults(
searchResult.data.messages,
searchParams.keyword
);
console.log(`🔍 搜索完成,找到 ${searchResult.data.total} 条消息`);
return searchResult;
} else {
const msg = response?.message || response?.error || '搜索失败';
throw new Error(msg);
}
} catch (error) {
console.error('❌ 执行搜索失败:', error);
return { success: false, error: error.message };
} finally {
this.currentSearch.loading = false;
}
}
// 处理搜索结果
processSearchResults(messages, keyword) {
return messages.map(message => {
// 添加高亮信息
const highlightedContent = this.highlightKeyword(message.content, keyword);
return {
...message,
highlightedContent: highlightedContent,
searchKeyword: keyword,
// 添加消息摘要(用于显示上下文)
summary: this.generateMessageSummary(message.content, keyword)
};
});
}
// 关键词高亮
highlightKeyword(content, keyword) {
if (!content || !keyword) return content;
try {
// 转义特殊字符
const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp(`(${escapedKeyword})`, 'gi');
return content.replace(regex, '<mark class="search-highlight">$1</mark>');
} catch (error) {
console.error('❌ 关键词高亮失败:', error);
return content;
}
}
// 生成消息摘要
generateMessageSummary(content, keyword, maxLength = 100) {
if (!content || !keyword) return content;
try {
const keywordIndex = content.toLowerCase().indexOf(keyword.toLowerCase());
if (keywordIndex === -1) return content.substring(0, maxLength);
// 计算摘要范围
const start = Math.max(0, keywordIndex - 30);
const end = Math.min(content.length, keywordIndex + keyword.length + 30);
let summary = content.substring(start, end);
// 添加省略号
if (start > 0) summary = '...' + summary;
if (end < content.length) summary = summary + '...';
return summary;
} catch (error) {
console.error('❌ 生成消息摘要失败:', error);
return content.substring(0, maxLength);
}
}
// 加载更多搜索结果
async loadMoreResults() {
if (!this.currentSearch.hasMore || this.currentSearch.loading) {
return { success: false, error: '没有更多结果' };
}
const nextPage = this.currentSearch.page + 1;
const result = await this.searchMessages(this.currentSearch.keyword, {
type: this.currentSearch.type,
conversationId: this.currentSearch.conversationId,
page: nextPage
});
if (result.success) {
// 合并结果
this.currentSearch.results = [...this.currentSearch.results, ...result.data.messages];
this.currentSearch.page = nextPage;
this.currentSearch.hasMore = result.data.hasMore;
}
return result;
}
// 清除搜索结果
clearSearchResults() {
this.currentSearch = {
keyword: '',
type: 'all',
conversationId: null,
page: 1,
hasMore: true,
loading: false,
results: []
};
}
// 验证搜索关键词
validateKeyword(keyword) {
if (!keyword || typeof keyword !== 'string') {
return false;
}
const trimmedKeyword = keyword.trim();
return trimmedKeyword.length >= this.searchConfig.minKeywordLength;
}
// 获取消息类型代码
getMessageTypeCode(type) {
const typeMap = {
'text': 0,
'image': 1,
'voice': 2,
'video': 3,
'file': 4
};
return typeMap[type] || null;
}
// 更新搜索状态
updateSearchState(searchParams) {
this.currentSearch = {
keyword: searchParams.keyword,
type: searchParams.type,
conversationId: searchParams.conversationId,
page: searchParams.page,
hasMore: true,
loading: true,
results: []
};
}
// 生成缓存键
generateCacheKey(searchParams) {
return `search_${searchParams.keyword}_${searchParams.type}_${searchParams.conversationId || 'global'}_${searchParams.page}`;
}
// 从缓存获取
getFromCache(cacheKey) {
const cached = this.searchCache.get(cacheKey);
if (cached && (Date.now() - cached.timestamp) < this.searchConfig.cacheExpireTime) {
return cached.data;
}
return null;
}
// 保存到缓存
saveToCache(cacheKey, data) {
this.searchCache.set(cacheKey, {
data: data,
timestamp: Date.now()
});
// 限制缓存大小
if (this.searchCache.size > 100) {
const firstKey = this.searchCache.keys().next().value;
this.searchCache.delete(firstKey);
}
}
// 清理过期缓存
cleanupExpiredCache() {
const now = Date.now();
for (const [key, value] of this.searchCache) {
if (now - value.timestamp > this.searchConfig.cacheExpireTime) {
this.searchCache.delete(key);
}
}
}
// 添加到搜索历史
addToSearchHistory(keyword) {
const trimmedKeyword = keyword.trim();
if (!trimmedKeyword) return;
// 移除重复项
this.searchHistory = this.searchHistory.filter(item => item !== trimmedKeyword);
// 添加到开头
this.searchHistory.unshift(trimmedKeyword);
// 限制历史数量
if (this.searchHistory.length > this.searchConfig.maxSearchHistory) {
this.searchHistory = this.searchHistory.slice(0, this.searchConfig.maxSearchHistory);
}
// 保存到本地存储
this.saveSearchHistory();
}
// 获取搜索历史
getSearchHistory() {
return [...this.searchHistory];
}
// 清除搜索历史
clearSearchHistory() {
this.searchHistory = [];
this.saveSearchHistory();
}
// 删除搜索历史项
removeSearchHistoryItem(keyword) {
this.searchHistory = this.searchHistory.filter(item => item !== keyword);
this.saveSearchHistory();
}
// 加载搜索历史
async loadSearchHistory() {
try {
const history = wx.getStorageSync('messageSearchHistory') || [];
this.searchHistory = history;
} catch (error) {
console.error('❌ 加载搜索历史失败:', error);
}
}
// 保存搜索历史
async saveSearchHistory() {
try {
wx.setStorageSync('messageSearchHistory', this.searchHistory);
} catch (error) {
console.error('❌ 保存搜索历史失败:', error);
}
}
// 获取搜索建议
getSearchSuggestions(keyword) {
if (!keyword) return this.searchHistory.slice(0, 10);
const lowerKeyword = keyword.toLowerCase();
return this.searchHistory
.filter(item => item.toLowerCase().includes(lowerKeyword))
.slice(0, 10);
}
// 获取当前搜索状态
getCurrentSearchState() {
return { ...this.currentSearch };
}
// 获取搜索统计
getSearchStats() {
return {
historyCount: this.searchHistory.length,
cacheSize: this.searchCache.size,
currentKeyword: this.currentSearch.keyword,
isLoading: this.currentSearch.loading,
hasResults: this.currentSearch.results.length > 0
};
}
// 重置搜索管理器
reset() {
this.searchCache.clear();
this.clearSearchResults();
this.clearSearchHistory();
}
}
// 创建全局实例
const messageSearchManager = new MessageSearchManager();
module.exports = messageSearchManager;