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
12 KiB
ASL模块 - Week 2 Day 2 完成报告
日期: 2025-11-19
任务: 文献导入页 + Excel模板下载
状态: ✅ 100% 完成
📋 今日目标回顾
根据Week 2开发计划,Day 2的主要目标是:
- ✅ Excel模板生成与下载
- ✅ Excel上传与内存解析
- ✅ 字段验证与去重
- ✅ 文献预览表格
- ✅ 完整提交流程(创建项目+导入文献)
🎯 核心成果
1. Excel工具函数模块 ⭐⭐⭐⭐⭐
文件: frontend-v2/src/modules/asl/utils/excelUtils.ts
包含功能:
- ✅
downloadExcelTemplate()- 生成并下载Excel模板 - ✅
parseExcelFile()- 内存解析Excel文件 - ✅
validateLiterature()- 验证单条文献 - ✅
validateLiteratures()- 批量验证文献 - ✅
deduplicateLiteratures()- 去重逻辑(DOI + Title) - ✅
processExcelFile()- 完整处理流程
技术亮点:
// 内存解析,不落盘(云原生要求)
const buffer = await data.toBuffer();
const workbook = XLSX.read(buffer, { type: 'array' });
// 智能去重(优先DOI,其次Title)
if (lit.doi && lit.doi.trim() !== '') {
key = `doi:${lit.doi.toLowerCase().trim()}`;
} else {
key = `title:${lit.title.toLowerCase().replace(/\s+/g, '')}`;
}
2. 完善的"设置与启动"页面 ⭐⭐⭐⭐⭐
文件: frontend-v2/src/modules/asl/pages/TitleScreeningSettings.tsx
完整功能流程:
用户填写PICOS标准
↓
上传Excel文件
↓
前端内存解析 + 验证 + 去重
↓
显示预览表格 + 统计信息
↓
点击"开始AI初筛"
↓
创建项目 → 导入文献 → 跳转工作台
UI组件:
- ✅ Excel Dragger上传组件
- ✅ Excel模板下载按钮
- ✅ 解析统计信息卡片(4个Statistic组件)
- ✅ 文献预览表格(分页、Tooltip、省略)
- ✅ 错误信息Alert
- ✅ 启动按钮(带loading状态)
状态管理:
const [fileList, setFileList] = useState<UploadFile[]>([]);
const [literatures, setLiteratures] = useState<LiteratureData[]>([]);
const [parseStats, setParseStats] = useState<ParseStatistics | null>(null);
const [isUploading, setIsUploading] = useState(false);
const [canStart, setCanStart] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
3. API接口优化 ⭐⭐⭐
文件: frontend-v2/src/modules/asl/api/index.ts
修改内容:
- ✅ 修改
importLiteratures签名,接受{projectId, literatures}对象 - ✅ 导出统一的
aslApi对象,便于组件引用
使用示例:
import { aslApi } from '../api';
// 创建项目
const response = await aslApi.createProject({...});
// 导入文献
await aslApi.importLiteratures({
projectId,
literatures: [...],
});
🔧 技术实现细节
1. Excel模板生成
包含两个工作表:
-
文献列表:
- 3行示例数据
- 7列(Title, Abstract, PMID, Authors, Journal, Year, DOI)
- 自动设置列宽
-
字段说明:
- 每个字段的说明
- 必填/可选标记
- 使用建议
代码片段:
const wb = XLSX.utils.book_new();
const ws = XLSX.utils.json_to_sheet(templateData);
ws['!cols'] = [
{ wch: 60 }, // Title
{ wch: 80 }, // Abstract
// ...
];
XLSX.utils.book_append_sheet(wb, ws, '文献列表');
XLSX.writeFile(wb, '文献导入模板.xlsx');
2. 内存解析(云原生)
遵循云原生开发规范:
- ✅ 使用
FileReader.readAsArrayBuffer()读取文件 - ✅ 使用
XLSX.read(buffer, { type: 'array' })内存解析 - ✅ 不落盘,直接在内存中处理
字段映射(支持中英文):
title: row.Title || row.title || row['标题'] || ''
abstract: row.Abstract || row.abstract || row['摘要'] || ''
pmid: row.PMID || row.pmid || row['PMID编号'] || ''
// ...
3. 数据验证
验证规则:
title: 必填,至少10个字符abstract: 必填,至少50个字符- 其他字段: 可选
错误提示:
if (!lit.title || lit.title.length < 10) {
errors.push('标题太短(至少10个字符)');
}
4. 智能去重
去重策略:
- 优先级1: DOI(如果存在)
- 标准化:转小写、去空格
- 优先级2: Title
- 标准化:转小写、去除所有空白字符
代码逻辑:
const seen = new Map<string, LiteratureData>();
for (const lit of literatures) {
let key: string;
if (lit.doi && lit.doi.trim() !== '') {
key = `doi:${lit.doi.toLowerCase().trim()}`;
} else {
key = `title:${lit.title.toLowerCase().replace(/\s+/g, '')}`;
}
if (!seen.has(key)) {
seen.set(key, lit);
unique.push(lit);
} else {
duplicates.push(lit);
}
}
5. 文献预览表格
功能特性:
- ✅ 分页显示(50条/页)
- ✅ 标题和摘要超长省略(Tooltip显示全文)
- ✅ 响应式布局(scroll x)
- ✅ 序号自动计算
列定义:
const columns = [
{ title: '#', width: 60, render: (_, __, index) => index + 1 },
{ title: '标题', width: '35%', ellipsis: true },
{ title: '摘要', width: '30%', ellipsis: true, render: truncate },
{ title: 'PMID', width: 100 },
{ title: '年份', width: 80 },
{ title: '作者', ellipsis: true },
];
6. 统计信息展示
4个统计指标:
<Row gutter={16}>
<Col span={6}>
<Statistic title="总数" value={total} prefix={<CheckCircle />} />
</Col>
<Col span={6}>
<Statistic title="有效" value={afterDedup} valueStyle={{ color: '#3f8600' }} />
</Col>
<Col span={6}>
<Statistic title="重复" value={duplicates} valueStyle={{ color: '#faad14' }} />
</Col>
<Col span={6}>
<Statistic title="无效" value={invalid} valueStyle={{ color: '#cf1322' }} />
</Col>
</Row>
📊 完整交互流程
[用户进入页面]
↓
[填写PICOS表单] ← 必填(P, I, C, S, 纳入, 排除)
↓
[选择筛选风格] ← Radio(lenient/standard/strict)
↓
[点击"下载Excel模板"] ← 可选
↓ 下载包含示例的Excel模板
↓
[上传Excel文件] ← Dragger组件
↓
[前端自动解析] ← xlsx.read()
↓
├─ 字段映射(中英文兼容)
├─ 数据验证(title + abstract必填)
└─ 去重逻辑(DOI → Title)
↓
[显示统计信息] ← 4个Statistic卡片
├─ 总数: 100篇
├─ 有效: 95篇(绿色)
├─ 重复: 3篇(橙色)
└─ 无效: 2篇(红色)
↓
[显示文献预览表格] ← Table + Pagination
↓
[用户确认无误]
↓
[点击"开始AI初筛"] ← Button(disabled如果未完成)
↓
[Loading: "正在创建项目..."]
↓
[API: createProject()] ← 获得projectId
↓
[Loading: "正在导入文献..."]
↓
[API: importLiteratures({ projectId, literatures })]
↓
[Success: "项目创建成功!"]
↓
[自动跳转] → /literature/screening/title/workbench
🎨 UI/UX亮点
1. 友好的错误提示
分层提示:
- ✅ 文件解析错误 → 红色Message
- ✅ 数据验证失败 → 橙色Alert(显示前5条错误)
- ✅ 重复数据 → 蓝色Info Message
- ✅ 无效数据 → 橙色Warning Message
示例:
if (statistics.invalid > 0) {
message.warning(`有 ${statistics.invalid} 条数据验证失败,已自动过滤`, 3);
}
if (statistics.duplicates > 0) {
message.info(`检测到 ${statistics.duplicates} 条重复数据,已自动去重`, 3);
}
2. 优雅的Loading状态
三种Loading:
- 解析中:
message.loading('正在解析Excel文件...') - 创建项目:
message.loading('正在创建项目...') - 导入文献:
message.loading('正在导入文献...')
Button状态:
<Button
type="primary"
size="large"
loading={isSubmitting}
disabled={!canStart}
>
{isSubmitting ? '正在创建项目并导入文献...' : '开始AI标题摘要初筛'}
</Button>
3. 渐进式启用
启动按钮激活条件:
setCanStart(formValid && valid.length > 0);
- ❌ 表单未填写 → 按钮禁用
- ❌ 文献未导入 → 按钮禁用
- ✅ 表单+文献都完成 → 按钮启用
提示信息:
{!canStart && literatures.length === 0 && (
<Alert
message="请先完成以上步骤"
description="填写PICOS标准、纳入/排除标准,并导入文献后,即可开始筛选"
type="warning"
/>
)}
📦 新增/修改的文件
新增文件 ✅
frontend-v2/src/modules/asl/utils/excelUtils.ts- 366行代码
- 完整的Excel处理工具库
修改文件 ✅
-
frontend-v2/src/modules/asl/pages/TitleScreeningSettings.tsx- 从占位页面 → 完整功能页面
- 新增597行代码
-
frontend-v2/src/modules/asl/api/index.ts- 修改
importLiteratures函数签名 - 新增
aslApi统一导出对象
- 修改
依赖安装 ✅
npm install xlsx
# 新增依赖:xlsx@^0.18.5
✅ 完成标准验证
功能完整性 ✅
- Excel模板下载功能
- Excel上传与解析(内存,不落盘)
- 字段验证(title + abstract必填)
- 去重逻辑(DOI + Title)
- 文献预览表格
- 统计信息展示
- "开始AI初筛"按钮可用
- 一次性创建项目+导入文献
代码质量 ✅
- 无TypeScript类型错误
- 无ESLint警告
- 符合React最佳实践
- 完整的错误处理
- 友好的用户提示
云原生要求 ✅
- 内存解析Excel(不落盘)
- 使用环境变量配置
- 无同步阻塞操作
- 完整的异步处理
🧪 测试建议
测试场景
-
正常流程:
- 下载模板 → 填写数据 → 上传 → 预览 → 启动
-
异常场景:
- 上传空文件
- 上传非Excel文件
- 必填字段缺失
- 数据格式错误
- 重复数据处理
-
边界场景:
- 1篇文献
- 500+篇文献
- 中文列名
- 英文列名
- 混合列名
📝 使用说明
1. 下载模板
点击"下载Excel模板"按钮
→ 自动下载"文献导入模板.xlsx"
→ 包含2个工作表:
├─ 文献列表(带示例)
└─ 字段说明
2. 填写数据
必填字段:
Title: 文献标题(至少10字符)Abstract: 文献摘要(至少50字符)
可选字段:
PMID: PubMed IDAuthors: 作者列表Journal: 期刊名称Year: 发表年份DOI: DOI编号(用于去重)
3. 上传解析
点击或拖拽Excel文件到上传区域
→ 自动解析(2-5秒)
→ 显示统计信息和预览表格
→ 如有错误,显示详细提示
4. 启动筛选
确认数据无误后
→ 点击"开始AI标题摘要初筛"
→ 自动创建项目
→ 导入所有文献
→ 跳转到审核工作台
🚀 下一步计划
Week 2 Day 3-4(明天开始)
主要任务: 审核工作台(双行表格)
预计实现:
- 双行表格UI(主行 + 展开行)
- 两个模型的判断结果对比
- 冲突高亮显示
- PICO判断详情
- 双视图原文审查Modal
- 人工复核功能
参考原型: docs/03-业务模块/ASL-AI智能文献/03-UI设计/AI智能文献-标题摘要初筛原型.html
💡 经验总结
技术亮点 ⭐
- 内存解析:完全符合云原生要求,无文件落盘
- 智能去重:DOI优先级 + Title兜底,实用性强
- 渐进式交互:表单验证 + 文献导入 → 启用按钮
- 完整错误处理:分层提示,用户体验好
改进空间
- 大文件优化:目前未限制文件大小,可考虑添加
- 解析进度条:对于超大文件,可显示解析进度
- Excel格式检查:可提前检查列名是否匹配
- 批量操作:表格可支持批量删除无效行
📊 开发统计
| 指标 | 数值 |
|---|---|
| 新增代码 | ~1,000行 |
| 新增文件 | 1个 |
| 修改文件 | 2个 |
| 新增依赖 | 1个(xlsx) |
| 功能点 | 6个 |
| 开发时间 | 约2小时 |
| 完成度 | 100% ✅ |
🎉 Day 2 完成总结
✅ Excel模板生成与下载 - 完美实现
✅ Excel上传与解析 - 内存处理,云原生
✅ 数据验证与去重 - 智能去重,完整验证
✅ 文献预览表格 - 友好展示,分页支持
✅ 完整提交流程 - 一键启动,自动跳转
Day 2任务 100% 完成! 🎊
明天继续Week 2 Day 3-4开发! 💪
完成时间: 2025-11-19
下一个工作日: Week 2 Day 3