@@ -123,7 +123,6 @@ const definedMigrations: Migration[] = [
|
||||
name: 'Add notes column to connections table',
|
||||
check: async (db: Database): Promise<boolean> => {
|
||||
const notesColumnExists = await columnExists(db, 'connections', 'notes');
|
||||
// Only run if the column does NOT exist
|
||||
return !notesColumnExists;
|
||||
},
|
||||
sql: `
|
||||
@@ -297,6 +296,17 @@ const definedMigrations: Migration[] = [
|
||||
return !jumpChainColumnExists || !proxyTypeColumnExists;
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: 'Add variables column to quick_commands table',
|
||||
check: async (db: Database): Promise<boolean> => {
|
||||
const columnAlreadyExists = await columnExists(db, 'quick_commands', 'variables');
|
||||
return !columnAlreadyExists;
|
||||
},
|
||||
sql: `
|
||||
ALTER TABLE quick_commands ADD COLUMN variables TEXT NULL;
|
||||
`
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -166,6 +166,7 @@ CREATE TABLE IF NOT EXISTS quick_commands (
|
||||
name TEXT NULL, -- 名称可选
|
||||
command TEXT NOT NULL, -- 指令必选
|
||||
usage_count INTEGER NOT NULL DEFAULT 0, -- 使用频率
|
||||
variables TEXT NULL,
|
||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||
);
|
||||
|
||||
@@ -6,8 +6,8 @@ import { QuickCommandSortBy } from '../services/quick-commands.service';
|
||||
* 处理添加新快捷指令的请求
|
||||
*/
|
||||
export const addQuickCommand = async (req: Request, res: Response): Promise<void> => {
|
||||
// 从请求体中解构出 name, command, 以及可选的 tagIds
|
||||
const { name, command, tagIds } = req.body;
|
||||
// 从请求体中解构出 name, command, 以及可选的 tagIds 和 variables
|
||||
const { name, command, tagIds, variables } = req.body;
|
||||
|
||||
// --- 基本验证 ---
|
||||
if (!command || typeof command !== 'string' || command.trim().length === 0) {
|
||||
@@ -24,11 +24,16 @@ export const addQuickCommand = async (req: Request, res: Response): Promise<void
|
||||
res.status(400).json({ message: 'tagIds 必须是一个数字数组' });
|
||||
return;
|
||||
}
|
||||
// --- 结束验证 ---
|
||||
// 验证 variables (如果提供的话)
|
||||
if (variables !== undefined && (typeof variables !== 'object' || variables === null || Array.isArray(variables))) {
|
||||
res.status(400).json({ message: 'variables 必须是一个对象' });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// 将 tagIds 传递给 Service 层
|
||||
const newId = await QuickCommandsService.addQuickCommand(name, command, tagIds);
|
||||
// 将 tagIds 和 variables 传递给 Service 层
|
||||
const newId = await QuickCommandsService.addQuickCommand(name, command, tagIds, variables);
|
||||
// 尝试获取新创建的带标签的指令信息返回
|
||||
const newCommand = await QuickCommandsService.getQuickCommandById(newId);
|
||||
if (newCommand) {
|
||||
@@ -65,8 +70,8 @@ export const getAllQuickCommands = async (req: Request, res: Response): Promise<
|
||||
*/
|
||||
export const updateQuickCommand = async (req: Request, res: Response): Promise<void> => {
|
||||
const id = parseInt(req.params.id, 10);
|
||||
// 从请求体中解构出 name, command, 以及可选的 tagIds
|
||||
const { name, command, tagIds } = req.body;
|
||||
// 从请求体中解构出 name, command, 以及可选的 tagIds 和 variables
|
||||
const { name, command, tagIds, variables } = req.body;
|
||||
|
||||
// --- 基本验证 ---
|
||||
if (isNaN(id)) {
|
||||
@@ -87,11 +92,16 @@ export const updateQuickCommand = async (req: Request, res: Response): Promise<v
|
||||
res.status(400).json({ message: 'tagIds 必须是一个数字数组' });
|
||||
return;
|
||||
}
|
||||
// --- 结束验证 ---
|
||||
// 验证 variables (如果提供的话)
|
||||
// undefined 表示不更新 variables, null 或对象表示要更新
|
||||
if (variables !== undefined && variables !== null && (typeof variables !== 'object' || Array.isArray(variables))) {
|
||||
res.status(400).json({ message: 'variables 必须是对象或 null' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 将 tagIds 传递给 Service 层
|
||||
const success = await QuickCommandsService.updateQuickCommand(id, name, command, tagIds);
|
||||
// 将 tagIds 和 variables 传递给 Service 层
|
||||
const success = await QuickCommandsService.updateQuickCommand(id, name, command, tagIds, variables);
|
||||
if (success) {
|
||||
// 尝试获取更新后的带标签的指令信息返回
|
||||
const updatedCommand = await QuickCommandsService.getQuickCommandById(id);
|
||||
|
||||
@@ -6,18 +6,21 @@ export interface QuickCommand {
|
||||
name: string | null; // 名称可选
|
||||
command: string;
|
||||
usage_count: number;
|
||||
variables?: string; // 存储 JSON 格式的变量键值对
|
||||
created_at: number; // Unix 时间戳 (秒)
|
||||
updated_at: number; // Unix 时间戳 (秒)
|
||||
}
|
||||
|
||||
// 定义包含标签 ID 的接口
|
||||
export interface QuickCommandWithTags extends QuickCommand {
|
||||
// 定义包含标签 ID 和解析后变量的接口
|
||||
export type QuickCommandWithTags = Omit<QuickCommand, 'variables'> & {
|
||||
tagIds: number[];
|
||||
}
|
||||
variables: Record<string, string> | null; // API 层面使用对象
|
||||
};
|
||||
|
||||
// 用于从数据库获取带 tag_ids_str 的行
|
||||
interface DbQuickCommandWithTagsRow extends QuickCommand {
|
||||
tag_ids_str: string | null;
|
||||
// variables 字段已包含在 QuickCommand 中,这里不需要重复定义,因为 QuickCommand 将包含 variables?: string
|
||||
}
|
||||
|
||||
|
||||
@@ -25,13 +28,15 @@ interface DbQuickCommandWithTagsRow extends QuickCommand {
|
||||
* 添加一条新的快捷指令
|
||||
* @param name - 指令名称 (可选)
|
||||
* @param command - 指令内容
|
||||
* @param variables - 变量对象 (可选)
|
||||
* @returns 返回插入记录的 ID
|
||||
*/
|
||||
export const addQuickCommand = async (name: string | null, command: string): Promise<number> => {
|
||||
const sql = `INSERT INTO quick_commands (name, command, created_at, updated_at) VALUES (?, ?, strftime('%s', 'now'), strftime('%s', 'now'))`;
|
||||
export const addQuickCommand = async (name: string | null, command: string, variables?: Record<string, string>): Promise<number> => {
|
||||
const sql = `INSERT INTO quick_commands (name, command, variables, created_at, updated_at) VALUES (?, ?, ?, strftime('%s', 'now'), strftime('%s', 'now'))`;
|
||||
try {
|
||||
const db = await getDbInstance();
|
||||
const result = await runDb(db, sql, [name, command]);
|
||||
const variablesJson = variables ? JSON.stringify(variables) : null;
|
||||
const result = await runDb(db, sql, [name, command, variablesJson]);
|
||||
if (typeof result.lastID !== 'number' || result.lastID <= 0) {
|
||||
throw new Error('添加快捷指令后未能获取有效的 lastID');
|
||||
}
|
||||
@@ -47,13 +52,15 @@ export const addQuickCommand = async (name: string | null, command: string): Pro
|
||||
* @param id - 要更新的记录 ID
|
||||
* @param name - 新的指令名称 (可选)
|
||||
* @param command - 新的指令内容
|
||||
* @param variables - 新的变量对象 (可选)
|
||||
* @returns 返回是否成功更新 (true/false)
|
||||
*/
|
||||
export const updateQuickCommand = async (id: number, name: string | null, command: string): Promise<boolean> => {
|
||||
const sql = `UPDATE quick_commands SET name = ?, command = ?, updated_at = strftime('%s', 'now') WHERE id = ?`;
|
||||
export const updateQuickCommand = async (id: number, name: string | null, command: string, variables?: Record<string, string>): Promise<boolean> => {
|
||||
const sql = `UPDATE quick_commands SET name = ?, command = ?, variables = ?, updated_at = strftime('%s', 'now') WHERE id = ?`;
|
||||
try {
|
||||
const db = await getDbInstance();
|
||||
const result = await runDb(db, sql, [name, command, id]);
|
||||
const variablesJson = variables ? JSON.stringify(variables) : null;
|
||||
const result = await runDb(db, sql, [name, command, variablesJson, id]);
|
||||
return result.changes > 0;
|
||||
} catch (err: any) {
|
||||
console.error('更新快捷指令时出错:', err.message);
|
||||
@@ -91,7 +98,7 @@ export const getAllQuickCommands = async (sortBy: 'name' | 'usage_count' = 'name
|
||||
// 使用 LEFT JOIN 连接关联表,并使用 GROUP_CONCAT 获取标签 ID 字符串
|
||||
const sql = `
|
||||
SELECT
|
||||
qc.id, qc.name, qc.command, qc.usage_count, qc.created_at, qc.updated_at,
|
||||
qc.id, qc.name, qc.command, qc.usage_count, qc.variables, qc.created_at, qc.updated_at,
|
||||
GROUP_CONCAT(qta.tag_id) as tag_ids_str
|
||||
FROM quick_commands qc
|
||||
LEFT JOIN quick_command_tag_associations qta ON qc.id = qta.quick_command_id
|
||||
@@ -100,11 +107,24 @@ export const getAllQuickCommands = async (sortBy: 'name' | 'usage_count' = 'name
|
||||
try {
|
||||
const db = await getDbInstance();
|
||||
const rows = await allDb<DbQuickCommandWithTagsRow>(db, sql);
|
||||
// 将 tag_ids_str 解析为数字数组
|
||||
return rows.map(row => ({
|
||||
...row,
|
||||
tagIds: row.tag_ids_str ? row.tag_ids_str.split(',').map(Number).filter(id => !isNaN(id)) : []
|
||||
}));
|
||||
// 将 tag_ids_str 解析为数字数组,并解析 variables
|
||||
return rows.map(row => {
|
||||
let parsedVariables: Record<string, string> | null = null;
|
||||
if (row.variables) {
|
||||
try {
|
||||
parsedVariables = JSON.parse(row.variables);
|
||||
} catch (e) {
|
||||
console.error(`Error parsing variables for quick command ${row.id}:`, e);
|
||||
//保持 parsedVariables 为 null
|
||||
}
|
||||
}
|
||||
const { variables, ...restOfRow } = row; // 从 row 中移除原始的 string 类型的 variables
|
||||
return {
|
||||
...restOfRow,
|
||||
variables: parsedVariables,
|
||||
tagIds: row.tag_ids_str ? row.tag_ids_str.split(',').map(Number).filter(id => !isNaN(id)) : []
|
||||
};
|
||||
});
|
||||
} catch (err: any) {
|
||||
console.error('获取快捷指令(带标签)时出错:', err.message);
|
||||
throw new Error('无法获取快捷指令');
|
||||
@@ -137,7 +157,7 @@ export const findQuickCommandById = async (id: number): Promise<QuickCommandWith
|
||||
// 使用 LEFT JOIN 连接关联表,并使用 GROUP_CONCAT 获取标签 ID 字符串
|
||||
const sql = `
|
||||
SELECT
|
||||
qc.id, qc.name, qc.command, qc.usage_count, qc.created_at, qc.updated_at,
|
||||
qc.id, qc.name, qc.command, qc.usage_count, qc.variables, qc.created_at, qc.updated_at,
|
||||
GROUP_CONCAT(qta.tag_id) as tag_ids_str
|
||||
FROM quick_commands qc
|
||||
LEFT JOIN quick_command_tag_associations qta ON qc.id = qta.quick_command_id
|
||||
@@ -147,9 +167,20 @@ export const findQuickCommandById = async (id: number): Promise<QuickCommandWith
|
||||
const db = await getDbInstance();
|
||||
const row = await getDbRow<DbQuickCommandWithTagsRow>(db, sql, [id]);
|
||||
if (row && typeof row.id !== 'undefined') {
|
||||
// 将 tag_ids_str 解析为数字数组
|
||||
// 将 tag_ids_str 解析为数字数组,并解析 variables
|
||||
let parsedVariables: Record<string, string> | null = null;
|
||||
if (row.variables) {
|
||||
try {
|
||||
parsedVariables = JSON.parse(row.variables);
|
||||
} catch (e) {
|
||||
console.error(`Error parsing variables for quick command ${row.id}:`, e);
|
||||
//保持 parsedVariables 为 null
|
||||
}
|
||||
}
|
||||
const { variables, ...restOfRow } = row; // 从 row 中移除原始的 string 类型的 variables
|
||||
return {
|
||||
...row,
|
||||
...restOfRow,
|
||||
variables: parsedVariables,
|
||||
tagIds: row.tag_ids_str ? row.tag_ids_str.split(',').map(Number).filter(id => !isNaN(id)) : []
|
||||
};
|
||||
} else {
|
||||
|
||||
@@ -10,15 +10,16 @@ export type QuickCommandSortBy = 'name' | 'usage_count';
|
||||
* @param name - 指令名称 (可选)
|
||||
* @param command - 指令内容
|
||||
* @param tagIds - 关联的快捷指令标签 ID 数组 (可选)
|
||||
* @param variables - 变量对象 (可选)
|
||||
* @returns 返回添加记录的 ID
|
||||
*/
|
||||
export const addQuickCommand = async (name: string | null, command: string, tagIds?: number[]): Promise<number> => {
|
||||
export const addQuickCommand = async (name: string | null, command: string, tagIds?: number[], variables?: Record<string, string>): Promise<number> => {
|
||||
if (!command || command.trim().length === 0) {
|
||||
throw new Error('指令内容不能为空');
|
||||
}
|
||||
// 如果 name 是空字符串,则视为 null
|
||||
const finalName = name && name.trim().length > 0 ? name.trim() : null;
|
||||
const commandId = await QuickCommandsRepository.addQuickCommand(finalName, command.trim());
|
||||
const commandId = await QuickCommandsRepository.addQuickCommand(finalName, command.trim(), variables);
|
||||
|
||||
// 添加成功后,设置标签关联
|
||||
if (commandId > 0 && tagIds && Array.isArray(tagIds)) {
|
||||
@@ -39,14 +40,15 @@ export const addQuickCommand = async (name: string | null, command: string, tagI
|
||||
* @param name - 新的指令名称 (可选)
|
||||
* @param command - 新的指令内容
|
||||
* @param tagIds - 新的关联标签 ID 数组 (可选, undefined 表示不更新标签)
|
||||
* @param variables - 新的变量对象 (可选)
|
||||
* @returns 返回是否成功更新 (更新行数 > 0)
|
||||
*/
|
||||
export const updateQuickCommand = async (id: number, name: string | null, command: string, tagIds?: number[]): Promise<boolean> => {
|
||||
export const updateQuickCommand = async (id: number, name: string | null, command: string, tagIds?: number[], variables?: Record<string, string>): Promise<boolean> => {
|
||||
if (!command || command.trim().length === 0) {
|
||||
throw new Error('指令内容不能为空');
|
||||
}
|
||||
const finalName = name && name.trim().length > 0 ? name.trim() : null;
|
||||
const commandUpdated = await QuickCommandsRepository.updateQuickCommand(id, finalName, command.trim());
|
||||
const commandUpdated = await QuickCommandsRepository.updateQuickCommand(id, finalName, command.trim(), variables);
|
||||
|
||||
// 如果指令更新成功,并且提供了 tagIds (即使是空数组也表示要更新),则更新标签关联
|
||||
if (commandUpdated && typeof tagIds !== 'undefined') {
|
||||
|
||||
Reference in New Issue
Block a user