Files
AIclinicalresearch/docs/09-架构实施/migration-scripts/003-migrate-aia.sql
HaHafeng 2e8699c217 feat(asl): Week 2 Day 2 - Excel import with template download and intelligent dedup
Features:
- feat: Excel template generation and download (with examples)
- feat: Excel file parsing in memory (cloud-native, no disk write)
- feat: Field validation (title + abstract required)
- feat: Smart deduplication (DOI priority + Title fallback)
- feat: Literature preview table with statistics
- feat: Complete submission flow (create project + import literatures)

Components:
- feat: Create excelUtils.ts with full Excel processing toolkit
- feat: Enhance TitleScreeningSettings page with upload/preview/submit
- feat: Update API interface signatures and export unified aslApi object

Dependencies:
- chore: Add xlsx library for Excel file processing

Ref: Week 2 Frontend Development - Day 2
Scope: ASL Module MVP - Title Abstract Screening
Cloud-Native: Memory parsing, no file persistence
2025-11-19 10:24:47 +08:00

345 lines
12 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- ========================================
-- 003-migrate-aia.sql
-- ========================================
-- 目的迁移aia_schemaAI智能问答模块
-- 迁移表5个projects, conversations, messages, general_conversations, general_messages
-- 预计时间30分钟
-- 作者AI助手
-- 日期2025-11-09
-- ========================================
-- 前置条件:
-- 1. 已执行 001-create-all-10-schemas.sql
-- 2. 已执行 002-migrate-platform.sql因为需要引用platform_schema.users
-- 3. public schema中的相关表存在且有数据
BEGIN;
-- ========================================
-- 第一步创建aia_schema.projects表
-- ========================================
CREATE TABLE IF NOT EXISTS aia_schema.projects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL,
name VARCHAR(255) NOT NULL,
background TEXT DEFAULT '',
research_type VARCHAR(50) DEFAULT 'observational',
conversation_count INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES platform_schema.users(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_aia_projects_user_id ON aia_schema.projects(user_id);
CREATE INDEX IF NOT EXISTS idx_aia_projects_created_at ON aia_schema.projects(created_at);
CREATE INDEX IF NOT EXISTS idx_aia_projects_deleted_at ON aia_schema.projects(deleted_at);
-- ========================================
-- 第二步创建aia_schema.conversations表
-- ========================================
CREATE TABLE IF NOT EXISTS aia_schema.conversations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL,
project_id UUID,
agent_id VARCHAR(100) NOT NULL,
title VARCHAR(255) NOT NULL,
model_name VARCHAR(50) DEFAULT 'deepseek-v3',
message_count INT DEFAULT 0,
total_tokens INT DEFAULT 0,
metadata JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES platform_schema.users(id) ON DELETE CASCADE,
FOREIGN KEY (project_id) REFERENCES aia_schema.projects(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_aia_conversations_user_id ON aia_schema.conversations(user_id);
CREATE INDEX IF NOT EXISTS idx_aia_conversations_project_id ON aia_schema.conversations(project_id);
CREATE INDEX IF NOT EXISTS idx_aia_conversations_agent_id ON aia_schema.conversations(agent_id);
CREATE INDEX IF NOT EXISTS idx_aia_conversations_created_at ON aia_schema.conversations(created_at);
CREATE INDEX IF NOT EXISTS idx_aia_conversations_deleted_at ON aia_schema.conversations(deleted_at);
-- ========================================
-- 第三步创建aia_schema.messages表
-- ========================================
CREATE TABLE IF NOT EXISTS aia_schema.messages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
conversation_id UUID NOT NULL,
role VARCHAR(20) NOT NULL,
content TEXT NOT NULL,
model VARCHAR(50),
metadata JSONB,
tokens INT,
is_pinned BOOLEAN DEFAULT false,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (conversation_id) REFERENCES aia_schema.conversations(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_aia_messages_conversation_id ON aia_schema.messages(conversation_id);
CREATE INDEX IF NOT EXISTS idx_aia_messages_created_at ON aia_schema.messages(created_at);
CREATE INDEX IF NOT EXISTS idx_aia_messages_is_pinned ON aia_schema.messages(is_pinned);
-- ========================================
-- 第四步创建aia_schema.general_conversations表
-- ========================================
CREATE TABLE IF NOT EXISTS aia_schema.general_conversations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL,
title VARCHAR(255) NOT NULL,
model_name VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES platform_schema.users(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_aia_general_conversations_user_id ON aia_schema.general_conversations(user_id);
CREATE INDEX IF NOT EXISTS idx_aia_general_conversations_created_at ON aia_schema.general_conversations(created_at);
CREATE INDEX IF NOT EXISTS idx_aia_general_conversations_updated_at ON aia_schema.general_conversations(updated_at);
-- ========================================
-- 第五步创建aia_schema.general_messages表
-- ========================================
CREATE TABLE IF NOT EXISTS aia_schema.general_messages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
conversation_id UUID NOT NULL,
role VARCHAR(20) NOT NULL,
content TEXT NOT NULL,
model VARCHAR(50),
metadata JSONB,
tokens INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (conversation_id) REFERENCES aia_schema.general_conversations(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_aia_general_messages_conversation_id ON aia_schema.general_messages(conversation_id);
CREATE INDEX IF NOT EXISTS idx_aia_general_messages_created_at ON aia_schema.general_messages(created_at);
-- ========================================
-- 第六步:迁移数据
-- ========================================
-- 6.1 迁移projects
INSERT INTO aia_schema.projects (
id, user_id, name, background, research_type,
conversation_count, created_at, updated_at, deleted_at
)
SELECT
id, user_id, name, background, research_type,
conversation_count, created_at, updated_at, deleted_at
FROM public.projects
ON CONFLICT (id) DO NOTHING;
-- 6.2 迁移conversations
INSERT INTO aia_schema.conversations (
id, user_id, project_id, agent_id, title,
model_name, message_count, total_tokens, metadata,
created_at, updated_at, deleted_at
)
SELECT
id, user_id, project_id, agent_id, title,
model_name, message_count, total_tokens, metadata,
created_at, updated_at, deleted_at
FROM public.conversations
ON CONFLICT (id) DO NOTHING;
-- 6.3 迁移messages
INSERT INTO aia_schema.messages (
id, conversation_id, role, content,
model, metadata, tokens, is_pinned, created_at
)
SELECT
id, conversation_id, role, content,
model, metadata, tokens, is_pinned, created_at
FROM public.messages
ON CONFLICT (id) DO NOTHING;
-- 6.4 迁移general_conversations
INSERT INTO aia_schema.general_conversations (
id, user_id, title, model_name,
created_at, updated_at, deleted_at
)
SELECT
id, user_id, title, model_name,
created_at, updated_at, deleted_at
FROM public.general_conversations
ON CONFLICT (id) DO NOTHING;
-- 6.5 迁移general_messages
INSERT INTO aia_schema.general_messages (
id, conversation_id, role, content,
model, metadata, tokens, created_at
)
SELECT
id, conversation_id, role, content,
model, metadata, tokens, created_at
FROM public.general_messages
ON CONFLICT (id) DO NOTHING;
-- ========================================
-- 第七步:数据验证
-- ========================================
DO $$
DECLARE
public_projects INTEGER;
public_conversations INTEGER;
public_messages INTEGER;
public_general_conversations INTEGER;
public_general_messages INTEGER;
aia_projects INTEGER;
aia_conversations INTEGER;
aia_messages INTEGER;
aia_general_conversations INTEGER;
aia_general_messages INTEGER;
all_match BOOLEAN := true;
BEGIN
-- 统计原表
SELECT COUNT(*) INTO public_projects FROM public.projects;
SELECT COUNT(*) INTO public_conversations FROM public.conversations;
SELECT COUNT(*) INTO public_messages FROM public.messages;
SELECT COUNT(*) INTO public_general_conversations FROM public.general_conversations;
SELECT COUNT(*) INTO public_general_messages FROM public.general_messages;
-- 统计新表
SELECT COUNT(*) INTO aia_projects FROM aia_schema.projects;
SELECT COUNT(*) INTO aia_conversations FROM aia_schema.conversations;
SELECT COUNT(*) INTO aia_messages FROM aia_schema.messages;
SELECT COUNT(*) INTO aia_general_conversations FROM aia_schema.general_conversations;
SELECT COUNT(*) INTO aia_general_messages FROM aia_schema.general_messages;
-- 输出统计
RAISE NOTICE '==================== 数据迁移统计 ====================';
RAISE NOTICE 'projects: public.% -> aia_schema.%', public_projects, aia_projects;
RAISE NOTICE 'conversations: public.% -> aia_schema.%', public_conversations, aia_conversations;
RAISE NOTICE 'messages: public.% -> aia_schema.%', public_messages, aia_messages;
RAISE NOTICE 'general_conversations: public.% -> aia_schema.%', public_general_conversations, aia_general_conversations;
RAISE NOTICE 'general_messages: public.% -> aia_schema.%', public_general_messages, aia_general_messages;
RAISE NOTICE '=====================================================';
-- 验证每个表
IF public_projects != aia_projects THEN
RAISE WARNING '⚠️ projects 数据量不一致';
all_match := false;
END IF;
IF public_conversations != aia_conversations THEN
RAISE WARNING '⚠️ conversations 数据量不一致';
all_match := false;
END IF;
IF public_messages != aia_messages THEN
RAISE WARNING '⚠️ messages 数据量不一致';
all_match := false;
END IF;
IF public_general_conversations != aia_general_conversations THEN
RAISE WARNING '⚠️ general_conversations 数据量不一致';
all_match := false;
END IF;
IF public_general_messages != aia_general_messages THEN
RAISE WARNING '⚠️ general_messages 数据量不一致';
all_match := false;
END IF;
IF all_match THEN
RAISE NOTICE '✅ AIA Schema 所有表数据迁移成功!';
ELSE
RAISE WARNING '⚠️ 部分表数据迁移存在问题,请检查';
END IF;
END $$;
-- ========================================
-- 第八步:外键完整性验证
-- ========================================
-- 验证所有project的user_id都存在于platform_schema.users中
DO $$
DECLARE
invalid_users INTEGER;
BEGIN
SELECT COUNT(*) INTO invalid_users
FROM aia_schema.projects p
LEFT JOIN platform_schema.users u ON p.user_id = u.id
WHERE u.id IS NULL;
IF invalid_users > 0 THEN
RAISE WARNING '⚠️ projects表中有 % 条记录的user_id无效', invalid_users;
ELSE
RAISE NOTICE '✅ projects表外键完整性验证通过';
END IF;
END $$;
COMMIT;
-- ========================================
-- 执行结果统计(可单独运行)
-- ========================================
SELECT
'aia_schema' AS schema_name,
'projects' AS table_name,
COUNT(*) AS row_count,
COUNT(DISTINCT user_id) AS unique_users,
MIN(created_at) AS earliest_record,
MAX(created_at) AS latest_record
FROM aia_schema.projects
UNION ALL
SELECT
'aia_schema',
'conversations',
COUNT(*),
COUNT(DISTINCT user_id),
MIN(created_at),
MAX(created_at)
FROM aia_schema.conversations
UNION ALL
SELECT
'aia_schema',
'messages',
COUNT(*),
NULL,
MIN(created_at),
MAX(created_at)
FROM aia_schema.messages;
-- ========================================
-- 完成提示
-- ========================================
-- ✅ AIA Schema 迁移完成
-- 包含5个表projects, conversations, messages,
-- general_conversations, general_messages
-- 下一步:执行 004-migrate-pkb.sql
-- ========================================