Download electron/database.ts from Leon4gr45/Velo: direct link, hf CLI and curl.
- Browser
- Download file 16.9 kB
-
https://huggingface.co/spaces/Leon4gr45/Velo/resolve/main/electron/database.ts
- Command line
-
hf download hf://spaces/Leon4gr45/Velo/electron/database.ts
-
curl -L -o database.ts https://huggingface.co/spaces/Leon4gr45/Velo/resolve/main/electron/database.ts
16.9 kB
| /** | |
| * Vela SQLite 数据库服务 — 主进程使用 | |
| * | |
| * 负责 SQLite 实例的连接、生命周期与建表。 | |
| * 具体业务逻辑由 /repositories 提供。 | |
| */ | |
| import { createRequire } from 'node:module' | |
| import path from 'node:path' | |
| import fs from 'node:fs' | |
| const require = createRequire(import.meta.url) | |
| const Database = require('better-sqlite3') as typeof import('better-sqlite3') | |
| import type BetterSqlite3 from 'better-sqlite3' | |
| let projectDb: BetterSqlite3.Database | null = null | |
| /** 初始化项目数据库(打开项目时调用) */ | |
| export function initProjectDatabase(projectPath: string): void { | |
| closeProjectDatabase() | |
| const dbPath = path.join(projectPath, '.vela', 'vela.db') | |
| fs.mkdirSync(path.dirname(dbPath), { recursive: true }) | |
| projectDb = new Database(dbPath) | |
| projectDb.pragma('journal_mode = WAL') | |
| projectDb.pragma('foreign_keys = ON') | |
| // 创建表结构 | |
| createTables(projectDb) | |
| // 对老库执行 schema 迁移(加 UNIQUE 约束等) | |
| migrateProjectDatabase(projectDb) | |
| console.log(`[Vela DB] 项目数据库已打开: ${dbPath}`) | |
| } | |
| /** 关闭项目数据库 */ | |
| export function closeProjectDatabase(): void { | |
| if (projectDb) { | |
| projectDb.close() | |
| projectDb = null | |
| } | |
| } | |
| /** 已执行的 schema 迁移版本号(用于幂等迁移) */ | |
| const SCHEMA_VERSION = 1 | |
| /** 对老库执行 schema 迁移(加 UNIQUE/CHECK 约束等) */ | |
| function migrateProjectDatabase(db: BetterSqlite3.Database): void { | |
| const currentVersionRow = db.prepare( | |
| `PRAGMA user_version` | |
| ).get() as { user_version: number } | undefined | |
| const currentVersion = currentVersionRow?.user_version ?? 0 | |
| if (currentVersion >= SCHEMA_VERSION) return | |
| // v0 → v1: 给 canon 表加约束(仅当索引不存在时) | |
| if (currentVersion < 1) { | |
| const timelineUnique = db.prepare( | |
| `SELECT 1 FROM sqlite_master WHERE type='index' AND name='idx_canon_timeline_unique'` | |
| ).get() | |
| if (!timelineUnique) { | |
| // 注意:先尝试 CREATE UNIQUE INDEX;若有重复数据会失败,需要清理 | |
| try { | |
| // 清理重复 sequence,保留 id 最小的那条 | |
| db.exec(` | |
| DELETE FROM canon_timeline_events | |
| WHERE id NOT IN ( | |
| SELECT MIN(id) FROM canon_timeline_events | |
| GROUP BY chapter_number, sequence | |
| ) | |
| `) | |
| db.exec(`CREATE UNIQUE INDEX idx_canon_timeline_unique ON canon_timeline_events(chapter_number, sequence)`) | |
| } catch (e) { | |
| console.warn('[Vela DB] 添加 canon_timeline unique 约束失败(可能存在冲突数据):', e) | |
| } | |
| } | |
| const factsUnique = db.prepare( | |
| `SELECT 1 FROM sqlite_master WHERE type='index' AND name='idx_canon_facts_unique'` | |
| ).get() | |
| if (!factsUnique) { | |
| try { | |
| db.exec(` | |
| DELETE FROM canon_facts | |
| WHERE id NOT IN ( | |
| SELECT MIN(id) FROM canon_facts | |
| WHERE statement IS NOT NULL AND statement != '' | |
| GROUP BY LOWER(TRIM(statement)) | |
| ) | |
| `) | |
| db.exec(`CREATE UNIQUE INDEX idx_canon_facts_unique ON canon_facts(statement COLLATE NOCASE)`) | |
| } catch (e) { | |
| console.warn('[Vela DB] 添加 canon_facts unique 约束失败:', e) | |
| } | |
| } | |
| const plotUnique = db.prepare( | |
| `SELECT 1 FROM sqlite_master WHERE type='index' AND name='idx_canon_plot_unique'` | |
| ).get() | |
| if (!plotUnique) { | |
| try { | |
| db.exec(` | |
| DELETE FROM canon_plot_lines | |
| WHERE id NOT IN ( | |
| SELECT MIN(id) FROM canon_plot_lines | |
| GROUP BY LOWER(TRIM(name)) | |
| ) | |
| `) | |
| db.exec(`CREATE UNIQUE INDEX idx_canon_plot_unique ON canon_plot_lines(name COLLATE NOCASE)`) | |
| } catch (e) { | |
| console.warn('[Vela DB] 添加 canon_plot unique 约束失败:', e) | |
| } | |
| } | |
| } | |
| db.pragma(`user_version = ${SCHEMA_VERSION}`) | |
| } | |
| /** 获取当前数据库实例 */ | |
| export function getProjectDb(): BetterSqlite3.Database | null { | |
| return projectDb | |
| } | |
| /** 创建完整表结构(9 张核心表 + 2 张沿用表) */ | |
| function createTables(db: BetterSqlite3.Database) { | |
| db.exec(` | |
| -- ============================================================ | |
| -- 1. project_core — 项目主台账(NovelConfig + 架构四大件) | |
| -- ============================================================ | |
| CREATE TABLE IF NOT EXISTS project_core ( | |
| id TEXT PRIMARY KEY DEFAULT 'main', | |
| project_name TEXT NOT NULL DEFAULT '', -- 小说工程名 | |
| -- [基础定位] | |
| genre TEXT DEFAULT '', -- 核心流派 | |
| sub_genre TEXT DEFAULT '', -- 细分流派 | |
| target_audience TEXT DEFAULT '', -- 目标受众 | |
| total_chapters INTEGER DEFAULT 100, -- 预计总章数 | |
| words_per_chapter INTEGER DEFAULT 3000, -- 单章基准字数 | |
| -- [写作技法] | |
| plot_structure TEXT DEFAULT 'three_act', -- 故事模型 | |
| narrative_pov TEXT DEFAULT 'third_limited', -- 叙事视角 | |
| writing_style TEXT DEFAULT '', -- 文风描述 | |
| reference_works TEXT DEFAULT '', -- 参考作品 | |
| global_guidance TEXT DEFAULT '', -- 全局行文指导 | |
| golden_finger TEXT DEFAULT '', -- 金手指设定 | |
| -- [架构四大件] | |
| premise TEXT DEFAULT '', -- 故事前提 | |
| worldbuilding TEXT DEFAULT '', -- 世界观 | |
| characters_arch TEXT DEFAULT '', -- 人物群像网络 | |
| synopsis TEXT DEFAULT '', -- 情节总大纲 | |
| -- [系统缓存] | |
| character_states TEXT DEFAULT '', -- 全书角色动态快照 | |
| created_at TEXT DEFAULT (datetime('now')), | |
| updated_at TEXT DEFAULT (datetime('now')) | |
| ); | |
| -- ============================================================ | |
| -- 2. blueprints — 章节蓝图 | |
| -- ============================================================ | |
| CREATE TABLE IF NOT EXISTS blueprints ( | |
| chapter_number INTEGER PRIMARY KEY, -- 章节序号 | |
| title TEXT NOT NULL DEFAULT '', -- 章节标题 | |
| role TEXT DEFAULT '', -- 章节角色 | |
| purpose TEXT DEFAULT '', -- 核心目的 | |
| key_events TEXT DEFAULT '', -- 关键事件 | |
| characters TEXT DEFAULT '[]', -- 出场角色 (JSON Array) | |
| suspense_hook TEXT DEFAULT '', -- 悬念钩子 | |
| user_guidance TEXT DEFAULT '', -- 用户预设指导 | |
| notes TEXT DEFAULT '', -- 后处理提取的章节要点 | |
| notes_updated_at TEXT DEFAULT '', -- notes 提取时间 | |
| created_at TEXT DEFAULT (datetime('now')), | |
| updated_at TEXT DEFAULT (datetime('now')) | |
| ); | |
| -- ============================================================ | |
| -- 3. characters — 角色卡(currentState 拍平为 cs_* 列) | |
| -- ============================================================ | |
| CREATE TABLE IF NOT EXISTS characters ( | |
| name TEXT PRIMARY KEY, -- 角色名 | |
| role TEXT DEFAULT 'supporting', -- protagonist/antagonist/supporting/minor | |
| gender TEXT DEFAULT '', | |
| age TEXT DEFAULT '', | |
| appearance TEXT DEFAULT '', -- 外貌 | |
| personality TEXT DEFAULT '', -- 性格 | |
| background TEXT DEFAULT '', -- 背景 | |
| abilities TEXT DEFAULT '', -- 能力 | |
| motivation TEXT DEFAULT '', -- 动机 | |
| relationships TEXT DEFAULT '', -- 关系链 | |
| arc TEXT DEFAULT '', -- 弧光 | |
| notes TEXT DEFAULT '', -- 备忘录 | |
| cs_location TEXT DEFAULT '', -- 当前位置 | |
| cs_power_level TEXT DEFAULT '', -- 修为境界 | |
| cs_physical_state TEXT DEFAULT '', -- 身体状态 | |
| cs_mental_state TEXT DEFAULT '', -- 心理状态 | |
| cs_key_items TEXT DEFAULT '', -- 关键道具 | |
| cs_recent_events TEXT DEFAULT '', -- 最近事件 | |
| cs_updated_at_chapter INTEGER DEFAULT 0, -- 状态更新于第几章 | |
| created_at TEXT DEFAULT (datetime('now')), | |
| updated_at TEXT DEFAULT (datetime('now')) | |
| ); | |
| -- ============================================================ | |
| -- 4. contents — 文本内容池(正文与元数据分离) | |
| -- ============================================================ | |
| CREATE TABLE IF NOT EXISTS contents ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| body TEXT NOT NULL DEFAULT '', -- 正文/报告内容 | |
| created_at TEXT DEFAULT (datetime('now')) | |
| ); | |
| -- ============================================================ | |
| -- 5. drafts — 草稿主线(finalized = 定稿) | |
| -- ============================================================ | |
| CREATE TABLE IF NOT EXISTS drafts ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| chapter_number INTEGER NOT NULL, -- 归属章节 | |
| version INTEGER NOT NULL, -- v1, v2... | |
| status TEXT DEFAULT 'draft', -- draft/revised/finalized/archived | |
| source TEXT DEFAULT 'write', -- write/rewrite | |
| content_id INTEGER NOT NULL, -- FK -> contents | |
| word_count INTEGER DEFAULT 0, -- 字数缓存 | |
| created_at TEXT DEFAULT (datetime('now')), | |
| updated_at TEXT DEFAULT (datetime('now')), | |
| FOREIGN KEY (content_id) REFERENCES contents(id) ON DELETE RESTRICT | |
| ); | |
| CREATE INDEX IF NOT EXISTS idx_drafts_chapter ON drafts(chapter_number); | |
| -- ============================================================ | |
| -- 6. revisions — 修稿(派生自 draft) | |
| -- ============================================================ | |
| CREATE TABLE IF NOT EXISTS revisions ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| base_draft_id INTEGER NOT NULL, -- 父草稿 FK | |
| revision_index INTEGER NOT NULL, -- r1, r2 | |
| revision_type TEXT NOT NULL, -- refine | review-fix | |
| status TEXT DEFAULT 'pending', -- pending/merged/discarded | |
| merged_to_draft_id INTEGER, -- 合并产出的新 draft | |
| user_prompt TEXT DEFAULT '', -- 用户指导 | |
| review_source_id INTEGER, -- 关联审稿 ID | |
| content_id INTEGER NOT NULL, -- FK -> contents | |
| word_count INTEGER DEFAULT 0, -- 字数缓存 | |
| created_at TEXT DEFAULT (datetime('now')), | |
| updated_at TEXT DEFAULT (datetime('now')), | |
| FOREIGN KEY (base_draft_id) REFERENCES drafts(id) ON DELETE CASCADE, | |
| FOREIGN KEY (content_id) REFERENCES contents(id) ON DELETE RESTRICT | |
| ); | |
| -- ============================================================ | |
| -- 7. reviews — 审稿(派生自 draft) | |
| -- ============================================================ | |
| CREATE TABLE IF NOT EXISTS reviews ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| base_draft_id INTEGER NOT NULL, -- 审查对象 FK | |
| review_index INTEGER NOT NULL, -- 审阅顺位 | |
| content_id INTEGER NOT NULL, -- FK -> contents | |
| created_at TEXT DEFAULT (datetime('now')), | |
| FOREIGN KEY (base_draft_id) REFERENCES drafts(id) ON DELETE CASCADE, | |
| FOREIGN KEY (content_id) REFERENCES contents(id) ON DELETE RESTRICT | |
| ); | |
| -- ============================================================ | |
| -- 8. post_process_runs — 后处理跑批实例 | |
| -- ============================================================ | |
| CREATE TABLE IF NOT EXISTS post_process_runs ( | |
| id TEXT PRIMARY KEY, -- UUID | |
| trigger_source_type TEXT NOT NULL, -- chapter_finalize / arch_extract | |
| trigger_source_id TEXT NOT NULL, -- 章节号 / draft_id | |
| source_label TEXT DEFAULT '', -- UI 标签 | |
| all_critical_passed INTEGER DEFAULT 0, | |
| created_at TEXT DEFAULT (datetime('now')), | |
| updated_at TEXT DEFAULT (datetime('now')) | |
| ); | |
| CREATE INDEX IF NOT EXISTS idx_post_runs_source | |
| ON post_process_runs(trigger_source_type, trigger_source_id); | |
| -- ============================================================ | |
| -- 9. post_process_steps — 后处理步骤明细 | |
| -- ============================================================ | |
| CREATE TABLE IF NOT EXISTS post_process_steps ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| run_id TEXT NOT NULL, -- FK -> post_process_runs | |
| step_key TEXT NOT NULL, -- 步骤标识 | |
| label TEXT DEFAULT '', -- 展示名称 | |
| critical INTEGER DEFAULT 0, -- 是否关键步骤 | |
| ok INTEGER DEFAULT 0, -- 是否完成 | |
| error_msg TEXT DEFAULT '', | |
| attempt_count INTEGER DEFAULT 0, | |
| completed_at TEXT DEFAULT '', | |
| last_attempt_at TEXT DEFAULT '', | |
| FOREIGN KEY (run_id) REFERENCES post_process_runs(id) ON DELETE CASCADE | |
| ); | |
| -- ============================================================ | |
| -- 沿用表:LLM 调用记录 | |
| -- ============================================================ | |
| CREATE TABLE IF NOT EXISTS llm_calls ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| model_id TEXT NOT NULL, | |
| model_name TEXT DEFAULT '', | |
| purpose TEXT DEFAULT '', | |
| prompt_tokens INTEGER DEFAULT 0, | |
| completion_tokens INTEGER DEFAULT 0, | |
| total_tokens INTEGER DEFAULT 0, | |
| duration_ms INTEGER DEFAULT 0, | |
| success INTEGER DEFAULT 1, | |
| error_message TEXT DEFAULT '', | |
| created_at TEXT DEFAULT (datetime('now')) | |
| ); | |
| -- ============================================================ | |
| -- 沿用表:角色状态快照 | |
| -- ============================================================ | |
| CREATE TABLE IF NOT EXISTS summary_snapshots ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| chapter_number INTEGER NOT NULL, | |
| character_states TEXT DEFAULT '', | |
| created_at TEXT DEFAULT (datetime('now')) | |
| ); | |
| -- ============================================================ | |
| -- 叙事一致性 (Narrative Consistency) —— Canon Store | |
| -- ============================================================ | |
| -- 结构化时间线事件 | |
| CREATE TABLE IF NOT EXISTS canon_timeline_events ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| chapter_number INTEGER NOT NULL, | |
| sequence INTEGER NOT NULL, | |
| characters TEXT DEFAULT '[]', | |
| location TEXT DEFAULT '', | |
| time_flow TEXT DEFAULT 'sequential', | |
| summary TEXT DEFAULT '', | |
| impact TEXT DEFAULT '', | |
| created_at TEXT DEFAULT (datetime('now')) | |
| ); | |
| CREATE INDEX IF NOT EXISTS idx_canon_timeline_chapter_seq | |
| ON canon_timeline_events(chapter_number, sequence); | |
| -- 角色状态历史(每个角色每个章节一条最新) | |
| CREATE TABLE IF NOT EXISTS canon_character_state ( | |
| character TEXT PRIMARY KEY, | |
| location TEXT DEFAULT '', | |
| power_level TEXT DEFAULT '', | |
| physical_state TEXT DEFAULT '', | |
| mental_state TEXT DEFAULT '', | |
| key_items TEXT DEFAULT '', | |
| current_goal TEXT DEFAULT '', | |
| knowledge_json TEXT DEFAULT '[]', | |
| relationships_json TEXT DEFAULT '{}', | |
| recent_events TEXT DEFAULT '', | |
| updated_at_chapter INTEGER DEFAULT 0, | |
| updated_at TEXT DEFAULT (datetime('now')) | |
| ); | |
| -- 长期未结剧情线 | |
| CREATE TABLE IF NOT EXISTS canon_plot_lines ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| name TEXT NOT NULL, | |
| status TEXT DEFAULT 'active', | |
| started_at INTEGER DEFAULT 0, | |
| last_advanced_at INTEGER DEFAULT 0, | |
| resolved_at INTEGER, | |
| characters TEXT DEFAULT '[]', | |
| current_state TEXT DEFAULT '', | |
| description TEXT DEFAULT '', | |
| created_at TEXT DEFAULT (datetime('now')) | |
| ); | |
| CREATE INDEX IF NOT EXISTS idx_canon_plot_status ON canon_plot_lines(status); | |
| -- 客观事实条目 | |
| CREATE TABLE IF NOT EXISTS canon_facts ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| category TEXT NOT NULL, | |
| statement TEXT NOT NULL, | |
| introduced_at INTEGER DEFAULT 0, | |
| characters TEXT DEFAULT '[]', | |
| evidence TEXT DEFAULT '', | |
| created_at TEXT DEFAULT (datetime('now')) | |
| ); | |
| CREATE INDEX IF NOT EXISTS idx_canon_facts_category ON canon_facts(category); | |
| -- 章节摘要(结构化) | |
| CREATE TABLE IF NOT EXISTS canon_chapter_summaries ( | |
| chapter_number INTEGER PRIMARY KEY, | |
| title TEXT DEFAULT '', | |
| summary TEXT DEFAULT '', | |
| created_at TEXT DEFAULT (datetime('now')) | |
| ); | |
| -- 索引 | |
| CREATE INDEX IF NOT EXISTS idx_llm_calls_time ON llm_calls(created_at); | |
| `) | |
| } | |