Features - User Management (Phase 4.1): - Database: Add user_modules table for fine-grained module permissions - Database: Add 4 user permissions (view/create/edit/delete) to role_permissions - Backend: UserService (780 lines) - CRUD with tenant isolation - Backend: UserController + UserRoutes (648 lines) - 13 API endpoints - Backend: Batch import users from Excel - Frontend: UserListPage (412 lines) - list/filter/search/pagination - Frontend: UserFormPage (341 lines) - create/edit with module config - Frontend: UserDetailPage (393 lines) - details/tenant/module management - Frontend: 3 modal components (592 lines) - import/assign/configure - API: GET/POST/PUT/DELETE /api/admin/users/* endpoints Architecture Upgrade - Module Permission System: - Backend: Add getUserModules() method in auth.service - Backend: Login API returns modules array in user object - Frontend: AuthContext adds hasModule() method - Frontend: Navigation filters modules based on user.modules - Frontend: RouteGuard checks requiredModule instead of requiredVersion - Frontend: Remove deprecated version-based permission system - UX: Only show accessible modules in navigation (clean UI) - UX: Smart redirect after login (avoid 403 for regular users) Fixes: - Fix UTF-8 encoding corruption in ~100 docs files - Fix pageSize type conversion in userService (String to Number) - Fix authUser undefined error in TopNavigation - Fix login redirect logic with role-based access check - Update Git commit guidelines v1.2 with UTF-8 safety rules Database Changes: - CREATE TABLE user_modules (user_id, tenant_id, module_code, is_enabled) - ADD UNIQUE CONSTRAINT (user_id, tenant_id, module_code) - INSERT 4 permissions + role assignments - UPDATE PUBLIC tenant with 8 module subscriptions Technical: - Backend: 5 new files (~2400 lines) - Frontend: 10 new files (~2500 lines) - Docs: 1 development record + 2 status updates + 1 guideline update - Total: ~4900 lines of code Status: User management 100% complete, module permission system operational
26 KiB
26 KiB
RVW稿件审查模块迁移计划(v2.1 - 稳定迁移版)
文档版本: v2.1
创建日期: 2026-01-07
最后更新: 2026-01-07
维护者: 开发团队
文档目的: 将稿件审查功能从旧架构安全迁移到新架构,同时整合MVP核心需求
📋 项目概述
1. 背景
稿件审查功能是2025-10-30(Day 30)独立开发的功能模块,目前位于 backend/src/legacy/ 和 frontend/ 目录中。现需要:
- 架构迁移:迁移到标准的模块目录结构(
modules/rvw) - 功能升级:整合《智能期刊审稿系统MVP产品需求文档》的核心功能
2. 迁移原则
| 原则 | 说明 |
|---|---|
| 稳定优先 | 每个Phase完成后必须通过测试验证 |
| 安全可靠 | 数据库迁移前必须备份,支持回滚 |
| 阶段验证 | 每个Phase有明确的验收标准 |
| 向后兼容 | 保留旧API过渡期,不影响现有功能 |
| 渐进式 | 先核心后扩展,先后端后前端 |
3. 功能范围
| 功能 | 本次开发 | 数据库支撑 | 说明 |
|---|---|---|---|
| 核心AI评估 | ✅ | ✅ | 稿约规范性+方法学 |
| 批量上传 | ✅ | ✅ | 多文件上传 |
| 审稿工作台 | ✅ | ✅ | 宽表布局+筛选 |
| 智能体选择 | ✅ | ✅ | 可选1个或2个 |
| 批量操作 | ✅ | ✅ | 批量运行审查 |
| 状态筛选 | ✅ | ✅ | 全部/待处理/已完成 |
| PDF报告导出 | ✅ | ✅ | 优化现有功能 |
| PICO卡片 | ⏸️ | ✅ | 暂不开发,数据库预留 |
| 系统设置 | ⏸️ | ✅ | 暂不开发,数据库预留 |
| 历史归档 | ⏸️ | ✅ | 暂不开发,数据库预留 |
| 登录页面 | ⏸️ | - | 暂不开发 |
4. 智能体选择说明
用户可以灵活选择运行的智能体:
选项 A: 只选择「稿约规范性智能体」 → 只运行规范性评估
选项 B: 只选择「方法学统计智能体」 → 只运行方法学评估
选项 C: 同时选择两个智能体 → 同时运行两项评估(默认)
📊 数据库设计(完整版,支撑未来扩展)
1. 期刊配置表(预留,暂不使用)
// review_schema
model JournalConfig {
id String @id @default(uuid())
name String // 期刊名称
logoUrl String? @map("logo_url") // Logo URL(预留)
defaultModel String @default("deepseek-v3") @map("default_model")
settings Json? // 其他配置(预留)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
reviewTasks ReviewTask[]
@@map("journal_configs")
@@schema("review_schema")
}
2. 审稿任务表(扩展版)
model ReviewTask {
id String @id @default(uuid())
userId String @map("user_id")
journalId String? @map("journal_id") // 预留:期刊关联
// 文件信息
fileName String @map("file_name")
fileSize Int @map("file_size")
filePath String? @map("file_path")
extractedText String @map("extracted_text")
wordCount Int? @map("word_count")
authorName String? @map("author_name") // 预留:作者提取
// 状态管理
status String @default("pending")
// ✅ 智能体选择:可选1个或2个
selectedAgents String[] @default(["editorial", "methodology"]) @map("selected_agents")
// 评估结果
editorialReview Json? @map("editorial_review")
methodologyReview Json? @map("methodology_review")
overallScore Float? @map("overall_score")
// 结果摘要(用于列表展示)
editorialScore Float? @map("editorial_score")
methodologyStatus String? @map("methodology_status") // pass/warn/fail
// 预留:PICO提取(暂不使用)
picoExtract Json? @map("pico_extract")
// 元数据
modelUsed String? @map("model_used")
startedAt DateTime? @map("started_at")
completedAt DateTime? @map("completed_at")
durationSeconds Int? @map("duration_seconds")
errorMessage String? @map("error_message")
// 预留:归档功能(暂不使用)
isArchived Boolean @default(false) @map("is_archived")
archivedAt DateTime? @map("archived_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// 关联
user users @relation(fields: [userId], references: [id], onDelete: Cascade)
journal JournalConfig? @relation(fields: [journalId], references: [id])
@@index([userId])
@@index([journalId])
@@index([status])
@@index([createdAt])
@@index([isArchived])
@@map("review_tasks")
@@schema("review_schema")
}
3. 字段说明
| 字段 | 本次使用 | 预留用途 |
|---|---|---|
journalId |
❌ | 系统设置:期刊关联 |
authorName |
❌ | 自动提取作者名 |
selectedAgents |
✅ | 用户选择的智能体 |
editorialScore |
✅ | 列表显示规范性分数 |
methodologyStatus |
✅ | 列表显示方法学状态 |
picoExtract |
❌ | PICO卡片数据 |
isArchived |
❌ | 历史归档功能 |
archivedAt |
❌ | 归档时间 |
📋 安全迁移策略
1. 迁移前准备
# 1. 备份数据库
pg_dump -h localhost -U postgres -d airesearch -F c -f backup_before_rvw_migration.dump
# 2. 记录当前数据量
SELECT COUNT(*) FROM public.review_tasks;
# 3. 导出关键数据(可选)
COPY public.review_tasks TO '/tmp/review_tasks_backup.csv' WITH CSV HEADER;
2. 回滚方案
# 如果迁移失败,回滚数据库
pg_restore -h localhost -U postgres -d airesearch -c backup_before_rvw_migration.dump
# 如果只需要回滚Schema迁移
ALTER TABLE review_schema.review_tasks SET SCHEMA public;
DROP SCHEMA review_schema;
3. 阶段性验证
每个Phase完成后必须通过以下验证:
| Phase | 验证内容 | 验收标准 |
|---|---|---|
| Phase 1 | 后端API测试 | 所有API返回正确,日志无ERROR |
| Phase 2 | 数据库迁移 | 数据完整,查询正常,索引有效 |
| Phase 3 | 前端功能测试 | 核心流程通顺,无白屏/报错 |
| Phase 4 | 集成测试 | 端到端流程正常 |
| Phase 5 | 验收测试 | 符合MVP验收标准 |
📋 开发任务清单
Phase 1:后端模块迁移(2天)
Day 1 上午:创建模块结构 + 复用核心代码
1.1 创建目录结构
backend/src/modules/rvw/
├── routes/
│ └── index.ts # 路由定义
├── controllers/
│ └── reviewController.ts # 控制器
├── services/
│ ├── reviewService.ts # 主服务(复用+扩展)
│ ├── editorialService.ts # 稿约评估(复用)
│ └── methodologyService.ts # 方法学评估(复用)
├── types/
│ └── index.ts # 类型定义
├── prompts/
│ ├── editorial_system.txt # 稿约Prompt
│ └── methodology_system.txt # 方法学Prompt
└── index.ts # 模块入口
- 1.1.1 创建目录结构
- 1.1.2 复制
reviewEditorialStandards()→editorialService.ts - 1.1.3 复制
reviewMethodology()→methodologyService.ts - 1.1.4 复制
parseJSONFromLLMResponse()→utils.ts - 1.1.5 复制 Prompt 文件到模块内
1.2 云原生改造
- 1.2.1 替换
console.log→logger - 1.2.2 移除 Mock用户ID,集成JWT认证
- 1.2.3 使用
process.env配置
Day 1 下午:智能体选择逻辑
1.3 智能体选择实现
// types/index.ts
export type AgentType = 'editorial' | 'methodology';
export interface RunReviewParams {
taskId: string;
agents: AgentType[]; // 可选1个或2个
}
// services/reviewService.ts
async function runReview(params: RunReviewParams) {
const { taskId, agents } = params;
// 验证:至少选择1个智能体
if (agents.length === 0) {
throw new Error('请至少选择一个智能体');
}
// 更新任务状态
await prisma.reviewTask.update({
where: { id: taskId },
data: {
status: 'reviewing',
selectedAgents: agents,
startedAt: new Date()
}
});
// 只运行选中的智能体
let editorialResult = null;
let methodologyResult = null;
if (agents.includes('editorial')) {
editorialResult = await editorialService.review(taskId);
}
if (agents.includes('methodology')) {
methodologyResult = await methodologyService.review(taskId);
}
// 计算综合分数
const overallScore = calculateOverallScore(editorialResult, methodologyResult, agents);
// 更新结果
await prisma.reviewTask.update({
where: { id: taskId },
data: {
status: 'completed',
editorialReview: editorialResult,
methodologyReview: methodologyResult,
editorialScore: editorialResult?.overall_score,
methodologyStatus: getMethodologyStatus(methodologyResult),
overallScore,
completedAt: new Date(),
durationSeconds: calculateDuration(taskId)
}
});
}
// 根据选择的智能体计算综合分数
function calculateOverallScore(editorial: any, methodology: any, agents: AgentType[]) {
if (agents.length === 2 && editorial && methodology) {
// 两个都选:40% + 60%
return editorial.overall_score * 0.4 + methodology.overall_score * 0.6;
} else if (agents.includes('editorial') && editorial) {
// 只选规范性
return editorial.overall_score;
} else if (agents.includes('methodology') && methodology) {
// 只选方法学
return methodology.overall_score;
}
return null;
}
- 1.3.1 实现智能体选择类型定义
- 1.3.2 实现
runReview()函数 - 1.3.3 实现综合分数计算逻辑
- 1.3.4 实现方法学状态判断(pass/warn/fail)
Day 2 上午:批量操作 + API扩展
1.4 批量运行实现
// services/reviewService.ts
async function batchRunReview(params: BatchRunParams) {
const { taskIds, agents } = params;
// 限制并发数
const MAX_CONCURRENT = 5;
const results = [];
for (let i = 0; i < taskIds.length; i += MAX_CONCURRENT) {
const batch = taskIds.slice(i, i + MAX_CONCURRENT);
const batchResults = await Promise.allSettled(
batch.map(taskId => runReview({ taskId, agents }))
);
results.push(...batchResults);
}
return results;
}
- 1.4.1 实现批量运行接口
- 1.4.2 实现并发控制(最多5个)
- 1.4.3 实现错误处理(单个失败不影响其他)
1.5 API路由定义
// routes/index.ts
export default async function rvwRoutes(fastify: FastifyInstance) {
// 任务管理
fastify.post('/tasks', reviewController.createTask); // 创建/上传
fastify.get('/tasks', reviewController.getTaskList); // 列表(筛选)
fastify.get('/tasks/:taskId', reviewController.getTaskDetail); // 详情
fastify.get('/tasks/:taskId/report', reviewController.getTaskReport); // 报告
fastify.delete('/tasks/:taskId', reviewController.deleteTask); // 删除
// 运行审查(核心功能)
fastify.post('/tasks/:taskId/run', reviewController.runReview); // 单个运行
fastify.post('/tasks/batch/run', reviewController.batchRunReview); // 批量运行
}
- 1.5.1 实现路由定义
- 1.5.2 实现控制器方法
- 1.5.3 添加请求验证
Day 2 下午:注册路由 + Phase 1 验证
1.6 注册新路由
// backend/src/index.ts
import rvwRoutes from './modules/rvw/routes/index.js';
// 注册新路由(v2)
await fastify.register(rvwRoutes, { prefix: '/api/v2/rvw' });
logger.info('✅ RVW稿件审查路由已注册: /api/v2/rvw');
// 保留旧路由(兼容)
await fastify.register(reviewRoutes, { prefix: '/api/v1' });
logger.info('✅ Legacy审稿路由保留: /api/v1/review');
- 1.6.1 注册新路由
- 1.6.2 保留旧路由兼容
1.7 Phase 1 验证测试
### 1. 创建任务
POST {{baseUrl}}/api/v2/rvw/tasks
Content-Type: multipart/form-data
# file: test.docx
### 2. 运行审查(只选规范性)
POST {{baseUrl}}/api/v2/rvw/tasks/{{taskId}}/run
Content-Type: application/json
{ "agents": ["editorial"] }
### 3. 运行审查(只选方法学)
POST {{baseUrl}}/api/v2/rvw/tasks/{{taskId}}/run
Content-Type: application/json
{ "agents": ["methodology"] }
### 4. 运行审查(两个都选)
POST {{baseUrl}}/api/v2/rvw/tasks/{{taskId}}/run
Content-Type: application/json
{ "agents": ["editorial", "methodology"] }
### 5. 批量运行
POST {{baseUrl}}/api/v2/rvw/tasks/batch/run
Content-Type: application/json
{
"taskIds": ["id1", "id2", "id3"],
"agents": ["editorial", "methodology"]
}
### 6. 获取任务列表
GET {{baseUrl}}/api/v2/rvw/tasks?status=pending&limit=10
### 7. 获取报告
GET {{baseUrl}}/api/v2/rvw/tasks/{{taskId}}/report
Phase 1 验收标准:
| 测试项 | 预期结果 | 通过 |
|---|---|---|
| 创建任务 | 返回taskId,状态pending | ⬜ |
| 只选规范性 | 只有editorialReview有值 | ⬜ |
| 只选方法学 | 只有methodologyReview有值 | ⬜ |
| 两个都选 | 两个Review都有值 | ⬜ |
| 批量运行 | 多个任务都完成 | ⬜ |
| 旧API兼容 | /api/v1/review/* 正常 |
⬜ |
- 1.7.1 编写测试用例
- 1.7.2 执行测试
- 1.7.3 修复问题
- 1.7.4 确认Phase 1通过
Phase 2:数据库Schema迁移(0.5天)
迁移前:备份
-
2.1 备份数据库
pg_dump -h localhost -U postgres -d airesearch -F c -f backup_phase2.dump -
2.2 记录当前数据
SELECT COUNT(*) FROM public.review_tasks;
迁移执行
-
2.3 创建迁移SQL
-- 1. 创建新Schema CREATE SCHEMA IF NOT EXISTS review_schema; -- 2. 创建期刊配置表(预留) CREATE TABLE review_schema.journal_configs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(255) NOT NULL, logo_url TEXT, default_model VARCHAR(50) DEFAULT 'deepseek-v3', settings JSONB, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- 3. 迁移review_tasks表到新Schema ALTER TABLE public.review_tasks SET SCHEMA review_schema; -- 4. 添加新字段(支持未来扩展) ALTER TABLE review_schema.review_tasks ADD COLUMN IF NOT EXISTS journal_id UUID REFERENCES review_schema.journal_configs(id), ADD COLUMN IF NOT EXISTS author_name VARCHAR(255), ADD COLUMN IF NOT EXISTS selected_agents TEXT[] DEFAULT ARRAY['editorial', 'methodology'], ADD COLUMN IF NOT EXISTS editorial_score FLOAT, ADD COLUMN IF NOT EXISTS methodology_status VARCHAR(20), ADD COLUMN IF NOT EXISTS pico_extract JSONB, ADD COLUMN IF NOT EXISTS is_archived BOOLEAN DEFAULT FALSE, ADD COLUMN IF NOT EXISTS archived_at TIMESTAMPTZ; -- 5. 创建索引 CREATE INDEX IF NOT EXISTS idx_review_tasks_journal ON review_schema.review_tasks(journal_id); CREATE INDEX IF NOT EXISTS idx_review_tasks_archived ON review_schema.review_tasks(is_archived); -- 6. 创建默认期刊配置 INSERT INTO review_schema.journal_configs (name, default_model) VALUES ('默认期刊', 'deepseek-v3'); -
2.4 更新Prisma Schema
- 修改
@@schema("review_schema") - 添加新字段
- 修改
-
2.5 执行迁移
npx prisma migrate dev --name rvw_schema_migration
Phase 2 验证
- 2.6 验证数据完整性
-- 确认数据量一致 SELECT COUNT(*) FROM review_schema.review_tasks; -- 确认字段可用 SELECT id, selected_agents, editorial_score FROM review_schema.review_tasks LIMIT 5; -- 确认外键关系 SELECT rt.id, rt.user_id, u.email FROM review_schema.review_tasks rt JOIN public.users u ON rt.user_id = u.id LIMIT 5;
Phase 2 验收标准:
| 测试项 | 预期结果 | 通过 |
|---|---|---|
| 数据迁移 | 数据量一致 | ⬜ |
| 新字段 | 字段存在且可用 | ⬜ |
| 外键关系 | 关联正常 | ⬜ |
| 索引有效 | 查询性能正常 | ⬜ |
| API正常 | 后端API仍可用 | ⬜ |
- 2.7 确认Phase 2通过
Phase 3:前端重构(3天)
Day 3:模块结构 + API层
3.1 创建模块结构
frontend-v2/src/modules/rvw/
├── api/
│ └── reviewApi.ts # API封装
├── components/
│ ├── ScoreCard.tsx # 复用
│ ├── EditorialReview.tsx # 复用
│ ├── MethodologyReview.tsx # 复用
│ ├── AgentSelector.tsx # 🆕 智能体选择弹窗
│ ├── BatchToolbar.tsx # 🆕 批量操作栏
│ ├── TaskTable.tsx # 🆕 任务列表表格
│ └── StatusFilter.tsx # 🆕 状态筛选
├── pages/
│ ├── ReviewDashboard.tsx # 🆕 审稿工作台
│ └── ReviewDetail.tsx # 报告详情(优化)
├── hooks/
│ ├── useReviewTask.ts
│ └── useBatchOperation.ts
├── stores/
│ └── useReviewStore.ts
├── types/
│ └── index.ts
└── index.tsx
- 3.1.1 创建目录结构
- 3.1.2 复用现有组件(ScoreCard、EditorialReview、MethodologyReview)
3.2 API封装
// api/reviewApi.ts
export type AgentType = 'editorial' | 'methodology';
// 运行审查(支持选择智能体)
export async function runReview(taskId: string, agents: AgentType[]): Promise<void> {
return axios.post(`/api/v2/rvw/tasks/${taskId}/run`, { agents });
}
// 批量运行
export async function batchRunReview(taskIds: string[], agents: AgentType[]): Promise<void> {
return axios.post('/api/v2/rvw/tasks/batch/run', { taskIds, agents });
}
// 获取任务列表(带筛选)
export interface TaskListParams {
status?: 'all' | 'pending' | 'completed';
limit?: number;
offset?: number;
}
export async function getTaskList(params: TaskListParams): Promise<TaskListResponse> {
return axios.get('/api/v2/rvw/tasks', { params });
}
- 3.2.1 实现API封装
- 3.2.2 实现类型定义
Day 4:核心页面开发
3.3 智能体选择弹窗
// components/AgentSelector.tsx
interface AgentSelectorProps {
visible: boolean;
taskIds: string[]; // 支持单个或多个
onConfirm: (agents: AgentType[]) => void;
onCancel: () => void;
}
// 弹窗内容:
// ✅ 稿约规范性智能体(默认选中)
// 格式、参考文献、图片检查
// ☐ 方法学统计智能体
// DeepSeek 深度逻辑推理
//
// 提示:可选择1个或同时选择2个智能体
//
// [取消] [立即运行]
- 3.3.1 实现智能体选择弹窗
- 3.3.2 支持单选和多选
- 3.3.3 默认选中规范性智能体
3.4 审稿工作台
┌─────────────────────────────────────────────────────────────────┐
│ Header: [Logo] 智能审稿系统 [上传新稿件] │
├─────────────────────────────────────────────────────────────────┤
│ Filter: [全部|待处理|已完成] │
├─────────────────────────────────────────────────────────────────┤
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ ☐ 文件名称/信息 上传时间 审稿维度 结果摘要 操作 │ │
│ │ ☐ 替雷利珠单抗...pdf 10:30 [规范][方法] 92分 [查看] │
│ │ ☐ 高血压药物...docx 刚刚 [未运行] 等待... [开始] │
│ └───────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ [Batch Toolbar: 3个文件已选中 | 运行智能审稿 | ✕] │
└─────────────────────────────────────────────────────────────────┘
- 3.4.1 实现页面布局
- 3.4.2 实现任务列表表格
- 3.4.3 实现状态筛选
- 3.4.4 实现批量操作栏
Day 5:报告详情 + Phase 3 验证
3.5 报告详情页
- 3.5.1 实现Tab切换(规范性/方法学)
- 3.5.2 复用评估详情组件
- 3.5.3 根据选择的智能体显示对应Tab
- 3.5.4 实现导出报告按钮
3.6 路由配置
// router/index.tsx
{
path: '/rvw',
children: [
{ path: '', element: <ReviewDashboard /> },
{ path: ':taskId', element: <ReviewDetail /> }
]
}
- 3.6.1 配置路由
- 3.6.2 添加导航菜单
Phase 3 验证测试
| 测试项 | 预期结果 | 通过 |
|---|---|---|
| 页面加载 | 工作台正常显示 | ⬜ |
| 文件上传 | 支持多文件 | ⬜ |
| 智能体选择 | 可选1个或2个 | ⬜ |
| 批量操作 | 批量运行成功 | ⬜ |
| 状态筛选 | 筛选结果正确 | ⬜ |
| 报告查看 | 显示对应评估结果 | ⬜ |
- 3.7 确认Phase 3通过
Phase 4:集成测试(1天)
4.1 端到端测试
| 测试场景 | 步骤 | 预期结果 |
|---|---|---|
| 单文件+单智能体 | 上传→选规范性→查看 | 只显示规范性报告 |
| 单文件+双智能体 | 上传→选两个→查看 | 显示两个报告Tab |
| 批量+双智能体 | 上传3个→批量运行 | 3个都完成 |
| 状态筛选 | 上传→筛选待处理 | 正确过滤 |
- 4.1.1 执行端到端测试
- 4.1.2 修复发现的问题
4.2 性能测试
| 指标 | 目标 | 实际 |
|---|---|---|
| 列表加载 | < 1秒 | ⬜ |
| 单文件审查 | < 2分钟 | ⬜ |
| 5文件并发 | < 5分钟 | ⬜ |
- 4.2.1 执行性能测试
4.3 兼容性测试
- 4.3.1 Chrome浏览器测试
- 4.3.2 Edge浏览器测试
- 4.3.3 旧API兼容性确认
Phase 5:验收与上线(0.5天)
5.1 MVP验收标准
| 验收项 | 预期结果 | 通过 |
|---|---|---|
| 流程通 | 5个PDF,3分钟内全部完成 | ⬜ |
| 规范性准确 | 删掉摘要结论必须报错 | ⬜ |
| 方法学准确 | 混淆统计方法必须报存疑 | ⬜ |
| 无崩溃 | 连续上传20个不卡死 | ⬜ |
- 5.1.1 执行验收测试
- 5.1.2 编写验收报告
5.2 上线准备
- 5.2.1 更新文档
- 5.2.2 通知相关人员
- 5.2.3 监控上线后状态
📅 时间估算
| Phase | 任务 | 预估工时 | 验证点 |
|---|---|---|---|
| Phase 1 | 后端模块迁移 | 2天 | ✓ API测试通过 |
| Phase 2 | 数据库迁移 | 0.5天 | ✓ 数据完整性验证 |
| Phase 3 | 前端重构 | 3天 | ✓ 功能测试通过 |
| Phase 4 | 集成测试 | 1天 | ✓ 端到端测试通过 |
| Phase 5 | 验收上线 | 0.5天 | ✓ MVP验收通过 |
| 总计 | - | 7天 | - |
⏸️ 暂不开发的功能(数据库已预留)
| 功能 | 预留字段 | 后续计划 |
|---|---|---|
| PICO卡片 | pico_extract |
方法学评估扩展 |
| 系统设置 | JournalConfig表 |
期刊Logo/模型配置 |
| 历史归档 | is_archived, archived_at |
自动归档7天前数据 |
| 登录页面 | - | 独立产品时开发 |
⚠️ 风险控制
1. 数据库迁移风险
| 风险 | 概率 | 影响 | 控制措施 |
|---|---|---|---|
| 数据丢失 | 低 | 高 | 迁移前备份 |
| 迁移失败 | 中 | 中 | 准备回滚SQL |
| 性能下降 | 低 | 中 | 验证索引有效性 |
2. 功能回归风险
| 风险 | 概率 | 影响 | 控制措施 |
|---|---|---|---|
| 旧API中断 | 低 | 高 | 保留v1路由 |
| 评估结果异常 | 低 | 高 | 对比测试结果 |
| 前端白屏 | 中 | 中 | 阶段性测试 |
3. 回滚计划
# 如果需要回滚到迁移前状态
# 1. 停止服务
pm2 stop all
# 2. 回滚数据库
pg_restore -h localhost -U postgres -d airesearch -c backup_phase2.dump
# 3. 切换到旧代码分支
git checkout main
# 4. 重启服务
pm2 start all
📚 参考文档
文档版本: v2.1
最后更新: 2026-01-07
下一步: 确认后开始Phase 1