1. 项目背景与核心需求
医院档案管理系统是医疗机构信息化建设的重要组成部分。传统纸质档案管理存在查找效率低、存储空间占用大、易损毁丢失等问题。我们团队最近用SpringBoot+Vue技术栈实现了一套数字化解决方案,将患者病历、检验报告、影像资料等全部电子化归档,支持多维度检索和权限管控。
这个系统的核心价值在于:
- 实现档案全生命周期数字化管理(录入、归档、借阅、销毁)
- 通过OCR技术将历史纸质档案批量电子化
- 建立完善的权限体系和操作日志
- 支持与其他医疗系统(HIS、LIS等)数据对接
2. 技术架构设计
2.1 整体技术选型
采用前后端分离架构,技术栈组合经过多次压测验证:
- 后端:SpringBoot 2.7 + MyBatis-Plus 3.5
- 前端:Vue 3 + Element Plus + Axios
- 数据库:MySQL 8.0(主从架构)
- 中间件:Redis 6(缓存)、RabbitMQ(异步任务)
特别说明:选择MyBatis-Plus而非JPA是考虑到医院业务字段变更频繁,需要更灵活的SQL控制
2.2 关键架构决策
-
文件存储方案:
- 小文件(<10MB):直接存MySQL BLOB
- 大文件:MinIO对象存储+数据库存元数据
- 影像文件:专用PACS系统对接
-
检索优化:
- 基础字段:MySQL索引
- 全文检索:Elasticsearch 7.x
- 病历内容:结巴分词+倒排索引
-
安全设计:
- 传输层:HTTPS+国密SM4
- 数据层:敏感字段AES加密
- 审计:全操作日志+区块链存证
3. 核心功能实现
3.1 档案分类管理模块
采用树形结构存储科室-档案类型关系:
java复制@Entity
public class ArchiveCategory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String categoryName;
private Integer retentionYears; //保存年限
@ManyToOne
@JoinColumn(name = "parent_id")
private ArchiveCategory parent;
@OneToMany(mappedBy = "parent")
private Set<ArchiveCategory> children;
}
实现要点:
- 使用MP的@TableField注解处理树形查询
- 前端用el-tree组件展示
- 支持拖拽排序和批量导入
3.2 档案借阅流程
mermaid复制graph TD
A[发起申请] --> B{审批}
B -->|通过| C[生成借阅记录]
B -->|驳回| D[通知申请人]
C --> E[设置归还提醒]
E --> F[到期自动催还]
实际开发中用状态机实现:
java复制public enum BorrowStatus {
PENDING,
APPROVED,
REJECTED,
RETURNED,
OVERDUE
}
// 状态转换校验
public void changeStatus(BorrowStatus newStatus) {
if (!currentStatus.canTransferTo(newStatus)) {
throw new IllegalStateException("非法状态转换");
}
// ...
}
3.3 统计报表功能
采用动态SQL构建统计维度:
xml复制<select id="selectArchiveStats" resultType="ArchiveStatDTO">
SELECT
${groupField} AS dimension,
COUNT(*) AS total
FROM medical_archive
<where>
<if test="startDate != null">
AND create_time >= #{startDate}
</if>
<!-- 更多条件... -->
</where>
GROUP BY ${groupField}
</select>
前端配合ECharts实现可视化:
vue复制<template>
<div class="chart-container">
<el-select v-model="chartType" @change="refreshChart">
<el-option label="柱状图" value="bar"/>
<el-option label="饼图" value="pie"/>
</el-select>
<div ref="chart" style="height:400px"></div>
</div>
</template>
4. 性能优化实践
4.1 数据库优化
-
索引策略:
- 联合索引:
(patient_id, archive_type) - 全文索引:
medical_history_content - 空间索引:GIS地理位置查询
- 联合索引:
-
分库分表:
- 按科室水平分表
- 冷热数据分离(近3年数据单独存储)
4.2 缓存设计
采用多级缓存架构:
- 本地缓存(Caffeine):高频访问的基础数据
- Redis集群:
- String:单条档案详情
- ZSet:借阅排行榜
- Geo:按地理位置查询
缓存更新策略:
java复制@CacheEvict(value = "archiveDetail", key = "#archiveId")
public void updateArchive(MedicalArchive archive) {
// 先更新数据库
archiveMapper.updateById(archive);
// 异步更新ES索引
mqTemplate.send("archive.update", archive.getId());
}
5. 安全防护方案
5.1 权限控制实现
基于RBAC模型扩展:
sql复制CREATE TABLE `sys_role` (
`id` bigint NOT NULL AUTO_INCREMENT,
`role_name` varchar(50) NOT NULL COMMENT '角色名称',
`data_scope` tinyint NOT NULL COMMENT '数据权限范围:1全部 2本科室 3本人',
PRIMARY KEY (`id`)
);
CREATE TABLE `sys_role_menu` (
`role_id` bigint NOT NULL,
`menu_id` bigint NOT NULL,
PRIMARY KEY (`role_id`,`menu_id`)
);
前端权限指令:
javascript复制Vue.directive('permission', {
inserted(el, binding) {
if (!store.getters.permissions.includes(binding.value)) {
el.parentNode.removeChild(el);
}
}
})
5.2 审计日志设计
采用AOP记录关键操作:
java复制@Aspect
@Component
public class AuditLogAspect {
@AfterReturning(
pointcut = "@annotation(com.xxx.ArchivesAuditLog)",
returning = "result"
)
public void afterReturning(JoinPoint jp, Object result) {
// 获取操作信息
MethodSignature signature = (MethodSignature) jp.getSignature();
ArchivesAuditLog annotation = signature.getMethod()
.getAnnotation(ArchivesAuditLog.class);
// 构建日志实体
AuditLog log = new AuditLog();
log.setOperation(annotation.value());
log.setParams(JsonUtils.toJson(jp.getArgs()));
log.setResult(JsonUtils.toJson(result));
// 异步保存
auditLogQueue.add(log);
}
}
6. 部署与运维
6.1 容器化部署
Docker Compose编排方案:
yaml复制version: '3'
services:
app:
image: hospital-archive:${TAG}
ports:
- "8080:8080"
depends_on:
- redis
- mysql
environment:
- SPRING_PROFILES_ACTIVE=prod
mysql:
image: mysql:8.0
volumes:
- ./mysql/data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
6.2 监控方案
-
基础监控:Prometheus + Grafana
- JVM指标
- 接口响应时间
- 缓存命中率
-
业务监控:
- 档案积压告警
- 借阅超期预警
- 存储容量预警
7. 踩坑实录
7.1 MyBatis批量插入优化
初始方案:
java复制for (ArchiveItem item : list) {
mapper.insert(item); // 循环单条插入
}
优化后:
xml复制<insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO archive_item(xxx)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.field1}, #{item.field2})
</foreach>
</insert>
性能对比:
| 数据量 | 原方案耗时 | 优化后耗时 |
|---|---|---|
| 1000条 | 12.8s | 0.6s |
7.2 Vue大文件上传
采用分片上传方案:
javascript复制const chunkSize = 5 * 1024 * 1024; // 5MB
async function upload(file) {
const chunks = Math.ceil(file.size / chunkSize);
for (let i = 0; i < chunks; i++) {
const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize);
const formData = new FormData();
formData.append('chunk', chunk);
formData.append('index', i);
await axios.post('/upload', formData);
}
// 合并请求...
}
关键优化点:
- 断点续传(记录已上传分片)
- 并行上传(Promise.all)
- MD5校验文件完整性
8. 扩展方向
-
AI应用:
- 自然语言处理:病历结构化提取
- 图像识别:报告自动分类
- 预测分析:借阅需求预测
-
移动端适配:
- 微信小程序查档
- 电子签名审批
- 消息推送提醒
-
区块链存证:
- 关键操作上链
- 电子档案哈希存证
- 司法取证支持
