feat(rvw): Complete RVW module development Phase 1-3

Summary:
- Migrate backend to modules/rvw with v2 API routes (/api/v2/rvw)
- Add new database fields: selectedAgents, editorialScore, methodologyStatus, picoExtract, isArchived
- Create frontend module in frontend-v2/src/modules/rvw
- Implement Dashboard with task list, filtering, batch operations
- Implement ReportDetail with dual tabs (editorial/methodology)
- Implement AgentModal for intelligent agent selection
- Register RVW module in moduleRegistry.ts
- Add navigation entry in TopNavigation
- Update documentation for RVW module status (v3.0)
- Update system status document (v2.9)

Features:
- User can select agents: editorial, methodology, or both
- Support batch task execution
- Task status filtering
- Replace console.log with logger service
- Maintain v1 API backward compatibility

Tested: Frontend and backend verified locally
Status: 85% complete (Phase 1-3 done)
This commit is contained in:
2026-01-07 22:39:08 +08:00
parent 06028c6952
commit 179afa2c6b
226 changed files with 5860 additions and 21 deletions

View File

@@ -1,10 +1,10 @@
# AIclinicalresearch 系统当前状态与开发指南
> **文档版本:** v2.8
> **文档版本:** v2.9
> **创建日期:** 2025-11-28
> **维护者:** 开发团队
> **最后更新:** 2026-01-07
> **重大进展:** 🎉 **PKB模块核心功能全部实现具备生产可用性** - 批处理完整流程验证通过
> **重大进展:** 🎉 **RVW稿件审查模块开发完成85%** - 后端迁移+数据库扩展+前端重构全部完成
> **部署状态:** ✅ 生产环境运行中 | 公网地址http://8.140.53.236/
> **文档目的:** 快速了解系统当前状态为新AI助手提供上下文
@@ -45,7 +45,7 @@
| **IIT** | IIT Manager Agent | AI驱动IIT研究助手 - 智能质控+REDCap集成 | ⭐⭐⭐⭐⭐ | 🎉 **Phase 1.5完成60%- AI对话+REDCap数据集成** | **P0** |
| **SSA** | 智能统计分析 | 队列/预测模型/RCT分析 | ⭐⭐⭐⭐⭐ | 📋 规划中 | P2 |
| **ST** | 统计分析工具 | 100+轻量化统计工具 | ⭐⭐⭐⭐ | 📋 规划中 | P2 |
| **RVW** | 稿件审查系统 | 方法学评估、审稿流程 | ⭐⭐⭐⭐ | 📋 规划中 | P3 |
| **RVW** | 稿件审查系统 | 方法学评估、审稿流程 | ⭐⭐⭐⭐ | **开发完成85%** | P3 |
---
@@ -661,6 +661,7 @@ AIclinicalresearch/
| **2026-01-07 上午** | **PKB前端V3** 🎉 | ✅ PKB模块前端V3设计实现完成Dashboard+Workspace+3种工作模式 |
| **2026-01-07 下午** | **PKB批处理完善** 🏆 | ✅ 批处理完整流程调试通过(执行+进度+结果导出)+ 文档上传功能 + UI优化 |
| **当前** | **PKB模块生产可用** | ✅ 核心功能全部实现90%),具备生产环境部署条件 |
| **2026-01-07 晚** | **RVW模块开发完成** 🎉 | ✅ Phase 1-3完成后端迁移+数据库扩展+前端重构) |
---
@@ -814,9 +815,9 @@ npm run dev # http://localhost:3000
- **总计**:约 85,000 行
### 模块完成度
-**已完成**AIA100%、平台基础层100%
- 🚧 **开发中**PKB75%前端V3设计完成、ASL80%、DCTool C 98%Tool B后端100%Tool B前端0%、IIT60%Phase 1.5完成)
- 📋 **未开始**SSA、ST、RVW
-**已完成**AIA100%、平台基础层100%、RVW85%Phase 1-3完成
- 🚧 **开发中**PKB90%,核心功能完成、ASL80%、DCTool C 98%Tool B后端100%Tool B前端0%、IIT60%Phase 1.5完成)
- 📋 **未开始**SSA、ST
### 部署完成度
-**基础设施**VPC100%、NAT网关100%、安全组100%
@@ -952,9 +953,9 @@ if (items.length >= 50) {
---
**文档版本**v2.8
**文档版本**v2.9
**最后更新**2026-01-07
**下次更新**ASL智能文献筛选模块启动 或 IIT Manager Agent Phase 2
**下次更新**RVW生产环境部署 或 ASL智能文献筛选模块启动
---
@@ -1016,3 +1017,36 @@ if (items.length >= 50) {
- ✅ 测试通过查询test0102项目ID 7患者详细信息
**模块进度**60%完成Phase 1.5
---
**RVW稿件审查模块开发完成2026-01-07**
### Phase 1后端模块迁移与扩展
- ✅ 创建 `backend/src/modules/rvw/` 模块结构
- ✅ 迁移 reviewService、editorialService、methodologyService
- ✅ 实现智能体选择selectedAgents
- ✅ 实现批量运行APIbatchRunReviewTasks
- ✅ 替换 console.log 为 logger 服务
- ✅ 注册 v2 API路由/api/v2/rvw
### Phase 2数据库字段扩展
- ✅ 添加 selectedAgents、editorialScore、methodologyStatus 字段
- ✅ 添加 picoExtract、isArchived、archivedAt 字段
- ✅ 使用 prisma db push 同步到数据库
### Phase 3前端重构frontend-v2
- ✅ 创建 `frontend-v2/src/modules/rvw/index.tsx`~503行
- ✅ 实现 Dashboard 组件(任务列表、筛选、批量操作)
- ✅ 实现 ReportDetail 组件(双标签页切换)
- ✅ 实现 AgentModal 组件(智能体选择弹窗)
- ✅ 注册到 moduleRegistry.ts
- ✅ 添加顶部导航"预审稿"入口
**技术亮点**
- 🔥 **新旧API兼容**v1 + v2 API同时运行
- 🔥 **智能体可选**:用户可选择运行稿约规范性/方法学/两者
- 🔥 **批量操作**:支持多选任务批量运行
- 🔥 **云原生改造**:使用 logger 服务,遵循开发规范
**模块进度**85%完成Phase 1-3

View File

@@ -606,5 +606,6 @@ async saveProcessedData(recordId, newData) {

View File

@@ -793,5 +793,6 @@ export const AsyncProgressBar: React.FC<AsyncProgressBarProps> = ({

View File

@@ -1286,5 +1286,6 @@ interface FulltextScreeningResult {

View File

@@ -400,5 +400,6 @@ GET /api/v1/asl/fulltext-screening/tasks/:taskId/export

View File

@@ -502,5 +502,6 @@ Failed to open file '\\tmp\\extraction_service\\temp_10000_test.pdf'

View File

@@ -568,5 +568,6 @@ df['creatinine'] = pd.to_numeric(df['creatinine'], errors='coerce')

View File

@@ -983,5 +983,6 @@ export const aiController = new AIController();

View File

@@ -1317,5 +1317,6 @@ npm install react-markdown

View File

@@ -225,5 +225,6 @@ FMA___基线 | FMA___1个月 | FMA___2个月

View File

@@ -383,5 +383,6 @@ formula = "FMA总分0-100 / 100"

View File

@@ -217,5 +217,6 @@ async handleFillnaMice(request, reply) {

View File

@@ -189,5 +189,6 @@ method: 'mean' | 'median' | 'mode' | 'constant' | 'ffill' | 'bfill'

View File

@@ -640,5 +640,6 @@ import { logger } from '../../../../common/logging/index.js';

View File

@@ -443,5 +443,6 @@ import { ChatContainer } from '@/shared/components/Chat';

View File

@@ -353,5 +353,6 @@ const initialMessages = defaultMessages.length > 0 ? defaultMessages : [{

View File

@@ -641,5 +641,6 @@ http://localhost:5173/data-cleaning/tool-c

View File

@@ -429,5 +429,6 @@ Docs: docs/03-业务模块/DC-数据清洗整理/06-开发记录/DC模块重建

View File

@@ -302,5 +302,6 @@ ConflictDetectionService // 冲突检测(字段级对比)

View File

@@ -466,5 +466,6 @@ Tool B后端代码**100%复用**了平台通用能力层,无任何重复开发

View File

@@ -243,5 +243,6 @@ $ node scripts/check-dc-tables.mjs

View File

@@ -476,5 +476,6 @@ ${fields.map((f, i) => `${i + 1}. ${f.name}${f.desc}`).join('\n')}

View File

@@ -683,3 +683,4 @@ private async processMessageAsync(xmlData: any) {

View File

@@ -1077,3 +1077,4 @@ async function testIntegration() {

View File

@@ -218,3 +218,4 @@ Content-Type: application/json

View File

@@ -638,3 +638,4 @@ REDCap API: exportRecords success { recordCount: 1 }

View File

@@ -644,3 +644,4 @@ backend/src/modules/iit-manager/

View File

@@ -794,3 +794,4 @@ CREATE TABLE iit_schema.wechat_tokens (

View File

@@ -551,3 +551,4 @@ Day 3 的开发工作虽然遇到了多个技术问题,但最终成功完成

View File

@@ -318,3 +318,4 @@ AI: "出生日期2017-01-04

View File

@@ -262,3 +262,4 @@ Day 4: REDCap EMWebhook推送← 作为增强,而非核心

View File

@@ -676,3 +676,4 @@ const answer = `根据研究方案[1]和CRF表格[2],纳入标准包括:

View File

@@ -356,3 +356,4 @@ const newResults = resultsData.map((docResult: any) => ({
3. 增加更多模板和自定义能力
4. 完善错误处理和用户反馈

View File

@@ -229,3 +229,4 @@ const chatApi = axios.create({
**下次更新**:批处理功能调试完成后

View File

@@ -0,0 +1,338 @@
# RVW稿件审查模块 - 当前状态与开发指南
> **文档版本:** v3.0
> **创建日期:** 2026-01-07
> **最后更新:** 2026-01-07
> **维护者:** 开发团队
> **当前状态:** ✅ **Phase 1-3 完成,前后端功能可用**
> **文档目的:** 快速了解RVW模块状态为新AI助手提供上下文
---
## 📊 模块概览
### 基本信息
| 项目 | 信息 |
|------|------|
| **模块名称** | RVW - 稿件审查系统 (Review) |
| **模块定位** | 智能期刊审稿辅助系统(可独立销售) |
| **商业价值** | ⭐⭐⭐⭐⭐ 极高 |
| **独立性** | ⭐⭐⭐⭐⭐ 极高(用户群完全不同) |
| **目标用户** | 期刊初审编辑 |
| **开发状态** | ✅ **核心功能100%完成,已集成到 frontend-v2** |
### 核心目标
> 打造一个**"开箱即用"**的智能审稿工具。编辑上传稿件,系统自动运行双重检查(规范性+方法学),输出可供参考的审稿报告。
>
> **核心指标**:上传到出报告 < 2分钟规范性问题检出率 > 80%
### 功能规格
#### 核心AI能力已完成 ✅)
1. **稿约规范性评估**11项标准
- 文题字数、摘要结构、参考文献、图片DPI等
- 基于《中华医学超声杂志》稿约标准
2. **方法学评估**3部分20个检查点
- 科研设计评估9个检查点
- 统计学方法描述评估5个检查点
- 统计分析评估6个检查点
3. **综合评分 + PICO提取**
- 规范性分数0-100
- 方法学状态(🔴错误 🟡存疑 🟢通过)
- PICO结构化提取P/I/C/O
#### 交互功能(✅ 已完成)
| 功能 | 旧版本 | 新版本 | 状态 |
|------|--------|--------|------|
| 单文件上传 | ✅ | ✅ | ✅ 已完成 |
| 批量上传 | ❌ | ✅ | ✅ 已完成 |
| 审稿工作台(宽表) | ❌ | ✅ | ✅ 已完成 |
| 智能体选择 | 自动 | 用户可选 | ✅ 已完成 |
| 批量操作 | ❌ | ✅ | ✅ 已完成 |
| 状态筛选 | ❌ | ✅ | ✅ 已完成 |
| 历史归档 | ❌ | ✅ | ⏸️ 数据库已支持UI暂缓 |
| 系统设置 | ❌ | ✅ | ⏸️ 暂不开发 |
| 登录页面 | ❌ | ⏸️ | ⏸️ 复用平台登录 |
| PICO卡片 | ❌ | ✅ | ⏸️ 数据库已支持UI暂缓 |
---
## 🏗️ 架构状态
### ✅ 已完成迁移Modules
```
后端(✅ 已完成):
backend/src/modules/rvw/
├── routes/index.ts # v2 API路由/api/v2/rvw
├── controllers/reviewController.ts # 控制器(含批量操作、智能体选择)
├── services/
│ ├── reviewService.ts # 核心服务(任务创建、执行)
│ ├── editorialService.ts # 稿约规范性评估
│ ├── methodologyService.ts # 方法学评估
│ └── utils.ts # 工具函数
├── types/index.ts # TypeScript类型定义
└── __tests__/ # API测试脚本
前端(✅ 已完成):
frontend-v2/src/modules/rvw/
└── index.tsx # 完整模块(~503行
├── Dashboard组件 # 审稿工作台(宽表、筛选、批量操作)
├── ReportDetail组件 # 报告详情(双标签页切换)
└── AgentModal组件 # 智能体选择弹窗
旧版本(保留兼容):
backend/src/legacy/
├── routes/reviewRoutes.ts # v1 API路由保留
├── controllers/reviewController.ts
└── services/reviewService.ts
Prompt保持不变
backend/prompts/
├── review_editorial_system.txt # 稿约评估266行
└── review_methodology_system.txt # 方法学评估257行
数据库(✅ 已完成):
- ReviewTask表已添加新字段selectedAgents, editorialScore, methodologyStatus, picoExtract, isArchived, archivedAt
- Schema迁移待后续执行当前在public schema
```
---
## 📋 开发进度
| Phase | 任务 | 状态 | 完成日期 |
|-------|------|------|---------|
| Phase 1 | 后端模块迁移与扩展 | ✅ 已完成 | 2026-01-07 |
| Phase 2 | 数据库字段扩展 | ✅ 已完成 | 2026-01-07 |
| Phase 3 | 前端重构frontend-v2 | ✅ 已完成 | 2026-01-07 |
| Phase 4 | 集成测试 | 🔧 基本通过 | 2026-01-07 |
| Phase 5 | 系统设置与归档 | ⏸️ 暂缓 | - |
| **总计** | - | **85%** | - |
### Phase 1-3 完成内容
**后端Phase 1**
- ✅ 创建 `backend/src/modules/rvw/` 模块结构
- ✅ 迁移并优化 reviewService、editorialService、methodologyService
- ✅ 实现智能体选择selectedAgentseditorial/methodology/both
- ✅ 实现批量运行APIbatchRunReviewTasks
- ✅ 替换 console.log 为 logger 服务
- ✅ 注册 v2 API路由/api/v2/rvw
**数据库Phase 2**
- ✅ 添加 selectedAgents 字段String[]
- ✅ 添加 editorialScore 字段Float?
- ✅ 添加 methodologyStatus 字段String?
- ✅ 添加 picoExtract 字段Json?
- ✅ 添加 isArchived、archivedAt 字段(归档支持)
- ✅ 使用 prisma db push 同步到数据库
**前端Phase 3**
- ✅ 创建 `frontend-v2/src/modules/rvw/index.tsx`~503行
- ✅ 实现 Dashboard 组件(任务列表、筛选、批量操作)
- ✅ 实现 ReportDetail 组件(双标签页:稿约规范性/方法学)
- ✅ 实现 AgentModal 组件(智能体选择弹窗)
- ✅ 注册到 moduleRegistry.ts
- ✅ 添加顶部导航入口
详细任务清单见:[RVW模块迁移计划](./04-开发计划/RVW模块迁移计划.md)
---
## 🔧 技术依赖
### 复用的平台能力
| 能力 | 位置 | 用途 |
|------|------|------|
| **LLM网关** | `@/common/llm/LLMFactory` | AI评估 |
| **文档处理** | `ExtractionClient` | Word/PDF文本提取 |
| **存储** | `@/common/storage` | 文件存储 |
| **日志** | `@/common/logging` | 结构化日志 |
| **任务队列** | `jobQueue` | 异步任务处理 |
### LLM模型
| 模型 | 用途 | 说明 |
|------|------|------|
| DeepSeek-V3 | 默认 | 性价比高,推理能力强 |
| DeepSeek-R1 | 备选 | 深度推理 |
| Qwen3-72B | 备选 | 中文理解好 |
---
## 📚 相关文档
### 需求文档
- [智能期刊审稿系统MVP PRD](./01-需求分析/智能期刊审稿系统%20MVP%20产品需求文档.md)
- [智能审稿V7原型](./01-需求分析/智能审稿V7.html) - 可直接浏览器打开
### 开发文档
- [迁移计划v2.0整合版)](./04-开发计划/RVW模块迁移计划.md) ⬅️ **主要开发文档**
### 参考文档
- [现有系统技术摸底报告](../../00-项目概述/现有系统技术摸底报告.md) - Line 578-748
- [云原生开发规范](../../04-开发规范/08-云原生开发规范.md)
- [系统架构分层设计](../../00-系统总体设计/01-系统架构分层设计.md)
### Prompt文件
- [稿约规范性评估标准](./稿约规范性评估标准.txt)
- [稿件方法学评估标准](./稿件方法学评估标准.txt)
---
## 🎯 快速开始
### 访问审稿模块
1. **启动后端**
```bash
cd backend
npm run dev
```
2. **启动前端(新版 frontend-v2**
```bash
cd frontend-v2
npm run dev
```
3. **访问审稿页面**
- 打开 `http://localhost:3000`
- 点击顶部导航栏的 **"预审稿"** 标签
- 或直接访问 `http://localhost:3000/rvw`
4. **测试流程**
- 点击"上传稿件"按钮
- 选择Word/PDF文档≤5MB
- 选择智能体(稿约规范性/方法学/两者都选)
- 点击"运行"按钮
- 等待AI评估完成约1-2分钟
- 查看评估报告
### API测试新版 v2 API
```http
### 获取任务列表
GET http://localhost:3001/api/v2/rvw/tasks
### 按状态筛选
GET http://localhost:3001/api/v2/rvw/tasks?status=pending
### 上传稿件
POST http://localhost:3001/api/v2/rvw/tasks
Content-Type: multipart/form-data
# file: 文件
# selectedAgents: ["editorial", "methodology"]
### 运行单个任务
POST http://localhost:3001/api/v2/rvw/tasks/{{taskId}}/run
Content-Type: application/json
{"selectedAgents": ["editorial", "methodology"]}
### 批量运行任务
POST http://localhost:3001/api/v2/rvw/tasks/batch-run
Content-Type: application/json
{"taskIds": ["id1", "id2"], "selectedAgents": ["editorial"]}
### 获取任务详情
GET http://localhost:3001/api/v2/rvw/tasks/{{taskId}}
### 获取报告
GET http://localhost:3001/api/v2/rvw/tasks/{{taskId}}/report
```
### 旧版API保持兼容
```http
### 上传稿件旧API
POST http://localhost:3001/api/v1/review/upload
Content-Type: multipart/form-data
```
---
## ⚠️ 注意事项
### 对新AI助手
1.**核心功能已完成**:前后端已迁移到新架构,可正常使用
2.**已集成到 frontend-v2**:通过顶部导航栏"预审稿"进入
3.**v2 API 已就绪**/api/v2/rvw/* 路由可用
4.**遵循云原生规范**:使用 logger 服务替代 console.log
5. ⚠️ **保留旧API**v1路由保持兼容支持旧前端
### 已完成改造
| 问题 | 改造前 | 改造后 | 状态 |
|------|--------|--------|------|
| 日志 | console.log | logger服务 | ✅ 已完成 |
| 用户认证 | Mock用户ID | getUserId(request) | ✅ 已完成 |
| 智能体选择 | 自动 | 用户可选 | ✅ 已完成 |
| 批量操作 | 无 | batchRunReviewTasks | ✅ 已完成 |
### 待后续改造
| 问题 | 当前 | 目标 | 优先级 |
|------|------|------|--------|
| Schema位置 | public | review_schema | P2 |
| 任务处理 | 直接异步 | jobQueue | P2 |
| 报告导出 | 基础版 | PDF优化 | P3 |
---
## 📈 验收标准
根据MVP PRD验收标准如下
1. **流程通**用户能成功上传5个PDF勾选双模型运行等待3分钟内状态全部变为"已完成"
2. **报告准**
- 上传一篇故意删掉"摘要结论"的稿件,规范性智能体必须报错
- 上传一篇故意混淆"t检验"和"卡方检验"的稿件,方法学智能体必须报"存疑"或"错误"
3. **无崩溃**连续上传20个文件系统不卡死不白屏
---
## 🚀 未来规划
### ✅ 已完成2026-01-07
- [x] 架构迁移到 modules/rvw后端
- [x] 架构迁移到 modules/rvw前端 frontend-v2
- [x] 整合MVP新功能批量上传、工作台、智能体选择
- [x] 云原生改造logger服务
- [x] v2 API 路由注册
- [x] 数据库字段扩展
### 后续版本
- [ ] Schema迁移到 review_schema
- [ ] 任务队列改造jobQueue
- [ ] PDF报告导出优化
- [ ] PICO卡片UI实现
- [ ] 历史归档UI实现
- [ ] 登录页面(独立产品时)
- [ ] 审稿人管理系统
- [ ] 多轮审稿流程
- [ ] 期刊库管理
- [ ] 独立产品打包
---
**文档版本:** v3.0
**最后更新:** 2026-01-07
**当前状态:** ✅ Phase 1-3 完成,模块可用
**下一步:** 生产环境部署测试 或 Schema隔离迁移

View File

@@ -0,0 +1,529 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>智能期刊审稿系统 - 中华脑血管病杂志专版</title>
<script src="https://cdn.tailwindcss.com"></script>
<!-- 引入 Lucide 图标 (UMD版) -->
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background-color: #f8fafc; color: #334155; }
/* 状态标签 */
.tag { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600; line-height: 1.5; border: 1px solid transparent; }
.tag-blue { background: #eff6ff; color: #1d4ed8; border-color: #dbeafe; }
.tag-purple { background: #f5f3ff; color: #6d28d9; border-color: #ede9fe; }
.tag-green { background: #f0fdf4; color: #15803d; border-color: #dcfce7; }
.tag-amber { background: #fffbeb; color: #b45309; border-color: #fef3c7; }
.tag-gray { background: #f8fafc; color: #64748b; border-color: #e2e8f0; }
/* 筛选 Chips */
.filter-chip {
padding: 4px 12px; border-radius: 9999px; font-size: 13px; font-weight: 500; cursor: pointer; transition: all 0.2s; border: 1px solid transparent; color: #64748b;
}
.filter-chip:hover { background-color: #f1f5f9; color: #0f172a; }
.filter-chip.active { background-color: #eff6ff; color: #2563eb; border-color: #bfdbfe; font-weight: 600; }
/* 选中行样式 */
tr.selected { background-color: #eff6ff; }
/* Tab 激活样式 */
.tab-btn { border-bottom: 2px solid transparent; color: #64748b; }
.tab-btn:hover { color: #334155; }
.tab-btn.active { color: #2563eb; border-bottom: 2px solid #2563eb; font-weight: 600; }
/* 动画 */
.fade-in { animation: fadeIn 0.4s cubic-bezier(0.16, 1, 0.3, 1); }
.slide-up { animation: slideUp 0.4s cubic-bezier(0.16, 1, 0.3, 1); }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
/* 侧边栏 Tooltip */
.sidebar-btn:hover::after {
content: attr(data-title); position: absolute; left: 100%; top: 50%; transform: translateY(-50%); margin-left: 12px; background: #1e293b; color: white; padding: 6px 10px; border-radius: 6px; font-size: 12px; white-space: nowrap; z-index: 50; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
/* 评分环 */
.score-circle {
width: 80px; height: 80px; border-radius: 50%; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 6px solid #e2e8f0;
}
.score-circle.pass { border-color: #22c55e; background: #f0fdf4; color: #15803d; }
.score-circle.warn { border-color: #f59e0b; background: #fffbeb; color: #b45309; }
/* 登录页背景 */
.login-bg {
background-image: radial-gradient(circle at 10% 20%, rgb(239, 246, 255) 0%, rgb(255, 255, 255) 90%);
}
</style>
</head>
<body class="h-screen overflow-hidden">
<!-- 视图 0: 登录页 (新增模块) -->
<div id="view-login" class="fixed inset-0 z-50 login-bg flex flex-col items-center justify-center p-4">
<div class="w-full max-w-md bg-white rounded-2xl shadow-xl p-8 slide-up border border-slate-100">
<!-- 品牌 LOGO 区域 -->
<div class="flex flex-col items-center mb-8">
<div class="w-16 h-16 bg-indigo-600 rounded-2xl flex items-center justify-center text-white shadow-lg mb-4 transform rotate-3">
<i data-lucide="brain-circuit" class="w-10 h-10"></i>
</div>
<h1 class="text-2xl font-bold text-slate-800">中华脑血管病杂志</h1>
<p class="text-sm text-slate-500 mt-2">智能审稿辅助系统 v1.0</p>
</div>
<!-- 登录表单 -->
<form onsubmit="handleLogin(event)" class="space-y-5">
<div>
<label class="block text-sm font-bold text-slate-700 mb-1.5">账号</label>
<div class="relative">
<i data-lucide="user" class="absolute left-3 top-2.5 w-5 h-5 text-slate-400"></i>
<input type="text" value="editor_admin" class="w-full pl-10 pr-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition-all" placeholder="请输入编辑账号">
</div>
</div>
<div>
<label class="block text-sm font-bold text-slate-700 mb-1.5">密码</label>
<div class="relative">
<i data-lucide="lock" class="absolute left-3 top-2.5 w-5 h-5 text-slate-400"></i>
<input type="password" value="123456" class="w-full pl-10 pr-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition-all" placeholder="请输入密码">
</div>
</div>
<div class="flex items-center justify-between text-sm">
<label class="flex items-center gap-2 cursor-pointer">
<input type="checkbox" checked class="w-4 h-4 text-indigo-600 rounded border-gray-300">
<span class="text-slate-600">记住我</span>
</label>
<a href="#" class="text-indigo-600 hover:underline font-medium">忘记密码?</a>
</div>
<button type="submit" class="w-full bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-2.5 rounded-lg shadow-md transition-all hover:shadow-lg flex items-center justify-center gap-2">
登录系统 <i data-lucide="arrow-right" class="w-4 h-4"></i>
</button>
</form>
<p class="text-center text-xs text-slate-400 mt-8">
&copy; 2025 AI审稿技术支持平台 · 技术支持
</p>
</div>
</div>
<!-- 主系统容器 (登录后显示) -->
<div id="app-container" class="hidden h-full flex">
<!-- 侧边栏 -->
<aside class="w-18 bg-slate-900 flex flex-col items-center py-6 gap-4 z-20 shadow-xl flex-shrink-0 relative">
<!-- 客户 LOGO (侧边栏折叠版) -->
<div class="w-10 h-10 bg-indigo-500 rounded-xl flex items-center justify-center text-white shadow-lg mb-4" title="中华脑血管病杂志">
<i data-lucide="brain-circuit" class="w-6 h-6"></i>
</div>
<button onclick="switchView('list')" class="sidebar-btn w-10 h-10 rounded-lg bg-white/10 text-white flex items-center justify-center hover:bg-indigo-600 transition-colors relative" data-title="审稿工作台">
<i data-lucide="layout-grid" class="w-5 h-5"></i>
</button>
<button onclick="alert('历史归档模式此处将显示7天前已完成的稿件。')" class="sidebar-btn w-10 h-10 rounded-lg text-slate-400 flex items-center justify-center hover:bg-white/10 hover:text-white transition-colors relative" data-title="历史归档">
<i data-lucide="archive" class="w-5 h-5"></i>
</button>
<div class="mt-auto flex flex-col gap-4 relative">
<button onclick="openSettings()" class="sidebar-btn w-10 h-10 rounded-lg text-slate-400 flex items-center justify-center hover:bg-white/10 hover:text-white transition-colors relative" data-title="系统设置">
<i data-lucide="settings" class="w-5 h-5"></i>
</button>
<!-- 用户头像 (点击触发菜单) -->
<div class="relative">
<div onclick="toggleUserMenu()" class="w-10 h-10 rounded-full bg-gradient-to-tr from-indigo-500 to-purple-500 flex items-center justify-center text-xs font-bold text-white border-2 border-slate-700 cursor-pointer hover:border-white transition-all shadow-md">
主编
</div>
<!-- 用户悬浮菜单 -->
<div id="user-menu" class="hidden absolute bottom-0 left-12 ml-2 w-48 bg-white rounded-lg shadow-xl border border-gray-100 py-1 z-50 animate-in fade-in zoom-in-95 origin-bottom-left">
<div class="px-4 py-3 border-b border-gray-50 bg-slate-50">
<p class="text-sm font-bold text-slate-800">王主编</p>
<p class="text-xs text-slate-500 truncate">editor@chinastroke.com</p>
</div>
<button onclick="openChangePassword()" class="w-full text-left px-4 py-2 text-sm text-slate-700 hover:bg-indigo-50 hover:text-indigo-600 flex items-center gap-2">
<i data-lucide="key" class="w-4 h-4"></i> 修改密码
</button>
<button onclick="handleLogout()" class="w-full text-left px-4 py-2 text-sm text-red-600 hover:bg-red-50 flex items-center gap-2">
<i data-lucide="log-out" class="w-4 h-4"></i> 退出登录
</button>
</div>
</div>
</div>
</aside>
<!-- 主内容区 -->
<main class="flex-1 flex flex-col min-w-0 bg-white">
<!-- 视图 1: 审稿列表 (Dashboard) -->
<div id="view-list" class="flex-1 flex flex-col h-full relative fade-in">
<!-- 顶部核心操作区 -->
<header class="bg-white px-8 pt-6 pb-4 border-b border-gray-100 flex-shrink-0 z-10">
<div class="flex justify-between items-center mb-6">
<!-- 客户 LOGO 展示区 (新增) -->
<div class="flex items-center gap-3">
<div class="bg-indigo-50 p-2 rounded-lg text-indigo-700">
<i data-lucide="brain-circuit" class="w-6 h-6"></i>
</div>
<div>
<h1 class="text-xl font-bold text-slate-800">中华脑血管病杂志 · 智能审稿系统</h1>
<p class="text-xs text-slate-500">当前工作区:编辑部初审组</p>
</div>
</div>
<div class="flex gap-3">
<input type="file" id="file-upload" multiple class="hidden" onchange="handleFileUpload(this)">
<button onclick="document.getElementById('file-upload').click()" class="px-5 py-2.5 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg text-sm font-bold flex items-center gap-2 shadow-sm transition-all hover:-translate-y-0.5">
<i data-lucide="upload-cloud" class="w-4 h-4"></i> 上传新稿件
</button>
</div>
</div>
<!-- 筛选栏 -->
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="text-xs font-bold text-slate-400 uppercase tracking-wider mr-2">状态:</span>
<div class="filter-chip active" onclick="activateFilter(this)">全部 <span class="ml-1 text-xs opacity-60 bg-black/10 px-1.5 rounded-full">3</span></div>
<div class="filter-chip" onclick="activateFilter(this)">待处理 <span class="ml-1 text-xs opacity-60 bg-slate-200 px-1.5 rounded-full">1</span></div>
<div class="filter-chip" onclick="activateFilter(this)">已完成</div>
</div>
<div class="h-4 w-px bg-gray-200 mx-2"></div>
<div class="flex items-center gap-2">
<span class="text-xs font-bold text-slate-400 uppercase tracking-wider mr-2">时间:</span>
<div class="filter-chip active" onclick="activateFilter(this)">不限</div>
<div class="filter-chip" onclick="activateFilter(this)">今天</div>
<div class="filter-chip" onclick="activateFilter(this)">近7天</div>
</div>
</div>
</header>
<!-- 批量操作栏 -->
<div id="batch-toolbar" class="hidden absolute top-36 left-1/2 transform -translate-x-1/2 bg-slate-800 text-white px-5 py-3 rounded-full shadow-2xl flex items-center gap-6 z-30 fade-in border border-slate-700">
<div class="flex items-center gap-2">
<div class="w-5 h-5 rounded-full bg-indigo-500 flex items-center justify-center text-[10px] font-bold" id="selected-count">0</div>
<span class="text-sm font-medium">个文件已选中</span>
</div>
<div class="h-4 w-px bg-slate-600"></div>
<button onclick="openAgentModal()" class="text-sm font-bold text-white hover:text-indigo-300 flex items-center gap-2 transition-colors">
<i data-lucide="play" class="w-4 h-4 text-green-400"></i> 运行智能审稿
</button>
<button onclick="clearSelection()" class="text-slate-400 hover:text-white ml-2">
<i data-lucide="x" class="w-4 h-4"></i>
</button>
</div>
<!-- 列表区域 -->
<div class="flex-1 overflow-auto bg-slate-50/50 p-6">
<div class="bg-white border border-gray-200 rounded-xl shadow-sm overflow-hidden min-h-[500px]">
<table class="w-full text-left text-sm" id="file-table">
<thead class="bg-gray-50 border-b border-gray-200 text-gray-500 font-semibold uppercase tracking-wider text-xs">
<tr>
<th class="px-6 py-4 w-12"><input type="checkbox" onchange="toggleSelectAll(this)" class="rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 cursor-pointer"></th>
<th class="px-6 py-4 w-1/3">文件名称 / 信息</th>
<th class="px-6 py-4">上传时间</th>
<th class="px-6 py-4">审稿维度</th>
<th class="px-6 py-4">结果摘要</th>
<th class="px-6 py-4 text-right">操作</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100" id="table-body">
<!-- 已完成示例 -->
<tr class="hover:bg-slate-50 group transition-colors">
<td class="px-6 py-4"><input type="checkbox" onchange="toggleRowSelection(this)" class="row-checkbox rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 cursor-pointer"></td>
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<div class="bg-red-50 text-red-600 p-2.5 rounded-lg border border-red-100">
<i data-lucide="file-text" class="w-5 h-5"></i>
</div>
<div>
<div class="font-bold text-slate-800 text-base mb-0.5 cursor-pointer hover:text-indigo-600" onclick="viewReport(true)">
替雷利珠单抗治疗非小细胞肺癌的临床研究.pdf
</div>
<div class="text-xs text-slate-400 flex items-center gap-2">
<span class="bg-slate-100 px-1.5 rounded">12.5 MB</span> <span></span> <span>王某某</span>
</div>
</div>
</div>
</td>
<td class="px-6 py-4 text-slate-500 font-mono text-xs">10:30</td>
<td class="px-6 py-4">
<div class="flex gap-1.5">
<span class="tag tag-blue">规范性</span>
<span class="tag tag-purple">方法学</span>
</div>
</td>
<td class="px-6 py-4">
<div class="flex flex-col gap-1.5">
<div class="flex items-center gap-2 text-xs">
<div class="w-2 h-2 rounded-full bg-green-500"></div>
<span class="text-slate-600">规范性:</span>
<span class="font-bold text-green-700">92分</span>
</div>
<div class="flex items-center gap-2 text-xs">
<div class="w-2 h-2 rounded-full bg-amber-500"></div>
<span class="text-slate-600">方法学:</span>
<span class="font-bold text-amber-700">存疑</span>
</div>
</div>
</td>
<td class="px-6 py-4 text-right">
<button onclick="viewReport(true)" class="text-indigo-600 font-bold hover:bg-indigo-50 px-3 py-1.5 rounded-md transition-colors text-xs">查看</button>
</td>
</tr>
<!-- 待处理示例 -->
<tr class="hover:bg-slate-50 group transition-colors status-pending">
<td class="px-6 py-4"><input type="checkbox" onchange="toggleRowSelection(this)" class="row-checkbox rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 cursor-pointer"></td>
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<div class="bg-blue-50 text-blue-600 p-2.5 rounded-lg border border-blue-100">
<i data-lucide="file-type-2" class="w-5 h-5"></i>
</div>
<div>
<div class="font-bold text-slate-800 text-base mb-0.5">
高血压药物多中心随机对照试验_V2.docx
</div>
<div class="text-xs text-slate-400 flex items-center gap-2">
<span class="bg-slate-100 px-1.5 rounded">4.2 MB</span> <span></span> <span>李四</span>
</div>
</div>
</div>
</td>
<td class="px-6 py-4 text-slate-500 font-mono text-xs">刚刚</td>
<td class="px-6 py-4"><span class="tag tag-gray">未运行</span></td>
<td class="px-6 py-4"><span class="text-xs text-slate-400 italic">等待发起...</span></td>
<td class="px-6 py-4 text-right">
<button onclick="selectRowAndOpenModal(this)" class="border border-indigo-200 text-indigo-600 hover:bg-indigo-50 px-3 py-1.5 rounded-md transition-colors text-xs font-medium flex items-center gap-1 ml-auto">
<i data-lucide="play" class="w-3 h-3"></i> 开始
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- 视图 2: 详情报告页 (保持 V6 的真实数据) -->
<div id="view-detail" class="hidden flex-1 flex flex-col h-full bg-slate-50 relative animate-in fade-in slide-in-from-bottom-4">
<header class="h-16 bg-white border-b border-gray-200 px-6 flex items-center justify-between sticky top-0 z-20 shadow-sm">
<div class="flex items-center gap-4">
<button onclick="switchView('list')" class="flex items-center gap-2 text-slate-500 hover:text-slate-800 transition-colors px-2 py-1 rounded hover:bg-slate-100">
<i data-lucide="arrow-left" class="w-5 h-5"></i>
<span class="text-sm font-medium">返回列表</span>
</button>
<div class="h-6 w-px bg-slate-200"></div>
<div>
<h1 class="text-base font-bold text-slate-800 flex items-center gap-2">
替雷利珠单抗治疗非小细胞肺癌的临床研究.pdf
<span class="tag tag-blue">V2 修回稿</span>
</h1>
</div>
</div>
<div class="flex items-center gap-3">
<button class="px-3 py-1.5 bg-indigo-600 text-white rounded text-sm font-medium hover:bg-indigo-700 transition shadow-sm flex items-center gap-2">
<i data-lucide="file-check" class="w-4 h-4"></i> 导出报告
</button>
</div>
</header>
<div class="flex-1 overflow-auto p-8 max-w-5xl mx-auto w-full">
<div class="flex gap-1 bg-slate-200/50 p-1 rounded-lg mb-8 w-fit mx-auto">
<button onclick="switchTab('compliance')" id="tab-compliance" class="px-6 py-2 rounded-md text-sm font-bold bg-white text-indigo-600 shadow-sm transition-all">稿约规范性 (92分)</button>
<button onclick="switchTab('methodology')" id="tab-methodology" class="px-6 py-2 rounded-md text-sm font-medium text-slate-500 hover:text-slate-700 transition-all">方法学评估 (存疑)</button>
</div>
<!-- 规范性报告 -->
<div id="content-compliance" class="space-y-6 fade-in">
<div class="bg-white p-6 rounded-2xl shadow-sm border border-green-200 flex items-center gap-8">
<div class="score-circle pass">
<span class="text-2xl font-bold">92</span>
<span class="text-[10px] font-bold uppercase">Pass</span>
</div>
<div class="flex-1">
<h3 class="font-bold text-lg text-slate-800">基本符合稿约规范</h3>
<p class="text-slate-600 text-sm mt-1">AI 已完成 11 项维度检查。主要问题集中在图片清晰度,其他格式(参考文献、伦理声明)均已合规。</p>
</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
<div class="p-4 bg-slate-50 border-b border-gray-200 font-bold text-sm text-slate-700">检测详情</div>
<div class="p-5 border-b border-gray-100 flex gap-4 bg-amber-50/50">
<div class="mt-1 text-amber-500"><i data-lucide="alert-triangle" class="w-5 h-5"></i></div>
<div class="flex-1">
<div class="flex justify-between"><h4 class="font-bold text-sm text-slate-800">图片分辨率不足 (Figure 3)</h4><span class="tag tag-amber">警告</span></div>
<p class="text-sm text-slate-600 mt-1">检测到病理切片图 DPI 为 150低于期刊要求的 300 DPI。</p>
<div class="mt-3 bg-white border border-amber-200 p-3 rounded-lg text-xs text-slate-600"><strong>建议:</strong> 请提供原始高清大图。</div>
</div>
</div>
</div>
</div>
<!-- 方法学报告 -->
<div id="content-methodology" class="hidden space-y-6 fade-in">
<div class="bg-indigo-900 text-white rounded-2xl p-6 shadow-lg">
<div class="flex items-center gap-2 mb-4 opacity-80 text-xs font-bold uppercase tracking-wider"><i data-lucide="brain-circuit" class="w-4 h-4"></i> DeepSeek 智能提取</div>
<div class="grid grid-cols-4 gap-4">
<div class="bg-white/10 p-3 rounded-lg"><div class="text-indigo-300 text-xs uppercase mb-1 font-bold">P</div><div class="text-sm font-medium">非小细胞肺癌</div></div>
<div class="bg-white/10 p-3 rounded-lg"><div class="text-indigo-300 text-xs uppercase mb-1 font-bold">I</div><div class="text-sm font-medium">替雷利珠单抗</div></div>
<div class="bg-white/10 p-3 rounded-lg"><div class="text-indigo-300 text-xs uppercase mb-1 font-bold">C</div><div class="text-sm font-medium">传统化疗</div></div>
<div class="bg-white/10 p-3 rounded-lg"><div class="text-indigo-300 text-xs uppercase mb-1 font-bold">O</div><div class="text-sm font-medium">OS, PFS</div></div>
</div>
</div>
<div class="bg-white rounded-xl border border-red-200 shadow-sm overflow-hidden">
<div class="p-4 bg-red-50 border-b border-red-100 text-red-700 font-bold text-sm flex items-center gap-2"><i data-lucide="x-circle" class="w-4 h-4"></i> 核心逻辑漏洞</div>
<div class="p-6"><h5 class="font-bold text-slate-800 text-sm">统计检验方法缺失</h5><p class="text-sm text-slate-600 mt-1">比较 AUC 时未提及具体检验方法(如 Delong Test仅给出 P 值不严谨。</p></div>
</div>
</div>
</div>
</div>
</main>
<!-- 弹窗:选择智能体 -->
<div id="modal-agent" class="fixed inset-0 bg-slate-900/50 hidden z-50 flex items-center justify-center backdrop-blur-sm">
<div class="bg-white rounded-2xl shadow-2xl w-[400px] overflow-hidden transform transition-all scale-100">
<div class="bg-slate-900 p-5 text-white"><h3 class="font-bold text-lg flex items-center gap-2"><i data-lucide="play-circle" class="w-5 h-5 text-indigo-400"></i> 发起智能审稿</h3></div>
<div class="p-6 space-y-4">
<label class="flex items-start gap-3 p-3 border border-gray-200 rounded-xl hover:border-indigo-500 cursor-pointer transition-colors"><input type="checkbox" checked class="mt-1 w-4 h-4 text-indigo-600 rounded"><div><span class="block font-bold text-slate-800 text-sm">稿约规范性智能体</span><span class="block text-xs text-slate-500 mt-0.5">格式、参考文献、图片</span></div></label>
<label class="flex items-start gap-3 p-3 border border-gray-200 rounded-xl hover:border-indigo-500 cursor-pointer transition-colors"><input type="checkbox" class="mt-1 w-4 h-4 text-indigo-600 rounded"><div><span class="block font-bold text-slate-800 text-sm">方法学统计智能体</span><span class="block text-xs text-slate-500 mt-0.5">DeepSeek 深度逻辑推理</span></div></label>
</div>
<div class="p-4 bg-slate-50 flex justify-end gap-3 border-t border-gray-100">
<button onclick="closeModal()" class="px-4 py-2 text-sm text-slate-600 hover:bg-gray-200 rounded-lg">取消</button>
<button onclick="runBatchTask()" class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-bold rounded-lg shadow-sm">立即运行</button>
</div>
</div>
</div>
<!-- 弹窗:系统设置 -->
<div id="modal-settings" class="fixed inset-0 bg-slate-900/50 hidden z-50 flex items-center justify-center backdrop-blur-sm">
<div class="bg-white rounded-2xl shadow-2xl w-[400px] p-6">
<h3 class="font-bold text-lg mb-4 text-slate-800">系统设置</h3>
<div class="space-y-4">
<div><label class="block text-sm font-bold text-slate-700 mb-1">期刊 Logo 上传</label><input type="file" class="block w-full text-sm text-slate-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-xs file:font-semibold file:bg-indigo-50 file:text-indigo-700 hover:file:bg-indigo-100"/></div>
<div><label class="block text-sm font-bold text-slate-700 mb-1">默认 AI 模型</label><select class="w-full border border-gray-300 rounded-lg p-2 text-sm"><option>DeepSeek V3</option><option>DeepSeek R1</option></select></div>
</div>
<div class="mt-6 pt-4 border-t border-gray-100 flex justify-end"><button onclick="closeSettings()" class="px-4 py-2 bg-indigo-600 text-white rounded-lg text-sm font-bold">保存设置</button></div>
</div>
</div>
<!-- 弹窗:修改密码 -->
<div id="modal-password" class="fixed inset-0 bg-slate-900/50 hidden z-50 flex items-center justify-center backdrop-blur-sm">
<div class="bg-white rounded-2xl shadow-2xl w-[400px] p-6">
<h3 class="font-bold text-lg mb-4 text-slate-800">修改密码</h3>
<div class="space-y-4">
<div><label class="block text-sm font-bold text-slate-700 mb-1">旧密码</label><input type="password" class="w-full p-2 border rounded-lg"></div>
<div><label class="block text-sm font-bold text-slate-700 mb-1">新密码</label><input type="password" class="w-full p-2 border rounded-lg"></div>
<div><label class="block text-sm font-bold text-slate-700 mb-1">确认新密码</label><input type="password" class="w-full p-2 border rounded-lg"></div>
</div>
<div class="mt-6 pt-4 border-t border-gray-100 flex justify-end gap-2">
<button onclick="closeChangePassword()" class="px-4 py-2 text-sm text-slate-600">取消</button>
<button onclick="closeChangePassword(); alert('密码修改成功');" class="px-4 py-2 bg-indigo-600 text-white rounded-lg text-sm font-bold">确认修改</button>
</div>
</div>
</div>
</div>
<script>
lucide.createIcons();
// 1. 登录逻辑
function handleLogin(e) {
e.preventDefault();
const btn = e.target.querySelector('button');
const originalText = btn.innerHTML;
btn.innerHTML = `<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i> 登录中...`;
lucide.createIcons();
setTimeout(() => {
document.getElementById('view-login').classList.add('hidden');
document.getElementById('app-container').classList.remove('hidden');
lucide.createIcons();
}, 1000);
}
function handleLogout() {
if(confirm('确定要退出登录吗?')) {
window.location.reload();
}
}
// 视图切换
function switchView(viewId) {
document.getElementById('view-list').classList.add('hidden');
document.getElementById('view-detail').classList.add('hidden');
document.getElementById('view-' + viewId).classList.remove('hidden');
if(viewId === 'list') lucide.createIcons();
}
function viewReport(isReal) { switchView('detail'); }
// Tab 切换
function switchTab(tabId) {
document.getElementById('content-compliance').classList.add('hidden');
document.getElementById('content-methodology').classList.add('hidden');
document.getElementById('tab-compliance').className = "px-6 py-2 rounded-md text-sm font-medium text-slate-500 hover:text-slate-700 transition-all";
document.getElementById('tab-methodology').className = "px-6 py-2 rounded-md text-sm font-medium text-slate-500 hover:text-slate-700 transition-all";
document.getElementById('content-' + tabId).classList.remove('hidden');
document.getElementById('tab-' + tabId).className = "px-6 py-2 rounded-md text-sm font-bold bg-white text-indigo-600 shadow-sm transition-all";
}
// 筛选逻辑 (Visual Only)
function activateFilter(chip) {
Array.from(chip.parentElement.children).forEach(c => c.classList.remove('active', 'bg-eff6ff', 'text-2563eb', 'border-bfdbfe', 'font-600'));
chip.classList.add('active');
}
// 弹窗逻辑
function openAgentModal() { document.getElementById('modal-agent').classList.remove('hidden'); }
function closeModal() { document.getElementById('modal-agent').classList.add('hidden'); }
function openSettings() { document.getElementById('modal-settings').classList.remove('hidden'); }
function closeSettings() { document.getElementById('modal-settings').classList.add('hidden'); }
// 用户菜单逻辑
function toggleUserMenu() { document.getElementById('user-menu').classList.toggle('hidden'); }
function openChangePassword() {
toggleUserMenu();
document.getElementById('modal-password').classList.remove('hidden');
}
function closeChangePassword() { document.getElementById('modal-password').classList.add('hidden'); }
// 点击外部关闭菜单
document.addEventListener('click', function(e) {
const menu = document.getElementById('user-menu');
const avatar = document.querySelector('[onclick="toggleUserMenu()"]');
if (!menu.classList.contains('hidden') && !menu.contains(e.target) && !avatar.contains(e.target)) {
menu.classList.add('hidden');
}
});
// 批量选择
function toggleRowSelection(checkbox) { checkbox.checked ? checkbox.closest('tr').classList.add('selected') : checkbox.closest('tr').classList.remove('selected'); updateBatchToolbar(); }
function toggleSelectAll(mainCheckbox) { document.querySelectorAll('.row-checkbox').forEach(cb => { cb.checked = mainCheckbox.checked; toggleRowSelection(cb); }); }
function updateBatchToolbar() {
const count = document.querySelectorAll('.row-checkbox:checked').length;
const toolbar = document.getElementById('batch-toolbar');
document.getElementById('selected-count').innerText = count;
count > 0 ? toolbar.classList.remove('hidden') : toolbar.classList.add('hidden');
}
function clearSelection() { document.querySelectorAll('.row-checkbox').forEach(cb => cb.checked = false); document.querySelectorAll('tr').forEach(tr => tr.classList.remove('selected')); document.querySelector('thead input').checked = false; updateBatchToolbar(); }
function selectRowAndOpenModal(btn) { clearSelection(); const cb = btn.closest('tr').querySelector('.row-checkbox'); cb.checked = true; toggleRowSelection(cb); openAgentModal(); }
// 模拟运行
function runBatchTask() {
closeModal();
const btn = document.querySelector('.status-pending button');
if(btn) {
btn.innerHTML = `<i data-lucide="loader-2" class="w-3 h-3 animate-spin"></i> 运行中`;
btn.className = "text-indigo-600 font-bold px-3 py-1.5 text-xs flex items-center gap-1";
}
clearSelection();
alert("任务已提交!");
}
function handleFileUpload(input) {
if(input.files.length) alert(`已选择 ${input.files.length} 个文件`);
}
</script>
</body>
</html>

View File

@@ -0,0 +1,135 @@
# **智能期刊审稿系统 MVP 产品需求文档**
| 项目属性 | 内容 |
| :---- | :---- |
| **项目名称** | 智能期刊审稿辅助系统 (SmartReview MVP) |
| **文档版本** | V1.0 (MVP) |
| **优先级** | P0 (核心功能闭环) |
| **目标用户** | 期刊初审编辑 |
## **1\. 产品目标 (Product Goal)**
打造一个\*\*“开箱即用”\*\*的智能审稿工具。编辑上传稿件,系统自动运行双重检查(规范性+方法学),输出可供参考的审稿报告。
核心指标:上传到出报告 \< 2分钟规范性问题检出率 \> 80%。
## **2\. 功能范围 (Scope)**
### **✅ MVP 包含 (In Scope)**
1. **账户体系**:简单的账号登录,支持期刊 Logo 定制。
2. **文件处理**:批量上传 Word/PDF列表展示状态。
3. **审查流程**:用户手动选择智能体发起审查,后台异步运行。
4. **规范性审查**覆盖字数、摘要结构、参考文献、图片DPI等硬指标。
5. **方法学审查**PICO 提取、研究类型识别、基础统计逻辑校验。
6. **报告与导出**:双视图报告展示,支持 PDF 导出。
### **❌ MVP 不包含 (Out of Scope)**
1. 在线文档编辑器 (Web Office)。
2. 复杂的角色权限管理系统。
3. 邮件自动发送功能。
4. 高级历史归档与检索系统。
5. 前端可视化的 Prompt 配置后台。
## **3\. 详细功能需求 (Requirements)**
### **3.1 登录页 (Login)**
* **功能**:账号密码验证。
* **UI**
* 左/上侧展示期刊 Logo 与名称(支持配置)。
* 输入框:账号、密码。
* 登录按钮:点击校验,失败提示“账号或密码错误”。
* **逻辑**Session 保持 24 小时,避免频繁登录。
### **3.2 审稿工作台 (Dashboard)**
这是系统的主界面,采用\*\*“宽表”\*\*布局。
#### **3.2.1 顶部操作区**
* **品牌区**展示“XX杂志社智能审稿系统”。
* **筛选栏**
* 状态 (全部/待处理/已完成)。
* 时间 (今天/近7天)。
* **上传按钮**
* 点击触发文件选择器。
* 支持多选。
* 支持扩展名:.docx, .pdf。
* 限制:单文件 \< 50MB。
#### **3.2.2 稿件列表**
列表包含以下列:
* 复选框:用于批量操作。
* 文件信息:文件名(点击进入详情)、大小、上传时间。
* 审稿维度:展示 Tags规范性、方法学
* 状态:
* **待处理**:刚上传,未运行。
* **运行中**:展示动态 Loading 图标。
* **已完成**
* 若跑了规范性,显示分数(如:绿色 "90分")。
* 若跑了方法学,显示结论(如:黄色 "存疑")。
* 操作列:
* \[开始审查\]:针对待处理状态。
* \[查看报告\]:针对已完成状态。
#### **3.2.3 任务发起弹窗 (The Launcher)**
* **触发**:点击列表中的“开始审查”或底部批量操作栏的“运行”。
* **内容**
* 标题:“发起智能审查”。
* 复选框组:
* \[x\] 稿约规范性智能体 (默认选中)。
* \[ \] 方法学统计智能体。
* **逻辑**:点击确定后,后端创建任务,列表状态变为“运行中”。
### **3.3 审稿详情页 (Report Detail)**
#### **3.3.1 头部信息**
* 展示文件名、作者若能提取、版本标签MVP可暂不显示
* 按钮:\[下载原稿\]、\[导出报告PDF\]。
#### **3.3.2 视图 A稿约规范性报告**
* **总评卡片**:展示总分 (0-100) 和 结论 (Pass/Fail)。
* **检查项列表**
* **文题字数**:提取字数,对比标准(如 \<20字
* **摘要结构**:正则匹配“目的/方法/结果/结论”关键词。
* **参考文献**:识别引用格式错误。
* **图片质量**:显示低分辨率图片的页码和 DPI 值。
* **交互**:每个报错项下方提供“建议修改意见”,支持点击复制。
#### **3.3.3 视图 B方法学评估报告**
* **PICO 卡片**:结构化展示提取出的 P/I/C/O 内容。
* **逻辑推理区**
* 展示模型对“研究类型”的判断(如:回顾性队列研究)。
* 展示“统计方法”的合理性分析(如:发现多组比较未用 ANOVA
* **红绿灯**
* 🔴 **错误**:明确的逻辑硬伤。
* 🟡 **存疑**:模型不确定,需人工复核。
* 🟢 **通过**:逻辑自洽。
## **4\. 技术与性能要求 (Non-functional)**
1. **响应速度**
* 列表加载 \< 1秒。
* 文件上传速度取决于带宽,需有进度反馈。
2. **并发处理**
* 支持至少 5 个任务并发运行(不需要排队太久)。
3. **兼容性**
* 优先支持 Chrome / Edge 浏览器。
4. **数据安全**
* 稿件文件存储需加密或隔离。
* 审稿报告仅授权账号可见。
## **5\. 验收标准 (Acceptance Criteria)**
1. **流程通**:用户能成功上传 5 个 PDF勾选双模型运行等待 3 分钟内,状态全部变为“已完成”。
2. **报告准**
* 上传一篇故意删掉“摘要结论”的稿件,规范性智能体必须报错。
* 上传一篇故意混淆“t检验”和“卡方检验”的稿件方法学智能体必须报“存疑”或“错误”。
3. **无崩溃**:连续上传 20 个文件,系统不卡死,不白屏。

View File

@@ -0,0 +1,829 @@
# 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

View File

@@ -766,3 +766,4 @@ docker exec redcap-apache php /tmp/create-redcap-password.php

View File

@@ -148,3 +148,4 @@ AIclinicalresearch/redcap-docker-dev/

View File

@@ -883,5 +883,6 @@ ACR镜像仓库

View File

@@ -1370,5 +1370,6 @@ SAE应用配置:

View File

@@ -1186,5 +1186,6 @@ docker exec -e PGPASSWORD="密码" ai-clinical-postgres psql -h RDS地址 -U air

View File

@@ -597,5 +597,6 @@ scripts/*.ts

View File

@@ -286,4 +286,5 @@ Node.js后端部署成功后

View File

@@ -509,4 +509,5 @@ Node.js后端 (SAE) ← http://172.17.173.88:3001

View File

@@ -224,4 +224,5 @@ curl http://localhost:3001/health

View File

@@ -262,4 +262,5 @@ npm run dev

View File

@@ -486,4 +486,5 @@ pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432

View File

@@ -1814,4 +1814,5 @@ curl http://8.140.53.236/

View File

@@ -362,4 +362,5 @@ crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-se

View File

@@ -684,4 +684,5 @@ docker login --username=gofeng117@163.com \

View File

@@ -494,5 +494,6 @@ NAT网关成本¥100/月,对初创团队是一笔开销

View File

@@ -399,5 +399,6 @@ curl http://你的SAE地址:3001/health

View File

@@ -731,5 +731,6 @@ const job = await queue.getJob(jobId);

View File

@@ -498,5 +498,6 @@ processLiteraturesInBackground(task.id, projectId, testLiteratures);

View File

@@ -975,5 +975,6 @@ ROI = (¥22,556 - ¥144) / ¥144 × 100% = 15,564%

View File

@@ -1032,5 +1032,6 @@ Redis 实例¥500/月

View File

@@ -490,5 +490,6 @@ import { ChatContainer } from '@/shared/components/Chat';

View File

@@ -410,3 +410,4 @@ frontend-v2/src/modules/pkb/

View File

@@ -272,3 +272,4 @@ npm run dev

View File

@@ -787,3 +787,4 @@ AIA智能问答模块

View File

@@ -932,3 +932,4 @@ CREATE INDEX idx_rvw_tasks_created_at ON rvw_schema.review_tasks(created_at);

View File

@@ -585,3 +585,4 @@ const typography = {

View File

@@ -897,3 +897,4 @@ app.use('/api/v1/knowledge', (req, res) => {

View File

@@ -211,3 +211,4 @@ rm -rf src/modules/pkb

View File

@@ -386,3 +386,4 @@ GET /api/v2/pkb/batch-tasks/batch/templates

View File

@@ -30,3 +30,4 @@ import pkbRoutes from './modules/pkb/routes/index.js';

View File

@@ -299,3 +299,4 @@ backend/

View File

@@ -510,3 +510,4 @@ const response = await fetch('/api/v2/pkb/batch-tasks/batch/execute', {