fix: tools group storage bug

This commit is contained in:
ikechan8370 2025-04-13 17:24:37 +08:00
parent f4fb3dddd2
commit 51765fcfa0

View file

@ -38,26 +38,217 @@ export class SQLiteToolsGroupStorage extends ChaiteStorage {
this.db = new sqlite3.Database(this.dbPath, async (err) => { this.db = new sqlite3.Database(this.dbPath, async (err) => {
if (err) return reject(err) if (err) return reject(err)
try {
// 首先检查表是否存在
const tableExists = await this.checkTableExists()
if (tableExists) {
// 如果表存在,检查并迁移旧结构
await this.migrateTableIfNeeded()
} else {
// 如果表不存在,创建新表
await this.createTable()
}
// 确保索引存在
await this.ensureIndex()
this.initialized = true
resolve()
} catch (error) {
reject(error)
}
})
})
}
/**
* 检查表是否存在
*/
async checkTableExists () {
return new Promise((resolve, reject) => {
this.db.get(
'SELECT name FROM sqlite_master WHERE type=\'table\' AND name=?',
[this.tableName],
(err, row) => {
if (err) return reject(err)
resolve(!!row)
}
)
})
}
/**
* 创建新表
*/
async createTable () {
return new Promise((resolve, reject) => {
this.db.run(`CREATE TABLE IF NOT EXISTS ${this.tableName} ( this.db.run(`CREATE TABLE IF NOT EXISTS ${this.tableName} (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
description TEXT, description TEXT,
tools TEXT NOT NULL, toolIds TEXT NOT NULL,
isDefault INTEGER DEFAULT 0,
createdAt TEXT, createdAt TEXT,
updatedAt TEXT updatedAt TEXT
)`, (err) => { )`, (err) => {
if (err) return reject(err) if (err) return reject(err)
this.db.run(`CREATE INDEX IF NOT EXISTS idx_tools_groups_name ON ${this.tableName} (name)`, (err) => {
if (err) return reject(err)
this.initialized = true
resolve() resolve()
}) })
}) })
}
/**
* 确保索引存在
*/
async ensureIndex () {
return new Promise((resolve, reject) => {
this.db.run(`CREATE INDEX IF NOT EXISTS idx_tools_groups_name ON ${this.tableName} (name)`, (err) => {
if (err) return reject(err)
resolve()
}) })
}) })
} }
/**
* 检查并迁移表结构
*/
async migrateTableIfNeeded () {
// 检查表结构
const columns = await this.getTableColumns()
// 检查是否有旧版本的结构有tools字段而不是toolIds
const hasOldStructure = columns.includes('tools') && !columns.includes('toolIds')
const needsDefaultColumn = !columns.includes('isDefault')
if (hasOldStructure || needsDefaultColumn) {
console.log(`检测到旧表结构,开始迁移 ${this.tableName} 表...`)
// 备份所有数据
const allData = await this.backupData()
// 重命名旧表
await this.renameTable(`${this.tableName}_old`)
// 创建新表
await this.createTable()
await this.ensureIndex()
// 恢复数据到新表
if (allData.length > 0) {
await this.restoreData(allData, hasOldStructure)
}
// 删除旧表
await this.dropTable(`${this.tableName}_old`)
console.log(`${this.tableName} 迁移完成,共迁移 ${allData.length} 条数据`)
}
}
/**
* 获取表的所有列名
*/
async getTableColumns () {
return new Promise((resolve, reject) => {
this.db.all(`PRAGMA table_info(${this.tableName})`, (err, rows) => {
if (err) return reject(err)
const columns = rows.map(row => row.name)
resolve(columns)
})
})
}
/**
* 备份表数据
*/
async backupData () {
return new Promise((resolve, reject) => {
this.db.all(`SELECT * FROM ${this.tableName}`, (err, rows) => {
if (err) return reject(err)
resolve(rows)
})
})
}
/**
* 重命名表
*/
async renameTable (newName) {
return new Promise((resolve, reject) => {
this.db.run(`ALTER TABLE ${this.tableName} RENAME TO ${newName}`, (err) => {
if (err) return reject(err)
resolve()
})
})
}
/**
* 删除表
*/
async dropTable (tableName) {
return new Promise((resolve, reject) => {
this.db.run(`DROP TABLE IF EXISTS ${tableName}`, (err) => {
if (err) return reject(err)
resolve()
})
})
}
/**
* 恢复数据到新表
*/
async restoreData (data, hasOldStructure) {
const promises = data.map(row => {
return new Promise((resolve, reject) => {
// 处理数据转换
const newRow = { ...row }
if (hasOldStructure && row.tools) {
try {
// 从旧的tools结构提取toolIds
const tools = JSON.parse(row.tools)
newRow.toolIds = JSON.stringify(tools.map(t => t.id || t))
delete newRow.tools
} catch (e) {
console.error(`解析工具组数据错误: ${row.id}`, e)
newRow.toolIds = JSON.stringify([])
delete newRow.tools
}
}
// 添加isDefault字段
if (newRow.isDefault === undefined) {
newRow.isDefault = 0
}
// 添加时间戳
if (!newRow.createdAt) {
newRow.createdAt = new Date().toISOString()
}
if (!newRow.updatedAt) {
newRow.updatedAt = new Date().toISOString()
}
const fields = Object.keys(newRow)
const placeholders = fields.map(() => '?').join(',')
const values = fields.map(field => newRow[field])
this.db.run(
`INSERT INTO ${this.tableName} (${fields.join(',')}) VALUES (${placeholders})`,
values,
(err) => {
if (err) return reject(err)
resolve()
}
)
})
})
return Promise.all(promises)
}
async ensureInitialized () { async ensureInitialized () {
if (!this.initialized) { if (!this.initialized) {
await this.initialize() await this.initialize()
@ -80,14 +271,16 @@ export class SQLiteToolsGroupStorage extends ChaiteStorage {
try { try {
const toolsGroup = { const toolsGroup = {
...row, ...row,
tools: JSON.parse(row.tools) toolIds: JSON.parse(row.toolIds),
isDefault: Boolean(row.isDefault)
} }
resolve(toolsGroup) resolve(toolsGroup)
} catch (e) { } catch (e) {
console.error(`解析工具组数据错误: ${key}`, e) console.error(`解析工具组数据错误: ${key}`, e)
resolve({ resolve({
...row, ...row,
tools: [] toolIds: [],
isDefault: Boolean(row.isDefault)
}) })
} }
}) })
@ -112,11 +305,12 @@ export class SQLiteToolsGroupStorage extends ChaiteStorage {
data.updatedAt = new Date().toISOString() data.updatedAt = new Date().toISOString()
// 提取工具组数据 // 提取工具组数据
const { name, description, tools } = data const { name, description, toolIds, isDefault } = data
const updatedAt = Date.now() const updatedAt = new Date().toISOString()
// 将工具列表序列化为JSON字符串 // 将工具ID列表序列化为JSON字符串
const toolsJson = JSON.stringify(tools || []) const toolIdsJson = JSON.stringify(toolIds || [])
const isDefaultValue = isDefault ? 1 : 0
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 检查工具组是否已存在 // 检查工具组是否已存在
@ -128,8 +322,8 @@ export class SQLiteToolsGroupStorage extends ChaiteStorage {
if (row) { if (row) {
// 更新现有工具组 // 更新现有工具组
this.db.run( this.db.run(
`UPDATE ${this.tableName} SET name = ?, description = ?, tools = ?, updatedAt = ? WHERE id = ?`, `UPDATE ${this.tableName} SET name = ?, description = ?, toolIds = ?, isDefault = ?, updatedAt = ? WHERE id = ?`,
[name, description, toolsJson, updatedAt, id], [name, description, toolIdsJson, isDefaultValue, updatedAt, id],
(err) => { (err) => {
if (err) { if (err) {
return reject(err) return reject(err)
@ -140,8 +334,8 @@ export class SQLiteToolsGroupStorage extends ChaiteStorage {
} else { } else {
// 插入新工具组 // 插入新工具组
this.db.run( this.db.run(
`INSERT INTO ${this.tableName} (id, name, description, tools, updatedAt) VALUES (?, ?, ?, ?, ?)`, `INSERT INTO ${this.tableName} (id, name, description, toolIds, isDefault, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?, ?)`,
[id, name, description, toolsJson, updatedAt], [id, name, description, toolIdsJson, isDefaultValue, data.createdAt, updatedAt],
(err) => { (err) => {
if (err) { if (err) {
return reject(err) return reject(err)
@ -187,13 +381,15 @@ export class SQLiteToolsGroupStorage extends ChaiteStorage {
try { try {
return { return {
...row, ...row,
tools: JSON.parse(row.tools) toolIds: JSON.parse(row.toolIds),
isDefault: Boolean(row.isDefault)
} }
} catch (e) { } catch (e) {
console.error(`解析工具组数据错误: ${row.id}`, e) console.error(`解析工具组数据错误: ${row.id}`, e)
return { return {
...row, ...row,
tools: [] toolIds: [],
isDefault: Boolean(row.isDefault)
} }
} }
}) })
@ -222,6 +418,9 @@ export class SQLiteToolsGroupStorage extends ChaiteStorage {
if (directFields.includes(key)) { if (directFields.includes(key)) {
conditions.push(`${key} = ?`) conditions.push(`${key} = ?`)
params.push(filter[key]) params.push(filter[key])
} else if (key === 'isDefault') {
conditions.push('isDefault = ?')
params.push(filter[key] ? 1 : 0)
} }
} }
@ -237,12 +436,15 @@ export class SQLiteToolsGroupStorage extends ChaiteStorage {
try { try {
const group = { const group = {
...row, ...row,
tools: JSON.parse(row.tools || '[]') toolIds: JSON.parse(row.toolIds || '[]'),
isDefault: Boolean(row.isDefault)
} }
// 过滤非直接字段 // 过滤其他字段
for (const key in filter) { for (const key in filter) {
if (!directFields.includes(key) && group[key] !== filter[key]) { if (!directFields.includes(key) &&
key !== 'isDefault' &&
JSON.stringify(group[key]) !== JSON.stringify(filter[key])) {
return null return null
} }
} }
@ -280,6 +482,11 @@ export class SQLiteToolsGroupStorage extends ChaiteStorage {
const placeholders = item.values.map(() => '?').join(',') const placeholders = item.values.map(() => '?').join(',')
conditions.push(`${item.field} IN (${placeholders})`) conditions.push(`${item.field} IN (${placeholders})`)
params.push(...item.values) params.push(...item.values)
} else if (item.field === 'isDefault' && Array.isArray(item.values) && item.values.length > 0) {
const boolValues = item.values.map(v => v ? 1 : 0)
const placeholders = boolValues.map(() => '?').join(',')
conditions.push(`isDefault IN (${placeholders})`)
params.push(...boolValues)
} else if (item.values.length > 0) { } else if (item.values.length > 0) {
memoryQueries.push(item) memoryQueries.push(item)
} }
@ -297,7 +504,8 @@ export class SQLiteToolsGroupStorage extends ChaiteStorage {
try { try {
return { return {
...row, ...row,
tools: JSON.parse(row.tools || '[]') toolIds: JSON.parse(row.toolIds || '[]'),
isDefault: Boolean(row.isDefault)
} }
} catch (e) { } catch (e) {
console.error(`解析工具组数据错误: ${row.id}`, e) console.error(`解析工具组数据错误: ${row.id}`, e)
@ -309,7 +517,11 @@ export class SQLiteToolsGroupStorage extends ChaiteStorage {
if (memoryQueries.length > 0) { if (memoryQueries.length > 0) {
toolsGroups = toolsGroups.filter(group => { toolsGroups = toolsGroups.filter(group => {
for (const { field, values } of memoryQueries) { for (const { field, values } of memoryQueries) {
if (!values.includes(group[field])) { // 对于toolIds字段做特殊处理
if (field === 'toolIds') {
const hasMatch = values.some(toolId => group.toolIds.includes(toolId))
if (!hasMatch) return false
} else if (!values.includes(group[field])) {
return false return false
} }
} }