AstroResearch/migrations/00000000000000_init.sql
Asfmq 5db4cc5998 refactor: 全栈架构重构与质量硬化——API 错误统一、工具域重组、安全加固、前端组件化
后端核心变更:
  - API 层: 新增 AppError 枚举统一错误类型,替代散落的 (StatusCode, String)
  - Agent 工具域: 重组为 astro/system/ 和 astro/research/ 两级域,新增 ProcessPaperTool 流水线工具
  - 安全: 新增 SSRF 双层防护 (同步字符串级 + 异步 DNS 解析级),覆盖 IPv4/IPv6 私网段
  - 弱密码检测: 扩展弱密码列表并增加最小长度检查
  - LLM 客户端: 新增 ChatCompleter/Embedder trait,支持依赖注入与批量向量化 embed_batch
  - 批量处理: AssetBatch 从串行改为 Semaphore 并发池 (BATCH_CONCURRENCY=3)
  - 分块器: 重写为三阶段结构化管线 (章节解析→短节合并→带标题路径子块)
  - RAG: embedding 计算移出事务,RetrievalResult 新增 headings/section_index 字段
  - 检索: ADS/arXiv 并行检索 (tokio::join!),去重改用 HashSet,本地库回填批量 IN 查询
  - 天体查询: Sesame API 升级到 v4,新增视差误差/自行/视向速度/多波段测光字段
  - 迁移: 14 个增量文件合并为单一 init.sql,支持 sqlx::migrate! 内存库集成测试
  - 测试: circuit_breaker/hooks/task_board/session/memory/streaming_executor 新增修正 15+ 测试

  前端架构重构:
  - 目录重组: features/ → pages/ + components/ + hooks/ 三层分离
  - App.tsx 从 1181 行压缩至 ~174 行 (逻辑抽入 9 个自定义 Hook)
  - Agent 面板拆分为 AgentSessionSidebar/AgentMessageList/AgentInputArea 子组件
  - 新增 GlobalDialog/PaperDetailModal/UncachedPaperModal 通用对话框组件
  - 工具函数抽取: celestial.ts (天体坐标格式), paper.tsx (文献信息渲染)
2026-06-25 23:45:37 +08:00

357 lines
12 KiB
SQL

-- AstroResearch 完整数据库初始化迁移
-- 合并自 18 个开发期增量迁移文件,版本: 2026-06-25
--
-- 领域划分:
-- 1. 文献与引用
-- 2. 笔记与高亮
-- 3. 批量同步
-- 4. RAG 切片与天体目标
-- 5. Agent 会话与消息
-- 6. Agent 任务看板
-- 7. Agent 审计日志
-- 8. Agent 团队协作
-- 9. 全文搜索 (FTS5)
-- ============================================================
-- 1. 文献与引用
-- ============================================================
CREATE TABLE IF NOT EXISTS papers (
bibcode TEXT PRIMARY KEY,
title TEXT NOT NULL,
authors TEXT, -- JSON array of author names
year TEXT,
pub TEXT, -- journal / publisher
keywords TEXT, -- JSON array of keywords
abstract TEXT,
doi TEXT,
arxiv_id TEXT,
citation_count INTEGER DEFAULT 0,
reference_count INTEGER DEFAULT 0,
pdf_path TEXT,
html_path TEXT,
markdown_path TEXT,
translation_path TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
doctype TEXT
);
CREATE INDEX IF NOT EXISTS idx_papers_doi ON papers(doi);
CREATE INDEX IF NOT EXISTS idx_papers_arxiv_id ON papers(arxiv_id);
CREATE TABLE IF NOT EXISTS citations_references (
source_bibcode TEXT NOT NULL,
target_bibcode TEXT NOT NULL,
PRIMARY KEY (source_bibcode, target_bibcode)
);
CREATE INDEX IF NOT EXISTS idx_citations_ref_source ON citations_references(source_bibcode);
CREATE INDEX IF NOT EXISTS idx_citations_ref_target ON citations_references(target_bibcode);
-- ============================================================
-- 2. 笔记与高亮
-- ============================================================
CREATE TABLE IF NOT EXISTS notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
bibcode TEXT NOT NULL,
paragraph_index INTEGER NOT NULL,
note_text TEXT NOT NULL DEFAULT '',
highlight_color TEXT NOT NULL DEFAULT 'yellow',
selected_text TEXT NOT NULL DEFAULT '',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (bibcode) REFERENCES papers(bibcode) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_notes_bibcode ON notes(bibcode);
-- ============================================================
-- 3. 批量同步
-- ============================================================
CREATE TABLE IF NOT EXISTS sync_queries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
query TEXT NOT NULL,
source TEXT NOT NULL,
limit_count INTEGER NOT NULL,
last_run DATETIME DEFAULT CURRENT_TIMESTAMP,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE(query, source, limit_count)
);
-- ============================================================
-- 4. RAG 切片与天体目标
-- ============================================================
CREATE TABLE IF NOT EXISTS paper_chunks_content (
rowid INTEGER PRIMARY KEY AUTOINCREMENT,
bibcode TEXT,
paragraph_index INTEGER,
content TEXT,
headings TEXT DEFAULT 'Document',
section_index INTEGER DEFAULT 0,
FOREIGN KEY(bibcode) REFERENCES papers(bibcode) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS paper_targets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
bibcode TEXT,
target_name TEXT,
ra TEXT,
dec TEXT,
parallax REAL,
spectral_type TEXT,
v_magnitude REAL,
aliases TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
otype TEXT,
oname TEXT,
pm_ra REAL,
pm_de REAL,
radial_velocity REAL,
parallax_err REAL,
photometry TEXT,
FOREIGN KEY(bibcode) REFERENCES papers(bibcode) ON DELETE CASCADE,
UNIQUE(bibcode, target_name)
);
CREATE INDEX IF NOT EXISTS idx_paper_targets_name ON paper_targets(target_name);
-- ============================================================
-- 5. Agent 会话与消息
-- ============================================================
CREATE TABLE IF NOT EXISTS agent_sessions (
session_id TEXT PRIMARY KEY,
title TEXT NOT NULL DEFAULT '',
model TEXT NOT NULL DEFAULT '',
turn_count INTEGER NOT NULL DEFAULT 0,
last_error TEXT,
summary TEXT,
metadata TEXT,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at DATETIME,
rewind_count INTEGER NOT NULL DEFAULT 0,
parent_session_id TEXT REFERENCES agent_sessions(session_id),
branch_metadata TEXT,
mode TEXT NOT NULL DEFAULT 'default'
);
CREATE INDEX IF NOT EXISTS idx_agent_sessions_updated
ON agent_sessions(updated_at) WHERE deleted_at IS NULL;
CREATE TABLE IF NOT EXISTS agent_messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
turn_index INTEGER NOT NULL DEFAULT 0,
step_index INTEGER NOT NULL DEFAULT 0,
role TEXT NOT NULL CHECK(role IN ('system', 'user', 'assistant', 'tool')),
content TEXT NOT NULL DEFAULT '',
thought TEXT,
tool_calls TEXT,
tool_call_id TEXT,
token_count INTEGER NOT NULL DEFAULT 0,
metadata TEXT,
raw_json TEXT,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
agent_name TEXT NOT NULL DEFAULT 'lead',
active INTEGER NOT NULL DEFAULT 1,
FOREIGN KEY (session_id) REFERENCES agent_sessions(session_id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_agent_messages_session ON agent_messages(session_id);
CREATE INDEX IF NOT EXISTS idx_agent_messages_turn ON agent_messages(session_id, turn_index);
CREATE INDEX IF NOT EXISTS idx_agent_messages_agent ON agent_messages(session_id, agent_name);
CREATE INDEX IF NOT EXISTS idx_agent_messages_active ON agent_messages(session_id, active);
-- ============================================================
-- 6. Agent 任务看板
-- ============================================================
CREATE TABLE IF NOT EXISTS agent_tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
task_id TEXT NOT NULL,
content TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'pending'
CHECK(status IN ('pending', 'in_progress', 'completed')),
blocked_by TEXT NOT NULL DEFAULT '[]',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
owner TEXT NOT NULL DEFAULT '',
FOREIGN KEY (session_id) REFERENCES agent_sessions(session_id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_agent_tasks_session ON agent_tasks(session_id);
CREATE INDEX IF NOT EXISTS idx_agent_tasks_status ON agent_tasks(session_id, status);
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_tasks_session_task ON agent_tasks(session_id, task_id);
-- ============================================================
-- 7. Agent 审计日志
-- ============================================================
CREATE TABLE IF NOT EXISTS agent_audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
step INTEGER NOT NULL DEFAULT 0,
tool_name TEXT,
status TEXT NOT NULL CHECK(status IN ('OK', 'FAIL', 'SESSION_STOP')),
elapsed_ms INTEGER NOT NULL DEFAULT 0,
output_preview TEXT,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
agent_name TEXT NOT NULL DEFAULT 'lead',
FOREIGN KEY (session_id) REFERENCES agent_sessions(session_id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_audit_log_session ON agent_audit_log(session_id);
CREATE INDEX IF NOT EXISTS idx_audit_log_created ON agent_audit_log(created_at);
-- ============================================================
-- 8. Agent 团队协作
-- ============================================================
CREATE TABLE IF NOT EXISTS agent_team_members (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
agent_name TEXT NOT NULL,
agent_role TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active'
CHECK(status IN ('spawning', 'active', 'idle', 'shutdown')),
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (session_id) REFERENCES agent_sessions(session_id) ON DELETE CASCADE,
UNIQUE(session_id, agent_name)
);
CREATE INDEX IF NOT EXISTS idx_team_members_session ON agent_team_members(session_id);
CREATE INDEX IF NOT EXISTS idx_team_members_status ON agent_team_members(session_id, status);
-- ============================================================
-- 9. 全文搜索 (FTS5)
-- ============================================================
-- 文献全文搜索
CREATE VIRTUAL TABLE IF NOT EXISTS papers_fts USING fts5(
title,
authors,
keywords,
abstract,
pub,
content='papers',
content_rowid='rowid'
);
CREATE TRIGGER IF NOT EXISTS papers_fts_insert AFTER INSERT ON papers
BEGIN
INSERT INTO papers_fts(rowid, title, authors, keywords, abstract, pub)
VALUES (NEW.rowid, NEW.title, NEW.authors, NEW.keywords, NEW.abstract, NEW.pub);
END;
CREATE TRIGGER IF NOT EXISTS papers_fts_update AFTER UPDATE ON papers
BEGIN
UPDATE papers_fts SET
title = NEW.title,
authors = NEW.authors,
keywords = NEW.keywords,
abstract = NEW.abstract,
pub = NEW.pub
WHERE rowid = OLD.rowid;
END;
CREATE TRIGGER IF NOT EXISTS papers_fts_delete AFTER DELETE ON papers
BEGIN
DELETE FROM papers_fts WHERE rowid = OLD.rowid;
END;
-- Agent 会话全文搜索
CREATE VIRTUAL TABLE IF NOT EXISTS agent_sessions_fts USING fts5(
session_id,
title,
summary,
metadata,
content='agent_sessions',
content_rowid='rowid'
);
CREATE TRIGGER IF NOT EXISTS sessions_fts_insert AFTER INSERT ON agent_sessions
BEGIN
INSERT INTO agent_sessions_fts(rowid, session_id, title, summary, metadata)
VALUES (NEW.rowid, NEW.session_id, NEW.title, NEW.summary, NEW.metadata);
END;
CREATE TRIGGER IF NOT EXISTS sessions_fts_update AFTER UPDATE ON agent_sessions
BEGIN
UPDATE agent_sessions_fts SET
session_id = NEW.session_id,
title = NEW.title,
summary = NEW.summary,
metadata = NEW.metadata
WHERE rowid = OLD.rowid;
END;
CREATE TRIGGER IF NOT EXISTS sessions_fts_delete AFTER DELETE ON agent_sessions
BEGIN
DELETE FROM agent_sessions_fts WHERE rowid = OLD.rowid;
END;
-- Agent 消息全文搜索
CREATE VIRTUAL TABLE IF NOT EXISTS agent_messages_fts USING fts5(
session_id,
role,
content,
thought,
tool_calls,
content='agent_messages',
content_rowid='rowid'
);
CREATE TRIGGER IF NOT EXISTS messages_fts_insert AFTER INSERT ON agent_messages
BEGIN
INSERT INTO agent_messages_fts(rowid, session_id, role, content, thought, tool_calls)
VALUES (NEW.rowid, NEW.session_id, NEW.role, NEW.content, NEW.thought, NEW.tool_calls);
END;
CREATE TRIGGER IF NOT EXISTS messages_fts_update AFTER UPDATE ON agent_messages
BEGIN
UPDATE agent_messages_fts SET
session_id = NEW.session_id,
role = NEW.role,
content = NEW.content,
thought = NEW.thought,
tool_calls = NEW.tool_calls
WHERE rowid = OLD.rowid;
END;
CREATE TRIGGER IF NOT EXISTS messages_fts_delete AFTER DELETE ON agent_messages
BEGIN
DELETE FROM agent_messages_fts WHERE rowid = OLD.rowid;
END;
-- 段落切片全文搜索(配合 vec_paper_chunks 向量检索做混合搜索)
CREATE VIRTUAL TABLE IF NOT EXISTS paper_chunks_fts USING fts5(
content,
content='paper_chunks_content',
content_rowid='rowid'
);
CREATE TRIGGER IF NOT EXISTS chunks_fts_insert AFTER INSERT ON paper_chunks_content
BEGIN
INSERT INTO paper_chunks_fts(rowid, content) VALUES (NEW.rowid, NEW.content);
END;
CREATE TRIGGER IF NOT EXISTS chunks_fts_update AFTER UPDATE ON paper_chunks_content
BEGIN
UPDATE paper_chunks_fts SET content = NEW.content WHERE rowid = OLD.rowid;
END;
CREATE TRIGGER IF NOT EXISTS chunks_fts_delete AFTER DELETE ON paper_chunks_content
BEGIN
DELETE FROM paper_chunks_fts WHERE rowid = OLD.rowid;
END;
-- ============================================================
-- 注意: vec_paper_chunks 向量虚拟表由 main.rs 在运行时动态创建,
-- 维度由 EMBEDDING_DIM 环境变量控制 (默认 1536)。
-- ============================================================