- 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
206 lines
7.2 KiB
TypeScript
206 lines
7.2 KiB
TypeScript
/**
|
||
* 手动创建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();
|
||
|