src/process/services/database/README.md
本文档介绍 AionUi 的新数据库系统,它使用 better-sqlite3 (主进程) 作为持久化存储。
┌─────────────────────────────────────┐
│ 主进程 (Main Process) │
│ │
│ ┌─────────────────────────────┐ │
│ │ better-sqlite3 │ │
│ │ - 账户系统 │ │
│ │ - 聊天记录持久化 │ │
│ │ - 配置信息 (db_version) │ │
│ └─────────────────────────────┘ │
│ ↕ IPC │
└─────────────────────────────────────┘
↕ IPC
┌─────────────────────────────────────┐
│ 渲染进程 (Renderer Process) │
│ │
│ - IPC Bridge 直接查询主进程数据库 │
│ - React State 管理UI状态 │
│ - localStorage 保存临时数据 │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 文件系统 (File System) │
│ │
│ - 图片文件 (message.resultDisplay) │
│ - 大文件附件 │
│ - 数据库文件 (aionui.db) │
└─────────────────────────────────────┘
数据库层完全复用现有的业务类型定义:
TChatConversation - 会话类型TMessage - 消息类型首次启动时,系统会自动将文件存储的数据迁移到数据库,无需手动操作。
import { getDatabase } from '@/process/database/export';
// 获取数据库实例
const db = getDatabase();
// 创建会话
const conversation: TChatConversation = {
id: 'conv_123',
name: 'My Conversation',
type: 'gemini',
extra: { workspace: '/path/to/workspace' },
model: {
/* provider info */
},
createTime: Date.now(),
modifyTime: Date.now(),
};
const result = db.createConversation(conversation);
if (result.success) {
console.log('Conversation created');
}
// 插入消息
const message: TMessage = {
id: 'msg_123',
conversation_id: 'conv_123',
type: 'text',
content: { content: 'Hello world' },
position: 'right',
createdAt: Date.now(),
};
db.insertMessage(message);
// 查询会话的消息(分页)
const messages = db.getConversationMessages('conv_123', 0, 50);
console.log(messages.data); // TMessage[]
import { ipcBridge } from '@/common';
// 通过IPC查询消息
const messages = await ipcBridge.database.getConversationMessages({
conversation_id: 'conv_123',
page: 0,
pageSize: 100,
});
// 草稿使用React状态管理
const [draft, setDraft] = useState('');
// UI状态使用localStorage
localStorage.setItem('sidebar_collapsed', 'true');
const collapsed = localStorage.getItem('sidebar_collapsed') === 'true';
{userData}/config/aionui.db{userData}/data/images/其中 {userData} 为:
~/Library/Application Support/AionUi/%APPDATA%/AionUi/~/.config/AionUi/import { getMigrationStatus } from '@/process/database/export';
const status = await getMigrationStatus();
console.log(status);
// {
// completed: true,
// date: 1738012345678,
// version: 1,
// stats: { conversations: 10, messages: 532, ... }
// }
import { migrateFileStorageToDatabase } from '@/process/database/export';
const result = await migrateFileStorageToDatabase();
if (result.success) {
console.log('Migration completed:', result.stats);
} else {
console.error('Migration errors:', result.errors);
}
import { rollbackMigration } from '@/process/database/export';
await rollbackMigration();
// 清除迁移标记,可以重新运行迁移
import { exportDatabaseToJSON } from '@/process/database/export';
const data = await exportDatabaseToJSON();
await fs.writeFile('backup.json', JSON.stringify(data, null, 2));
import { importDatabaseFromJSON } from '@/process/database/export';
const data = JSON.parse(await fs.readFile('backup.json', 'utf-8'));
await importDatabaseFromJSON(data);
直接复制 aionui.db 和 aionui.db-wal 文件即可。
createConversation(conversation, userId?) - 创建会话getConversation(conversationId) - 获取会话getUserConversations(userId?, page?, pageSize?) - 获取用户的所有会话(分页)updateConversation(conversationId, updates) - 更新会话deleteConversation(conversationId) - 删除会话insertMessage(message) - 插入单条消息insertMessages(messages) - 批量插入消息getConversationMessages(conversationId, page?, pageSize?) - 获取会话消息(分页)deleteConversationMessages(conversationId) - 删除会话的所有消息setConfig(key, value) - 设置配置(主要用于数据库版本跟踪)getConfig<T>(key) - 获取配置getAllConfigs() - 获取所有配置deleteConfig(key) - 删除配置getStats() - 获取数据库统计信息(返回: users, conversations, messages)vacuum() - 清理数据库,回收空间database.getConversationMessages({ conversation_id, page?, pageSize? }) - 查询消息(支持分页)insertMessages() 而不是循环调用 insertMessage()db.vacuum() 清理数据库如果出现 "database is locked" 错误:
如果迁移失败:
rollbackMigration() 回滚如果better-sqlite3加载失败:
npm rebuild better-sqlite3数据库Schema有版本控制,当前版本为 v4。每个版本升级都有对应的迁移脚本。
import { getDatabase } from '@/process/database/export';
const db = getDatabase();
// 查看迁移历史
const history = db.getMigrationHistory();
console.log(history);
// [
// { version: 1, name: 'Initial schema', timestamp: 1738012345678 },
// { version: 2, name: 'Add performance indexes', timestamp: 1738012345679 },
// ...
// ]
// 检查特定迁移是否已执行
const isV2Applied = db.isMigrationApplied(2);
迁移脚本在 migrations.ts 中定义。每个迁移包含:
const migration_v5: IMigration = {
version: 5,
name: 'Add user sessions table',
up: (db) => {
db.exec(`
CREATE TABLE IF NOT EXISTS user_sessions (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
token TEXT NOT NULL,
expires_at INTEGER NOT NULL,
created_at INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_user_sessions_token
ON user_sessions(token);
`);
console.log('[Migration v5] Added user sessions table');
},
down: (db) => {
db.exec(`DROP TABLE IF EXISTS user_sessions;`);
console.log('[Migration v5] Rolled back: Removed user sessions table');
},
};
// 添加到迁移列表
export const ALL_MIGRATIONS: IMigration[] = [
migration_v1,
migration_v2,
migration_v3,
migration_v4,
migration_v5, // 新增
];
export const CURRENT_DB_VERSION = 5; // 从 4 改为 5
应用启动时会自动检测版本变化并执行迁移:
[Database] Migrating from version 4 to 5
[Migrations] Running 1 migrations from v4 to v5
[Migrations] Running migration v5: Add user sessions table
[Migration v5] Added user sessions table
[Migrations] ✓ Migration v5 completed
[Migrations] All migrations completed successfully
所有迁移在单个事务中执行。如果任何迁移失败,所有更改将回滚:
// migrations.ts
const runAll = db.transaction(() => {
for (const migration of migrations) {
migration.up(db); // 如果抛出异常,整个事务回滚
}
});
每个成功的迁移都会记录在 configs 表中:
SELECT * FROM configs WHERE key LIKE 'migration_v%';
-- migration_v1: {"version":1,"name":"Initial schema","timestamp":1738012345678}
-- migration_v2: {"version":2,"name":"Add performance indexes","timestamp":1738012345679}
所有迁移使用 IF NOT EXISTS 确保可以安全重复执行。
import { rollbackMigrations } from '@/process/database/export';
// ⚠️ WARNING: 这会导致数据丢失!
const db = getDatabase();
rollbackMigrations(db.db, 4, 2); // 从 v4 回滚到 v2
// 回滚后需要手动更新版本号
setDatabaseVersion(db.db, 2);
ALTER TABLE ADD COLUMN 而不是删除字段down() 方法能正确恢复up: (db) => {
db.exec(`
CREATE TABLE IF NOT EXISTS new_table (
id TEXT PRIMARY KEY,
...
);
`);
};
up: (db) => {
db.exec(`
ALTER TABLE users ADD COLUMN phone TEXT;
`);
};
up: (db) => {
db.exec(`
CREATE INDEX IF NOT EXISTS idx_users_phone ON users(phone);
`);
};
up: (db) => {
// 先添加新字段
db.exec(`ALTER TABLE users ADD COLUMN full_name TEXT;`);
// 然后迁移数据
db.exec(`
UPDATE users
SET full_name = COALESCE(first_name || ' ' || last_name, username)
WHERE full_name IS NULL;
`);
};
如需添加新的数据库功能:
schema.ts 中添加表结构types.ts 中定义类型(优先复用现有业务类型)index.ts 中添加CRUD方法最后更新: 2025-01-27