Files
HaHafeng 66255368b7 feat(admin): Add user management and upgrade to module permission system
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
2026-01-16 13:42:10 +08:00

830 lines
26 KiB
Markdown
Raw Permalink 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.
# RVW稿件审查模块迁移计划v2.1 - 稳定迁移版)
> **文档版本:** v2.1
> **创建日期:** 2026-01-07
> **最后更新:** 2026-01-07
> **维护者:** 开发团队
> **文档目的:** 将稿件审查功能从旧架构安全迁移到新架构同时整合MVP核心需求
---
## 📋 项目概述
### 1. 背景
稿件审查功能是2025-10-30Day 30独立开发的功能模块目前位于 `backend/src/legacy/``frontend/` 目录中。现需要:
1. **架构迁移**:迁移到标准的模块目录结构(`modules/rvw`
2. **功能升级**整合《智能期刊审稿系统MVP产品需求文档》的核心功能
### 2. 迁移原则
| 原则 | 说明 |
|------|------|
| **稳定优先** | 每个Phase完成后必须通过测试验证 |
| **安全可靠** | 数据库迁移前必须备份,支持回滚 |
| **阶段验证** | 每个Phase有明确的验收标准 |
| **向后兼容** | 保留旧API过渡期不影响现有功能 |
| **渐进式** | 先核心后扩展,先后端后前端 |
### 3. 功能范围
| 功能 | 本次开发 | 数据库支撑 | 说明 |
|------|:--------:|:----------:|------|
| **核心AI评估** | ✅ | ✅ | 稿约规范性+方法学 |
| **批量上传** | ✅ | ✅ | 多文件上传 |
| **审稿工作台** | ✅ | ✅ | 宽表布局+筛选 |
| **智能体选择** | ✅ | ✅ | 可选1个或2个 |
| **批量操作** | ✅ | ✅ | 批量运行审查 |
| **状态筛选** | ✅ | ✅ | 全部/待处理/已完成 |
| PDF报告导出 | ✅ | ✅ | 优化现有功能 |
| PICO卡片 | ⏸️ | ✅ | **暂不开发,数据库预留** |
| 系统设置 | ⏸️ | ✅ | **暂不开发,数据库预留** |
| 历史归档 | ⏸️ | ✅ | **暂不开发,数据库预留** |
| 登录页面 | ⏸️ | - | 暂不开发 |
### 4. 智能体选择说明
用户可以灵活选择运行的智能体:
```
选项 A: 只选择「稿约规范性智能体」 → 只运行规范性评估
选项 B: 只选择「方法学统计智能体」 → 只运行方法学评估
选项 C: 同时选择两个智能体 → 同时运行两项评估(默认)
```
---
## 📊 数据库设计(完整版,支撑未来扩展)
### 1. 期刊配置表(预留,暂不使用)
```prisma
// 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. 审稿任务表(扩展版)
```prisma
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. 迁移前准备
```bash
# 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. 回滚方案
```bash
# 如果迁移失败,回滚数据库
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 智能体选择实现**
```typescript
// 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 批量运行实现**
```typescript
// 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路由定义**
```typescript
// 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 注册新路由**
```typescript
// 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 验证测试**
```http
### 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** 备份数据库
```bash
pg_dump -h localhost -U postgres -d airesearch -F c -f backup_phase2.dump
```
- [ ] **2.2** 记录当前数据
```sql
SELECT COUNT(*) FROM public.review_tasks;
```
#### 迁移执行
- [ ] **2.3** 创建迁移SQL
```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** 执行迁移
```bash
npx prisma migrate dev --name rvw_schema_migration
```
#### Phase 2 验证
- [ ] **2.6** 验证数据完整性
```sql
-- 确认数据量一致
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封装**
```typescript
// 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 智能体选择弹窗**
```tsx
// 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 路由配置**
```typescript
// 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个PDF3分钟内全部完成 | ⬜ |
| 规范性准确 | 删掉摘要结论必须报错 | ⬜ |
| 方法学准确 | 混淆统计方法必须报存疑 | ⬜ |
| 无崩溃 | 连续上传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. 回滚计划
```bash
# 如果需要回滚到迁移前状态
# 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
```
---
## 📚 参考文档
- [智能期刊审稿系统MVP PRD](../01-需求分析/智能期刊审稿系统%20MVP%20产品需求文档.md)
- [智能审稿V7原型](../01-需求分析/智能审稿V7.html)
- [云原生开发规范](../../04-开发规范/08-云原生开发规范.md)
- [现有系统技术摸底报告](../../00-项目概述/现有系统技术摸底报告.md)
---
**文档版本:** v2.1
**最后更新:** 2026-01-07
**下一步:** 确认后开始Phase 1