1. 项目概述
这个基于SSM框架的社区医疗服务系统是我在2023年完成的一个毕业设计项目,主要面向中小型医疗机构提供信息化管理解决方案。作为一名Java全栈开发者,我在这个项目中完整实践了从需求分析到系统部署的全流程开发。
系统采用经典的B/S架构,前端使用Vue.js+ElementUI构建响应式界面,后端基于Spring+SpringMVC+MyBatis框架组合,数据库选用MySQL 5.7。整个开发周期约4个月,最终实现了包括科室管理、医生排班、电子病历等六大核心模块,代码量达到2万余行。
提示:在医疗系统开发中,数据安全和业务流程合规性是首要考虑因素。本系统在设计时特别注重患者隐私保护和医疗规范符合性。
2. 技术选型与架构设计
2.1 技术栈决策过程
选择SSM框架组合主要基于以下考量:
- Spring框架:提供了完善的IoC容器和AOP支持,特别适合处理医疗系统中复杂的业务逻辑依赖关系。通过声明式事务管理,确保处方开具、费用结算等关键操作的原子性。
- SpringMVC:清晰的MVC分层使前端请求与后端业务逻辑解耦。我们配置了自定义拦截器链,用于处理医疗数据访问权限验证。
- MyBatis:相比Hibernate更灵活的SQL控制能力,便于优化复杂病历查询性能。通过TypeHandler实现了敏感字段的自动加解密。
数据库选型时对比了MySQL和PostgreSQL:
- MySQL 5.7的JSON类型支持便于存储非结构化的病历数据
- 内置的binlog为后续实现数据同步分析提供了便利
- 社区版零成本符合中小医院预算限制
2.2 系统架构详解
采用分层架构设计:
code复制表现层:Vue.js + ElementUI
↑
应用层:SpringMVC (RESTful API)
↑
业务层:Spring Service (事务管理)
↑
持久层:MyBatis + MySQL
↑
基础设施:Redis缓存 + 阿里云OSS(医疗影像存储)
关键设计决策:
- 使用DTO模式隔离领域模型与接口契约
- 采用贫血模型设计领域对象,业务逻辑集中在Service层
- 查询复杂病历数据时引入CQRS模式分离读写操作
3. 核心模块实现
3.1 科室管理模块
数据库设计要点:
sql复制CREATE TABLE `department` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL COMMENT '科室名称',
`type` ENUM('临床','医技','行政') NOT NULL,
`parent_id` INT DEFAULT NULL COMMENT '上级科室ID',
`leader_id` INT COMMENT '科室负责人ID',
`status` TINYINT DEFAULT 1,
PRIMARY KEY (`id`),
FOREIGN KEY (`parent_id`) REFERENCES `department`(`id`),
INDEX `idx_type` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
实现难点:
- 科室树形结构展示:采用MP的@TableField注解处理父子关系
- 科室医生统计:使用MyBatis的@SelectProvider实现动态SQL
java复制public List<DepartmentVO> getDepartmentTree() {
List<Department> all = departmentMapper.selectList(null);
return buildTree(all, 0);
}
private List<DepartmentVO> buildTree(List<Department> list, Integer parentId) {
return list.stream()
.filter(d -> Objects.equals(d.getParentId(), parentId))
.map(d -> {
DepartmentVO vo = convertToVO(d);
vo.setChildren(buildTree(list, d.getId()));
return vo;
}).collect(Collectors.toList());
}
3.2 电子病历模块
采用DDD领域驱动设计:
- 值对象:Diagnosis(诊断)、TreatmentPlan(治疗方案)
- 聚合根:MedicalRecord
- 领域服务:RecordTemplateService
病历结构化存储方案:
java复制public class MedicalRecord {
private Long id;
private String chiefComplaint; // 主诉
private String presentIllness; // 现病史
private List<Diagnosis> diagnoses; // 诊断列表
@TableField(typeHandler = JsonTypeHandler.class)
private Map<String, Object> extraData; // 扩展字段
}
注意:病历修改需要保留完整操作日志,我们通过MyBatis插件实现了自动审计日志记录。
4. 安全与性能优化
4.1 数据安全方案
- 敏感字段加密:
java复制@TableField(typeHandler = AESEncryptHandler.class)
private String idCardNumber;
-
权限控制矩阵:
| 角色 | 病历读取 | 病历修改 | 处方开具 |
|------------|---------|---------|---------|
| 普通医生 | 自己的患者 | 自己的患者 | √ |
| 科室主任 | 本科室 | × | × |
| 医务管理员 | 全部 | × | × | -
审计日志实现:
xml复制<plugin interceptor="com.medical.audit.AuditLogInterceptor">
<property name="ignoreMethods" value="select*,get*"/>
</plugin>
4.2 性能优化实践
- 门诊高峰应对:
- 使用Redis缓存科室排班表
- 热点数据预加载:每日8:00定时加载当天预约数据
- 采用Sentinel实现挂号接口限流
- SQL优化案例:
sql复制-- 优化前
SELECT * FROM prescription WHERE patient_id = ? AND status = 1;
-- 优化后
SELECT id, created_time FROM prescription
WHERE patient_id = ? AND status = 1
ORDER BY created_time DESC LIMIT 10;
- 连接池配置:
properties复制# Druid配置
spring.datasource.druid.initial-size=5
spring.datasource.druid.max-active=20
spring.datasource.druid.query-timeout=3000
5. 部署与测试
5.1 持续集成方案
采用GitLab CI实现自动化部署:
yaml复制stages:
- test
- build
- deploy
unit_test:
stage: test
script:
- mvn test -Pci
package:
stage: build
script:
- mvn package -DskipTests
artifacts:
paths:
- target/*.war
deploy_prod:
stage: deploy
only:
- master
script:
- scp target/medical.war user@prod:/opt/tomcat/webapps/
5.2 压力测试结果
使用JMeter模拟100并发:
- 挂号接口:平均响应时间<500ms
- 病历查询:TPS达到120/sec
- 处方提交:数据库事务成功率100%
关键指标监控:
- Tomcat线程池使用率<70%
- MySQL连接数峰值15/20
- JVM内存占用稳定在60%以下
6. 开发经验总结
- 医疗业务理解:
- 建议先绘制业务泳道图,明确各角色协作流程
- 与临床医生保持沟通,我们曾因误解"三查七对"规则导致处方流程修改3次
- 技术实践心得:
- MyBatis的
标签处理一对多关系比多次查询效率高30% - Spring的@Retryable注解可优雅处理医保接口的瞬时失败
- Vue的keep-alive缓存科室选择组件可提升用户体验
- 踩坑记录:
- 病历版本控制最初采用全量存储,后改为diff算法节省70%空间
- 未考虑医生交接班场景,导致排班冲突,后增加重叠时间校验
- 药品库存未实现乐观锁,出现过超卖情况
这个项目让我深刻体会到医疗系统开发的特殊性 - 既要保证技术实现的严谨性,又要符合医疗行业的规范要求。后续如果继续开发,我会考虑引入医疗知识图谱来增强诊断辅助功能,以及采用微服务架构更好地支持多院区协同。