Files
yawiii-ComfyUI-Prompt-Assis…/js/services/cache.js
T
2025-06-06 11:20:01 +08:00

1212 lines
43 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.
/**
* 一级注释:本地缓存服务模块
* 统一管理历史记录和标签的本地缓存
*/
import { logger } from '../utils/logger.js';
import { PromptFormatter } from "../utils/promptFormatter.js";
// ---缓存配置---
const CACHE_CONFIG = {
// 统一前缀
GLOBAL_PREFIX: "PromptAssistant_",
// 历史缓存配置
HISTORY_KEY_PREFIX: "PromptAssistant_history_cache_",
MAX_HISTORY_PER_NODE: 20, // 每个节点最多保存的历史条数
MAX_HISTORY_GLOBAL: 100, // 全局最多保存的历史条数
MAX_CONTENT_LENGTH: 5000, // 单条历史最大长度限制
// 标签缓存配置
TAG_KEY_PREFIX: "PromptAssistant_tag_cache_",
// 翻译缓存配置
TRANSLATE_CACHE_KEY: "PromptAssistant_translate_cache",
MAX_TRANSLATE_CACHE: 200, // 最多保存的翻译缓存条数
// 旧的缓存键名(用于迁移)
OLD_HISTORY_KEY_PREFIX: "history_cache_",
OLD_TAG_KEY_PREFIX: "tag_cache_",
OLD_TRANSLATE_CACHE_KEY: "translate_cache",
};
/**
* 通用缓存服务
* 提供基础的缓存操作方法
*/
class CacheService {
/**
* 从localStorage获取数据
* @param {string} key 缓存键
* @returns {any} 解析后的数据
*/
static get(key) {
try {
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : null;
} catch (error) {
logger.error(`缓存服务 | 读取失败 | 键:${key} | 错误:${error.message}`);
return null;
}
}
/**
* 将数据存储到localStorage
* @param {string} key 缓存键
* @param {any} value 要存储的数据
* @returns {boolean} 是否成功
*/
static set(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
return true;
} catch (error) {
logger.error(`缓存服务 | 写入失败 | 键:${key} | 错误:${error.message}`);
return false;
}
}
/**
* 从localStorage删除数据
* @param {string} key 缓存键
*/
static remove(key) {
try {
localStorage.removeItem(key);
} catch (error) {
logger.error(`缓存服务 | 删除失败 | 键:${key} | 错误:${error.message}`);
}
}
/**
* 清除指定前缀的所有缓存
* @param {string} prefix 键前缀
*/
static clearByPrefix(prefix) {
try {
const keysToRemove = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && key.startsWith(prefix)) {
keysToRemove.push(key);
}
}
keysToRemove.forEach(key => this.remove(key));
logger.debug(`缓存服务 | 清除前缀缓存 | 前缀:${prefix} | 数量:${keysToRemove.length}`);
} catch (error) {
logger.error(`缓存服务 | 清除前缀缓存失败 | 前缀:${prefix} | 错误:${error.message}`);
}
}
/**
* 迁移旧的缓存键名到新的键名格式
* @returns {Object} 迁移统计信息
*/
static migrateCache() {
try {
const stats = {
history: 0,
tags: 0,
translate: 0
};
// 1. 迁移历史缓存
const oldHistoryKey = `${CACHE_CONFIG.OLD_HISTORY_KEY_PREFIX}all`;
const newHistoryKey = `${CACHE_CONFIG.HISTORY_KEY_PREFIX}all`;
const historyData = this.get(oldHistoryKey);
if (historyData) {
this.set(newHistoryKey, historyData);
this.remove(oldHistoryKey);
stats.history = 1;
}
// 2. 迁移标签缓存
const tagKeys = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && key.startsWith(CACHE_CONFIG.OLD_TAG_KEY_PREFIX)) {
tagKeys.push(key);
}
}
tagKeys.forEach(oldKey => {
const newKey = oldKey.replace(
CACHE_CONFIG.OLD_TAG_KEY_PREFIX,
CACHE_CONFIG.TAG_KEY_PREFIX
);
const data = this.get(oldKey);
if (data) {
this.set(newKey, data);
this.remove(oldKey);
stats.tags++;
}
});
// 3. 迁移翻译缓存
const translateData = this.get(CACHE_CONFIG.OLD_TRANSLATE_CACHE_KEY);
if (translateData) {
this.set(CACHE_CONFIG.TRANSLATE_CACHE_KEY, translateData);
this.remove(CACHE_CONFIG.OLD_TRANSLATE_CACHE_KEY);
stats.translate = 1;
}
logger.log(`缓存迁移完成 | 历史缓存: ${stats.history}个 | 标签缓存: ${stats.tags}个 | 翻译缓存: ${stats.translate}个`);
return stats;
} catch (error) {
logger.error(`缓存迁移失败 | 错误: ${error.message}`);
return { history: 0, tags: 0, translate: 0 };
}
}
}
/**
* 历史缓存服务
* 管理历史记录的缓存操作
*/
class HistoryCacheService {
// 添加撤销/重做状态记录
static undoStates = new Map(); // 格式: { "nodeId_inputId": { currentIndex: number, records: array } }
/**
* 获取所有历史记录
* @returns {Array} 所有历史记录
*/
static getAllHistory() {
const key = `${CACHE_CONFIG.HISTORY_KEY_PREFIX}all`;
return CacheService.get(key) || [];
}
/**
* 保存所有历史记录
* @param {Array} history 历史记录数组
*/
static saveAllHistory(history) {
const key = `${CACHE_CONFIG.HISTORY_KEY_PREFIX}all`;
CacheService.set(key, history);
}
/**
* 获取历史记录列表
* @param {object} options 查询选项
* @param {string} options.nodeId 当前节点ID,用于置顶显示
* @param {number} options.limit 限制条数,默认50
* @returns {Array} 历史记录数组
*/
static getHistoryList({ nodeId = null, limit = 50 } = {}) {
try {
const allHistory = this.getAllHistory();
// 确保每条历史记录都有必要的字段
const validHistory = allHistory.filter(item => {
const isValid = item && item.node_id && item.input_id &&
item.content && item.timestamp && item.operation_type;
if (!isValid) {
logger.debug(`历史缓存 | 跳过无效记录 | 节点:${item?.node_id}`);
}
return isValid;
});
if (!nodeId) {
return validHistory
.sort((a, b) => b.timestamp - a.timestamp)
.slice(0, limit);
}
const currentNodeHistory = [];
const otherNodesHistory = [];
validHistory.forEach(item => {
if (item.node_id === nodeId) {
currentNodeHistory.push(item);
} else {
otherNodesHistory.push(item);
}
});
currentNodeHistory.sort((a, b) => b.timestamp - a.timestamp);
otherNodesHistory.sort((a, b) => b.timestamp - a.timestamp);
return [...currentNodeHistory, ...otherNodesHistory].slice(0, limit);
} catch (error) {
logger.error(`历史缓存 | 获取历史失败 | 错误:${error.message}`);
return [];
}
}
/**
* 获取指定节点和输入框的历史记录
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @param {boolean} oldToNew 是否按照从旧到新排序
* @returns {Array} 历史记录数组
*/
static getInputHistory(nodeId, inputId, oldToNew = false) {
try {
const allHistory = this.getAllHistory();
const filtered = allHistory.filter(item =>
item.node_id === nodeId &&
item.input_id === inputId
);
// 根据参数决定排序方向
return filtered.sort((a, b) =>
oldToNew ? a.timestamp - b.timestamp : b.timestamp - a.timestamp
);
} catch (error) {
logger.error(`历史缓存 | 获取输入框历史失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
return [];
}
}
/**
* 检查是否为重复的翻译记录
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @param {string} content 当前内容
* @param {string} operationType 操作类型
* @returns {boolean} 是否为重复记录
*/
static isRepeatedTranslation(nodeId, inputId, content, operationType) {
try {
if (operationType !== 'translate') {
return false;
}
// 获取该节点和输入框的历史记录
const history = this.getInputHistory(nodeId, inputId, false);
if (!history || history.length === 0) {
return false;
}
// 获取最近的一条记录
const lastRecord = history[0];
if (!lastRecord || lastRecord.operation_type !== 'translate') {
return false;
}
// 检查内容是否匹配
const currentContent = content.trim();
const lastContent = lastRecord.content.trim();
// 查询翻译缓存
const cacheResult = TranslateCacheService.queryTranslateCache(currentContent);
if (!cacheResult) {
return false;
}
// 检查是否为原文-译文或译文-原文的切换
if (cacheResult.type === 'source' && cacheResult.translatedText === lastContent) {
return true;
}
if (cacheResult.type === 'translated' && cacheResult.sourceText === lastContent) {
return true;
}
return false;
} catch (error) {
logger.error(`历史缓存 | 检查重复翻译失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
return false;
}
}
/**
* 添加历史记录
* @param {Object} historyItem 历史记录对象
* @returns {boolean} 是否添加成功
*/
static addHistory(historyItem) {
try {
// 验证必要字段
if (!historyItem.node_id || !historyItem.input_id || historyItem.content === undefined) {
logger.error("历史缓存 | 添加历史失败 | 缺少必要字段");
return false;
}
// 如果内容为空或只包含空白字符,跳过记录
if (!historyItem.content || historyItem.content.trim() === '') {
logger.debug(`历史缓存 | 跳过添加 | 节点:${historyItem.node_id} | 原因:空内容`);
return false;
}
// 内容长度限制
let content = historyItem.content;
if (content.length > CACHE_CONFIG.MAX_CONTENT_LENGTH) {
content = content.substring(0, CACHE_CONFIG.MAX_CONTENT_LENGTH) + "...";
}
// 检查是否为重复的翻译记录
if (this.isRepeatedTranslation(historyItem.node_id, historyItem.input_id, content, historyItem.operation_type)) {
logger.debug(`历史缓存 | 跳过添加 | 节点:${historyItem.node_id} | 原因:重复的翻译记录`);
return false;
}
const allHistory = this.getAllHistory();
// 获取当前节点和输入框的历史记录
const nodeHistory = allHistory.filter(item =>
item.node_id === historyItem.node_id &&
item.input_id === historyItem.input_id
);
// 检查是否与最近的一条历史记录内容相同
if (nodeHistory.length > 0) {
// 按时间戳降序排序,获取最新的记录
const latestHistory = nodeHistory.sort((a, b) => b.timestamp - a.timestamp)[0];
if (latestHistory.content === content) {
logger.debug(`历史缓存 | 跳过添加 | 节点:${historyItem.node_id} | 原因:与上一条记录相同`);
return false;
}
}
// 创建新的历史记录
const newItem = {
node_id: historyItem.node_id,
input_id: historyItem.input_id,
content: content,
operation_type: historyItem.operation_type || null,
timestamp: Date.now() + 1, // 确保时间戳唯一
request_id: historyItem.request_id || null
};
// 添加到历史记录末尾
allHistory.push(newItem);
// 限制每个节点的历史记录数量
const currentNodeHistory = allHistory.filter(item => item.node_id === historyItem.node_id);
if (currentNodeHistory.length > CACHE_CONFIG.MAX_HISTORY_PER_NODE) {
// 按时间戳排序
currentNodeHistory.sort((a, b) => b.timestamp - a.timestamp);
// 获取要保留的记录的时间戳
const cutoffTimestamp = currentNodeHistory[CACHE_CONFIG.MAX_HISTORY_PER_NODE - 1].timestamp;
// 移除旧记录
const filteredHistory = allHistory.filter(item =>
item.node_id !== historyItem.node_id ||
item.timestamp >= cutoffTimestamp
);
allHistory.length = 0;
allHistory.push(...filteredHistory);
}
// 限制全局历史记录数量
if (allHistory.length > CACHE_CONFIG.MAX_HISTORY_GLOBAL) {
// 按时间戳排序
allHistory.sort((a, b) => b.timestamp - a.timestamp);
allHistory.length = CACHE_CONFIG.MAX_HISTORY_GLOBAL;
}
this.saveAllHistory(allHistory);
logger.debug(`历史缓存 | 添加历史 | 节点:${historyItem.node_id} | 内容长度:${content.length}`);
return true;
} catch (error) {
logger.error(`历史缓存 | 添加历史失败 | 错误:${error.message}`);
return false;
}
}
/**
* 初始化撤销状态
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @param {string} currentContent 当前内容
*/
static initUndoState(nodeId, inputId, currentContent) {
try {
const key = `${nodeId}_${inputId}`;
// 获取该输入框的历史记录(从旧到新)
const inputHistory = this.getInputHistory(nodeId, inputId, true);
// 创建撤销状态,指向最新的记录
const undoState = {
currentIndex: inputHistory.length - 1, // 指向最后一条记录
currentContent: currentContent, // 保存当前内容
lastHistoryTimestamp: inputHistory.length > 0 ? inputHistory[inputHistory.length - 1].timestamp : 0
};
// 更新撤销状态
this.undoStates.set(key, undoState);
logger.debug(`历史缓存 | 初始化撤销状态 | 节点:${nodeId} | 输入框:${inputId} | 历史数:${inputHistory.length} | 当前位置:${undoState.currentIndex}`);
} catch (error) {
logger.error(`历史缓存 | 初始化撤销状态失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
}
}
/**
* 执行撤销操作
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @returns {string|null} 撤销后的内容
*/
static undo(nodeId, inputId) {
try {
const key = `${nodeId}_${inputId}`;
const state = this.undoStates.get(key);
if (!state) {
return null;
}
// 获取历史记录(从旧到新)
const history = this.getInputHistory(nodeId, inputId, true);
// 如果没有历史记录,返回null
if (history.length === 0) {
return null;
}
// 如果已经是第一条记录,无法继续撤销
if (state.currentIndex <= 0) {
return null;
}
// 更新当前位置
state.currentIndex--;
// 从历史记录中获取内容
const content = history[state.currentIndex]?.content || '';
// 更新状态
this.undoStates.set(key, state);
logger.debug(`历史缓存 | 撤销操作 | 节点:${nodeId} | 输入框:${inputId} | 位置:${state.currentIndex}/${history.length - 1} | 内容:${content}`);
return content;
} catch (error) {
logger.error(`历史缓存 | 撤销操作失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
return null;
}
}
/**
* 执行重做操作
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @returns {string|null} 重做后的内容
*/
static redo(nodeId, inputId) {
try {
const key = `${nodeId}_${inputId}`;
const state = this.undoStates.get(key);
if (!state) {
return null;
}
// 获取历史记录(从旧到新)
const history = this.getInputHistory(nodeId, inputId, true);
// 如果没有历史记录,返回null
if (history.length === 0) {
return null;
}
// 如果已经是最后一条记录,无法重做
if (state.currentIndex >= history.length - 1) {
return null;
}
// 更新当前位置
state.currentIndex++;
// 从历史记录中获取内容
const content = history[state.currentIndex]?.content || '';
// 更新状态
this.undoStates.set(key, state);
logger.debug(`历史缓存 | 重做操作 | 节点:${nodeId} | 输入框:${inputId} | 位置:${state.currentIndex}/${history.length - 1} | 内容:${content}`);
return content;
} catch (error) {
logger.error(`历史缓存 | 重做操作失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
return null;
}
}
/**
* 检查是否可以撤销
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @returns {boolean} 是否可以撤销
*/
static canUndo(nodeId, inputId) {
try {
const key = `${nodeId}_${inputId}`;
const state = this.undoStates.get(key);
return state && state.currentIndex > 0;
} catch (error) {
logger.error(`历史缓存 | 检查撤销状态失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
return false;
}
}
/**
* 检查是否可以重做
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @returns {boolean} 是否可以重做
*/
static canRedo(nodeId, inputId) {
try {
const key = `${nodeId}_${inputId}`;
const state = this.undoStates.get(key);
if (!state) return false;
const history = this.getInputHistory(nodeId, inputId, true);
return state.currentIndex < history.length - 1;
} catch (error) {
logger.error(`历史缓存 | 检查重做状态失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
return false;
}
}
/**
* 清除所有历史记录
*/
static clearAllHistory() {
try {
// 清除所有历史记录
this.saveAllHistory([]);
// 清除所有撤销状态
this.undoStates.clear();
logger.debug("历史缓存 | 清除所有历史");
} catch (error) {
logger.error(`历史缓存 | 清除所有历史失败 | 错误:${error.message}`);
}
}
/**
* 清除指定节点的历史记录
* @param {string} nodeId 节点ID
*/
static clearNodeHistory(nodeId) {
try {
// 清除节点的历史记录
const allHistory = this.getAllHistory();
const filteredHistory = allHistory.filter(item => item.node_id !== nodeId);
this.saveAllHistory(filteredHistory);
// 清除该节点的撤销状态
for (const [key] of this.undoStates) {
if (key.startsWith(nodeId + "_")) {
this.undoStates.delete(key);
}
}
logger.debug(`历史缓存 | 清除节点历史 | 节点:${nodeId}`);
} catch (error) {
logger.error(`历史缓存 | 清除节点历史失败 | 节点:${nodeId} | 错误:${error.message}`);
}
}
/**
* 添加历史记录并更新撤销状态
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @param {string} content 内容
* @param {string} operationType 操作类型
* @returns {boolean} 是否添加成功
*/
static addHistoryAndUpdateUndoState(nodeId, inputId, content, operationType = 'input') {
// 首先尝试添加历史记录
const success = this.addHistory({
node_id: nodeId,
input_id: inputId,
content: content,
operation_type: operationType,
timestamp: Date.now()
});
if (success) {
// 更新撤销状态
const key = `${nodeId}_${inputId}`;
const state = this.undoStates.get(key) || {
currentIndex: 0,
currentContent: content,
lastHistoryTimestamp: 0
};
// 获取最新的历史记录
const history = this.getInputHistory(nodeId, inputId, true);
// 更新状态
state.currentIndex = history.length - 1;
state.currentContent = content;
state.lastHistoryTimestamp = Date.now();
// 保存状态
this.undoStates.set(key, state);
logger.debug(`历史缓存 | 更新撤销状态 | 节点:${nodeId} | 输入框:${inputId} | 位置:${state.currentIndex}`);
}
return success;
}
/**
* 获取历史记录统计信息
* @returns {Object} 统计信息对象,包含总数和每个节点的数量
*/
static getHistoryStats() {
try {
const allHistory = this.getAllHistory();
const stats = {
total: allHistory.length,
byNode: {}
};
// 按节点统计
allHistory.forEach(item => {
if (!stats.byNode[item.node_id]) {
stats.byNode[item.node_id] = 0;
}
stats.byNode[item.node_id]++;
});
logger.debug(`历史缓存 | 获取统计 | 总数:${stats.total} | 节点数:${Object.keys(stats.byNode).length}`);
return stats;
} catch (error) {
logger.error(`历史缓存 | 获取统计失败 | 错误:${error.message}`);
return { total: 0, byNode: {} };
}
}
/**
* 修改历史记录项的操作类型
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @param {number} timestamp 时间戳,用于标识特定历史记录
* @param {string} newType 新的操作类型
* @returns {boolean} 是否修改成功
*/
static updateHistoryItemType(nodeId, inputId, timestamp, newType) {
try {
// 获取所有历史记录
const allHistory = this.getAllHistory();
// 查找匹配的历史记录
const itemIndex = allHistory.findIndex(item =>
item.node_id === nodeId &&
item.input_id === inputId &&
item.timestamp === timestamp
);
// 如果找到匹配的记录,修改其操作类型
if (itemIndex !== -1) {
allHistory[itemIndex].operation_type = newType;
// 保存修改后的历史记录
this.saveAllHistory(allHistory);
logger.debug(`历史缓存 | 修改操作类型 | 节点:${nodeId} | 输入框:${inputId} | 类型:${newType}`);
return true;
} else {
logger.debug(`历史缓存 | 修改操作类型失败 | 未找到匹配记录 | 节点:${nodeId} | 输入框:${inputId}`);
return false;
}
} catch (error) {
logger.error(`历史缓存 | 修改操作类型失败 | 错误:${error.message}`);
return false;
}
}
/**
* 更新历史记录项
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @param {number} timestamp 时间戳,用于标识特定历史记录
* @param {Object} updates 要更新的字段
* @returns {boolean} 是否修改成功
*/
static updateHistoryItem(nodeId, inputId, timestamp, updates) {
try {
// 获取所有历史记录
const allHistory = this.getAllHistory();
// 查找匹配的历史记录
const itemIndex = allHistory.findIndex(item =>
item.node_id === nodeId &&
item.input_id === inputId &&
item.timestamp === timestamp
);
// 如果找到匹配的记录,更新指定字段
if (itemIndex !== -1) {
// 更新所有提供的字段
Object.assign(allHistory[itemIndex], updates);
// 保存修改后的历史记录
this.saveAllHistory(allHistory);
logger.debug(`历史缓存 | 更新记录 | 节点:${nodeId} | 输入框:${inputId} | 字段:${Object.keys(updates).join(',')}`);
return true;
} else {
logger.debug(`历史缓存 | 更新失败 | 未找到匹配记录 | 节点:${nodeId} | 输入框:${inputId}`);
return false;
}
} catch (error) {
logger.error(`历史缓存 | 更新记录失败 | 错误:${error.message}`);
return false;
}
}
/**
* 根据请求ID获取相关联的历史记录
* @param {string} requestId 请求ID
* @returns {Array} 相关联的历史记录数组
*/
static getHistoryByRequestId(requestId) {
try {
if (!requestId) return [];
const allHistory = this.getAllHistory();
return allHistory.filter(item =>
item.request_id === requestId
).sort((a, b) => b.timestamp - a.timestamp);
} catch (error) {
logger.error(`历史缓存 | 获取请求关联历史失败 | 请求ID:${requestId} | 错误:${error.message}`);
return [];
}
}
}
/**
* 标签缓存服务
* 管理标签的缓存操作
*/
class TagCacheService {
/**
* 获取或创建标签格式
* @param {string} rawTag 原始标签
* @returns {Object} 标签格式对象
*/
static getOrCreateFormats(rawTag) {
return PromptFormatter.formatTag(rawTag);
}
/**
* 获取标签缓存
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @returns {Map|null} 标签缓存Map
*/
static getTagCache(nodeId, inputId) {
try {
const key = `${CACHE_CONFIG.TAG_KEY_PREFIX}${nodeId}_${inputId}`;
const data = CacheService.get(key);
if (data) {
return new Map(Object.entries(data));
}
return new Map();
} catch (error) {
logger.error(`标签缓存 | 获取缓存失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
return new Map();
}
}
/**
* 添加标签到缓存
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @param {string} rawTag 原始标签
* @param {Object} formats 标签格式对象
* @returns {boolean} 是否添加成功
*/
static addTag(nodeId, inputId, rawTag, formats) {
try {
const cache = this.getTagCache(nodeId, inputId);
// 确保formats包含所有必要的格式
const format1 = formats.format1 || ` ${rawTag}`;
const format2 = formats.format2 || ` ${rawTag},`;
const format3 = formats.format3 || `, ${rawTag}`;
const format4 = formats.format4 || `, ${rawTag},`;
const insertedFormat = formats.insertedFormat || null;
cache.set(rawTag, {
format1,
format2,
format3,
format4,
insertedFormat
});
this._saveTagCache(nodeId, inputId, cache);
logger.debug(`标签缓存 | 添加标签 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag}`);
return true;
} catch (error) {
logger.error(`标签缓存 | 添加标签失败 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 错误:${error.message}`);
return false;
}
}
/**
* 从缓存中移除标签
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @param {string} rawTag 原始标签
* @returns {boolean} 是否移除成功
*/
static removeTag(nodeId, inputId, rawTag) {
try {
const cache = this.getTagCache(nodeId, inputId);
const result = cache.delete(rawTag);
if (result) {
this._saveTagCache(nodeId, inputId, cache);
logger.debug(`标签缓存 | 移除标签 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag}`);
}
return result;
} catch (error) {
logger.error(`标签缓存 | 移除标签失败 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 错误:${error.message}`);
return false;
}
}
/**
* 检查标签是否在输入框中
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @param {string} rawTag 原始标签
* @param {string} inputValue 输入框值
* @returns {boolean} 是否存在
*/
static isTagInInput(nodeId, inputId, rawTag, inputValue) {
try {
const formats = this.getTagFormats(nodeId, inputId, rawTag);
if (!formats) {
return false;
}
// 按优先级顺序检查各种格式
const checkOrder = ['insertedFormat', 'format4', 'format3', 'format2', 'format1'];
for (const formatKey of checkOrder) {
const format = formats[formatKey];
if (format && inputValue.includes(format)) {
logger.debug(`标签检查 | 结果:已存在 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 匹配格式:${formatKey}`);
return true;
}
}
return false;
} catch (error) {
logger.error(`标签检查 | 结果:异常 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 错误:${error.message}`);
return false;
}
}
/**
* 获取标签的所有格式
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @param {string} rawTag 原始标签
* @returns {Object|null} 标签格式对象
*/
static getTagFormats(nodeId, inputId, rawTag) {
try {
const cache = this.getTagCache(nodeId, inputId);
return cache.get(rawTag) || null;
} catch (error) {
logger.error(`标签缓存 | 获取标签格式失败 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 错误:${error.message}`);
return null;
}
}
/**
* 获取标签的插入格式
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @param {string} rawTag 原始标签
* @returns {string|null} 插入的格式
*/
static getInsertedFormat(nodeId, inputId, rawTag) {
try {
const formats = this.getTagFormats(nodeId, inputId, rawTag);
return formats ? formats.insertedFormat : null;
} catch (error) {
logger.error(`标签缓存 | 获取插入格式失败 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 错误:${error.message}`);
return null;
}
}
/**
* 获取所有原始标签
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @returns {Array} 原始标签数组
*/
static getAllRawTags(nodeId, inputId) {
try {
const cache = this.getTagCache(nodeId, inputId);
return Array.from(cache.keys());
} catch (error) {
logger.error(`标签缓存 | 获取所有标签失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
return [];
}
}
/**
* 清除标签缓存
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
*/
static clearCache(nodeId, inputId) {
try {
this._removeTagCache(nodeId, inputId);
logger.debug(`标签缓存 | 清除缓存 | 节点:${nodeId} | 输入框:${inputId}`);
} catch (error) {
logger.error(`标签缓存 | 清除缓存失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
}
}
/**
* 保存标签缓存
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @param {Map} cache 缓存Map
* @private
*/
static _saveTagCache(nodeId, inputId, cache) {
const key = `${CACHE_CONFIG.TAG_KEY_PREFIX}${nodeId}_${inputId}`;
const data = Object.fromEntries(cache);
CacheService.set(key, data);
}
/**
* 移除标签缓存
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @private
*/
static _removeTagCache(nodeId, inputId) {
const key = `${CACHE_CONFIG.TAG_KEY_PREFIX}${nodeId}_${inputId}`;
CacheService.remove(key);
}
/**
* 获取标签统计信息
* @returns {Object} 统计信息对象,包含总数和每个节点的数量
*/
static getTagStats() {
try {
let total = 0;
const byNode = {};
// 遍历localStorage查找所有标签缓存
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && key.startsWith(CACHE_CONFIG.TAG_KEY_PREFIX)) {
// 从key中提取节点ID和输入框ID
const [nodeId, inputId] = key.replace(CACHE_CONFIG.TAG_KEY_PREFIX, '').split('_');
const cache = this.getTagCache(nodeId, inputId);
const count = cache.size;
// 更新统计
total += count;
if (!byNode[nodeId]) {
byNode[nodeId] = {};
}
byNode[nodeId][inputId] = count;
}
}
const stats = { total, byNode };
logger.debug(`标签缓存 | 获取统计 | 总数:${total} | 节点数:${Object.keys(byNode).length}`);
return stats;
} catch (error) {
logger.error(`标签缓存 | 获取统计失败 | 错误:${error.message}`);
return { total: 0, byNode: {} };
}
}
/**
* 更新标签的插入格式
* @param {string} nodeId 节点ID
* @param {string} inputId 输入框ID
* @param {string} rawTag 原始标签
* @param {string} insertedFormat 新的插入格式
* @returns {boolean} 是否更新成功
*/
static updateInsertedFormat(nodeId, inputId, rawTag, insertedFormat) {
try {
const cache = this.getTagCache(nodeId, inputId);
const formats = cache.get(rawTag);
if (!formats) {
logger.debug(`标签缓存 | 更新插入格式失败 | 原因:标签不存在 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag}`);
return false;
}
// 更新插入格式
formats.insertedFormat = insertedFormat;
cache.set(rawTag, formats);
// 保存到缓存
this._saveTagCache(nodeId, inputId, cache);
logger.debug(`标签缓存 | 更新插入格式 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 新格式:${insertedFormat}`);
return true;
} catch (error) {
logger.error(`标签缓存 | 更新插入格式失败 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 错误:${error.message}`);
return false;
}
}
}
/**
* 翻译缓存服务
* 管理翻译结果的缓存操作
*/
class TranslateCacheService {
/**
* 获取所有翻译缓存
* @returns {Map} 翻译缓存Map,key为原文,value为译文
*/
static getAllTranslateCache() {
try {
const data = CacheService.get(CACHE_CONFIG.TRANSLATE_CACHE_KEY);
if (data) {
return new Map(Object.entries(data));
}
return new Map();
} catch (error) {
logger.error(`翻译缓存 | 获取缓存失败 | 错误:${error.message}`);
return new Map();
}
}
/**
* 保存所有翻译缓存
* @param {Map} cache 翻译缓存Map
*/
static saveAllTranslateCache(cache) {
try {
// 将Map转换为普通对象
const data = Object.fromEntries(cache);
CacheService.set(CACHE_CONFIG.TRANSLATE_CACHE_KEY, data);
} catch (error) {
logger.error(`翻译缓存 | 保存缓存失败 | 错误:${error.message}`);
}
}
/**
* 添加翻译缓存
* @param {string} sourceText 原文
* @param {string} translatedText 译文
* @returns {boolean} 是否添加成功
*/
static addTranslateCache(sourceText, translatedText) {
try {
// 验证参数
if (!sourceText || !translatedText) {
logger.error("翻译缓存 | 添加缓存失败 | 缺少必要参数");
return false;
}
// 获取现有缓存
const cache = this.getAllTranslateCache();
// 添加或更新缓存
cache.set(sourceText, translatedText);
// 如果缓存过大,删除最旧的条目
if (cache.size > CACHE_CONFIG.MAX_TRANSLATE_CACHE) {
// 将Map转换为数组以便操作
const entries = Array.from(cache.entries());
// 删除最早的条目,保留最新的MAX_TRANSLATE_CACHE条
const newEntries = entries.slice(-CACHE_CONFIG.MAX_TRANSLATE_CACHE);
// 重建缓存
cache.clear();
newEntries.forEach(([key, value]) => cache.set(key, value));
}
// 保存缓存
this.saveAllTranslateCache(cache);
logger.debug(`翻译缓存 | 添加缓存 | 原文长度:${sourceText.length} | 译文长度:${translatedText.length}`);
return true;
} catch (error) {
logger.error(`翻译缓存 | 添加缓存失败 | 错误:${error.message}`);
return false;
}
}
/**
* 查询翻译缓存
* @param {string} text 要查询的文本
* @returns {Object|null} 查询结果,包含匹配类型和文本
*/
static queryTranslateCache(text) {
try {
if (!text) return null;
const cache = this.getAllTranslateCache();
// 检查是否为原文(key)
if (cache.has(text)) {
const translatedText = cache.get(text);
logger.debug(`翻译缓存 | 查询结果:命中原文 | 原文长度:${text.length} | 译文长度:${translatedText.length}`);
return {
type: 'source',
text: text,
translatedText: translatedText
};
}
// 检查是否为译文(value)
for (const [source, translated] of cache.entries()) {
if (translated === text) {
logger.debug(`翻译缓存 | 查询结果:命中译文 | 原文长度:${source.length} | 译文长度:${text.length}`);
return {
type: 'translated',
text: text,
sourceText: source
};
}
}
// 未命中缓存
logger.debug(`翻译缓存 | 查询结果:未命中 | 文本长度:${text.length}`);
return null;
} catch (error) {
logger.error(`翻译缓存 | 查询缓存失败 | 错误:${error.message}`);
return null;
}
}
/**
* 清除所有翻译缓存
*/
static clearAllTranslateCache() {
try {
CacheService.remove(CACHE_CONFIG.TRANSLATE_CACHE_KEY);
logger.debug("翻译缓存 | 清除所有缓存");
} catch (error) {
logger.error(`翻译缓存 | 清除缓存失败 | 错误:${error.message}`);
}
}
/**
* 获取翻译缓存统计信息
* @returns {Object} 统计信息对象
*/
static getTranslateCacheStats() {
try {
const cache = this.getAllTranslateCache();
const stats = {
total: cache.size,
sourceTextLengths: 0,
translatedTextLengths: 0
};
// 计算原文和译文的总长度
for (const [source, translated] of cache.entries()) {
stats.sourceTextLengths += source.length;
stats.translatedTextLengths += translated.length;
}
logger.debug(`翻译缓存 | 获取统计 | 总数:${stats.total} | 原文总长度:${stats.sourceTextLengths} | 译文总长度:${stats.translatedTextLengths}`);
return stats;
} catch (error) {
logger.error(`翻译缓存 | 获取统计失败 | 错误:${error.message}`);
return { total: 0, sourceTextLengths: 0, translatedTextLengths: 0 };
}
}
}
export { CacheService, HistoryCacheService, TagCacheService, TranslateCacheService, CACHE_CONFIG };