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
830 lines
26 KiB
Markdown
830 lines
26 KiB
Markdown
# RVW稿件审查模块迁移计划(v2.1 - 稳定迁移版)
|
||
|
||
> **文档版本:** v2.1
|
||
> **创建日期:** 2026-01-07
|
||
> **最后更新:** 2026-01-07
|
||
> **维护者:** 开发团队
|
||
> **文档目的:** 将稿件审查功能从旧架构安全迁移到新架构,同时整合MVP核心需求
|
||
|
||
---
|
||
|
||
## 📋 项目概述
|
||
|
||
### 1. 背景
|
||
|
||
稿件审查功能是2025-10-30(Day 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个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. 回滚计划
|
||
|
||
```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
|