refactor(asl): ASL frontend architecture refactoring with left navigation

- feat: Create ASLLayout component with 7-module left navigation
- feat: Implement Title Screening Settings page with optimized PICOS layout
- feat: Add placeholder pages for Workbench and Results
- fix: Fix nested routing structure for React Router v6
- fix: Resolve Spin component warning in MainLayout
- fix: Add QueryClientProvider to App.tsx
- style: Optimize PICOS form layout (P+I left, C+O+S right)
- style: Align Inclusion/Exclusion criteria side-by-side
- docs: Add architecture refactoring and routing fix reports

Ref: Week 2 Frontend Development
Scope: ASL module MVP - Title Abstract Screening
This commit is contained in:
2025-11-18 21:51:51 +08:00
parent e3e7e028e8
commit 3634933ece
213 changed files with 20054 additions and 442 deletions

View File

@@ -0,0 +1,205 @@
/**
* 手动创建ASL模块的4张表
* 避免影响现有表
*/
import { prisma } from '../src/config/database.js';
async function createAslTables() {
try {
console.log('🔍 开始创建ASL模块表...\n');
// 1. 创建筛选项目表
await prisma.$executeRawUnsafe(`
CREATE TABLE IF NOT EXISTS asl_schema.screening_projects (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
project_name TEXT NOT NULL,
pico_criteria JSONB NOT NULL,
inclusion_criteria TEXT NOT NULL,
exclusion_criteria TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'draft',
screening_config JSONB,
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES platform_schema.users(id) ON DELETE CASCADE
);
`);
console.log('✅ 创建 asl_schema.screening_projects');
// 创建索引
await prisma.$executeRawUnsafe(`
CREATE INDEX IF NOT EXISTS idx_screening_projects_user_id ON asl_schema.screening_projects(user_id);
`);
await prisma.$executeRawUnsafe(`
CREATE INDEX IF NOT EXISTS idx_screening_projects_status ON asl_schema.screening_projects(status);
`);
// 2. 创建文献条目表
await prisma.$executeRawUnsafe(`
CREATE TABLE IF NOT EXISTS asl_schema.literatures (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL,
pmid TEXT,
title TEXT NOT NULL,
abstract TEXT NOT NULL,
authors TEXT,
journal TEXT,
publication_year INTEGER,
doi TEXT,
pdf_url TEXT,
pdf_oss_key TEXT,
pdf_file_size INTEGER,
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_project FOREIGN KEY (project_id) REFERENCES asl_schema.screening_projects(id) ON DELETE CASCADE,
CONSTRAINT unique_project_pmid UNIQUE (project_id, pmid)
);
`);
console.log('✅ 创建 asl_schema.literatures');
// 创建索引
await prisma.$executeRawUnsafe(`
CREATE INDEX IF NOT EXISTS idx_literatures_project_id ON asl_schema.literatures(project_id);
`);
await prisma.$executeRawUnsafe(`
CREATE INDEX IF NOT EXISTS idx_literatures_doi ON asl_schema.literatures(doi);
`);
// 3. 创建筛选结果表
await prisma.$executeRawUnsafe(`
CREATE TABLE IF NOT EXISTS asl_schema.screening_results (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL,
literature_id TEXT NOT NULL,
-- DeepSeek判断
ds_model_name TEXT NOT NULL,
ds_p_judgment TEXT,
ds_i_judgment TEXT,
ds_c_judgment TEXT,
ds_s_judgment TEXT,
ds_conclusion TEXT,
ds_confidence DOUBLE PRECISION,
ds_p_evidence TEXT,
ds_i_evidence TEXT,
ds_c_evidence TEXT,
ds_s_evidence TEXT,
ds_reason TEXT,
-- Qwen判断
qwen_model_name TEXT NOT NULL,
qwen_p_judgment TEXT,
qwen_i_judgment TEXT,
qwen_c_judgment TEXT,
qwen_s_judgment TEXT,
qwen_conclusion TEXT,
qwen_confidence DOUBLE PRECISION,
qwen_p_evidence TEXT,
qwen_i_evidence TEXT,
qwen_c_evidence TEXT,
qwen_s_evidence TEXT,
qwen_reason TEXT,
-- 冲突状态
conflict_status TEXT NOT NULL DEFAULT 'none',
conflict_fields JSONB,
-- 最终决策
final_decision TEXT,
final_decision_by TEXT,
final_decision_at TIMESTAMP(3),
exclusion_reason TEXT,
-- AI处理状态
ai_processing_status TEXT NOT NULL DEFAULT 'pending',
ai_processed_at TIMESTAMP(3),
ai_error_message TEXT,
-- 可追溯信息
prompt_version TEXT NOT NULL DEFAULT 'v1.0.0',
raw_output JSONB,
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_project_result FOREIGN KEY (project_id) REFERENCES asl_schema.screening_projects(id) ON DELETE CASCADE,
CONSTRAINT fk_literature FOREIGN KEY (literature_id) REFERENCES asl_schema.literatures(id) ON DELETE CASCADE,
CONSTRAINT unique_project_literature UNIQUE (project_id, literature_id)
);
`);
console.log('✅ 创建 asl_schema.screening_results');
// 创建索引
await prisma.$executeRawUnsafe(`
CREATE INDEX IF NOT EXISTS idx_screening_results_project_id ON asl_schema.screening_results(project_id);
`);
await prisma.$executeRawUnsafe(`
CREATE INDEX IF NOT EXISTS idx_screening_results_literature_id ON asl_schema.screening_results(literature_id);
`);
await prisma.$executeRawUnsafe(`
CREATE INDEX IF NOT EXISTS idx_screening_results_conflict_status ON asl_schema.screening_results(conflict_status);
`);
await prisma.$executeRawUnsafe(`
CREATE INDEX IF NOT EXISTS idx_screening_results_final_decision ON asl_schema.screening_results(final_decision);
`);
// 4. 创建筛选任务表
await prisma.$executeRawUnsafe(`
CREATE TABLE IF NOT EXISTS asl_schema.screening_tasks (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL,
task_type TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
total_items INTEGER NOT NULL,
processed_items INTEGER NOT NULL DEFAULT 0,
success_items INTEGER NOT NULL DEFAULT 0,
failed_items INTEGER NOT NULL DEFAULT 0,
conflict_items INTEGER NOT NULL DEFAULT 0,
started_at TIMESTAMP(3),
completed_at TIMESTAMP(3),
estimated_end_at TIMESTAMP(3),
error_message TEXT,
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_project_task FOREIGN KEY (project_id) REFERENCES asl_schema.screening_projects(id) ON DELETE CASCADE
);
`);
console.log('✅ 创建 asl_schema.screening_tasks');
// 创建索引
await prisma.$executeRawUnsafe(`
CREATE INDEX IF NOT EXISTS idx_screening_tasks_project_id ON asl_schema.screening_tasks(project_id);
`);
await prisma.$executeRawUnsafe(`
CREATE INDEX IF NOT EXISTS idx_screening_tasks_status ON asl_schema.screening_tasks(status);
`);
console.log('\n✅ ASL模块4张表创建完成');
console.log('📊 表列表:');
console.log(' - asl_schema.screening_projects (筛选项目)');
console.log(' - asl_schema.literatures (文献条目)');
console.log(' - asl_schema.screening_results (筛选结果)');
console.log(' - asl_schema.screening_tasks (筛选任务)');
// 验证表
const tables = await prisma.$queryRawUnsafe<any[]>(`
SELECT tablename
FROM pg_tables
WHERE schemaname = 'asl_schema'
ORDER BY tablename;
`);
console.log('\n🔍 数据库验证:');
tables.forEach(t => console.log(`${t.tablename}`));
} catch (error) {
console.error('❌ 创建表失败:', error);
throw error;
} finally {
await prisma.$disconnect();
}
}
createAslTables();