基于Spring Boot与Vue.js的住院病历档案管理系统开发实战

一、系统架构设计

本系统采用前后端分离架构,后端基于Spring Boot 2.7.15,前端基于Vue.js 3.3.4,数据库使用MySQL 8.0。系统需实现病历档案的数字化管理、权限控制、数据加密存储与检索功能。

1.1 技术栈选型

  • 后端框架:Spring Boot 2.7.15
  • 安全框架:Spring Security + JWT
  • 数据库:MySQL 8.0
  • ORM框架:MyBatis Plus 3.5.3.1
  • 前端框架:Vue.js 3.3.4 + Element Plus 2.3.8
  • 构建工具:Maven 3.8.6 + Node.js 18.17.0

二、开发环境搭建

2.1 后端环境配置

创建Spring Boot项目,pom.xml核心依赖配置如下:

``` org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security com.baomidou mybatis-plus-boot-starter 3.5.3.1 mysql mysql-connector-java 8.0.33 io.jsonwebtoken jjwt 0.9.1 ```

application.yml数据库配置:

``` spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/medical_record?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: your_password jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl ```

2.2 前端环境配置

创建Vue项目并安装依赖:

``` npm create vue@latest medical-record-frontend cd medical-record-frontend npm install element-plus@2.3.8 npm install axios@1.5.0 npm install vue-router@4.2.4 npm install pinia@2.1.6 ```

main.js全局配置:

``` import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import App from './App.vue' import router from './router' import { createPinia } from 'pinia' const app = createApp(App) app.use(ElementPlus) app.use(router) app.use(createPinia()) app.mount('app') ```

三、数据库设计

3.1 核心表结构

创建病历主表:

``` CREATE TABLE medical_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID', record_no VARCHAR(50) NOT NULL UNIQUE COMMENT '病历编号', patient_id BIGINT NOT NULL COMMENT '患者ID', patient_name VARCHAR(50) NOT NULL COMMENT '患者姓名', id_card VARCHAR(18) COMMENT '身份证号', admission_time DATETIME NOT NULL COMMENT '入院时间', discharge_time DATETIME COMMENT '出院时间', department VARCHAR(50) COMMENT '科室', attending_doctor VARCHAR(50) COMMENT '主治医生', diagnosis TEXT COMMENT '诊断结果', treatment TEXT COMMENT '治疗方案', record_status TINYINT DEFAULT 1 COMMENT '病历状态:1-在院,2-已出院,3-归档', created_by VARCHAR(50) COMMENT '创建人', created_time DATETIME DEFAULT CURRENT_TIMESTAMP, updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_patient_id (patient_id), INDEX idx_record_no (record_no), INDEX idx_admission_time (admission_time) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='病历主表'; ```

基于Spring Boot与Vue.js的住院病历档案管理系统开发实战

创建病历附件表:

``` CREATE TABLE record_attachment ( id BIGINT PRIMARY KEY AUTO_INCREMENT, record_id BIGINT NOT NULL COMMENT '病历ID', file_name VARCHAR(255) NOT NULL COMMENT '文件名', file_path VARCHAR(500) NOT NULL COMMENT '文件存储路径', file_type VARCHAR(50) COMMENT '文件类型', file_size BIGINT COMMENT '文件大小(字节)', upload_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (record_id) REFERENCES medical_record(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='病历附件表'; ```

四、后端核心功能实现

4.1 实体类定义

MedicalRecord实体类:

``` @Data @TableName("medical_record") public class MedicalRecord { @TableId(type = IdType.AUTO) private Long id; private String recordNo; private Long patientId; private String patientName; private String idCard; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date admissionTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date dischargeTime; private String department; private String attendingDoctor; private String diagnosis; private String treatment; private Integer recordStatus; @TableField(fill = FieldFill.INSERT) private String createdBy; @TableField(fill = FieldFill.INSERT) private Date createdTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updatedTime; } ```

4.2 数据访问层

创建Mapper接口:

``` @Mapper public interface MedicalRecordMapper extends BaseMapper { @Select("SELECT FROM medical_record WHERE patient_name LIKE CONCAT('%', {name}, '%')") List selectByPatientName(@Param("name") String name); @Select("SELECT FROM medical_record WHERE admission_time BETWEEN {start} AND {end}") List selectByTimeRange(@Param("start") Date start, @Param("end") Date end); } ```

4.3 服务层实现

病历服务实现类:

``` @Service public class MedicalRecordServiceImpl extends ServiceImpl implements MedicalRecordService { @Autowired private MedicalRecordMapper recordMapper; @Override public Page queryByPage(PageQuery query) { Page page = new Page<>(query.getPageNum(), query.getPageSize()); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); if (StringUtils.hasText(query.getPatientName())) { wrapper.like(MedicalRecord::getPatientName, query.getPatientName()); } if (query.getStartTime() != null) { wrapper.ge(MedicalRecord::getAdmissionTime, query.getStartTime()); } if (query.getEndTime() != null) { wrapper.le(MedicalRecord::getAdmissionTime, query.getEndTime()); } wrapper.orderByDesc(MedicalRecord::getAdmissionTime); return recordMapper.selectPage(page, wrapper); } @Override @Transactional public boolean saveRecord(MedicalRecord record) { // 生成病历编号:年份+月份+6位序列号 String recordNo = generateRecordNo(); record.setRecordNo(recordNo); record.setRecordStatus(1); // 默认在院状态 return this.save(record); } private String generateRecordNo() { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM"); String prefix = sdf.format(new Date()); // 查询当月最大编号 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.likeRight(MedicalRecord::getRecordNo, prefix); wrapper.orderByDesc(MedicalRecord::getRecordNo); wrapper.last("LIMIT 1"); MedicalRecord lastRecord = this.getOne(wrapper); if (lastRecord == null) { return prefix + "000001"; } String lastNo = lastRecord.getRecordNo(); int sequence = Integer.parseInt(lastNo.substring(6)) + 1; return prefix + String.format("%06d", sequence); } } ```

4.4 文件上传接口

文件上传控制器:

``` @RestController @RequestMapping("/api/attachment") public class AttachmentController { @Value("${file.upload.path}") private String uploadPath; @PostMapping("/upload") public Result uploadFile(@RequestParam("file") MultipartFile file, @RequestParam Long recordId) throws IOException { if (file.isEmpty()) { return Result.error("文件不能为空"); } // 创建存储目录 File dir = new File(uploadPath); if (!dir.exists()) { dir.mkdirs(); } // 生成唯一文件名 String originalName = file.getOriginalFilename(); String fileExt = originalName.substring(originalName.lastIndexOf(".")); String fileName = UUID.randomUUID().toString() + fileExt; String filePath = uploadPath + File.separator + fileName; // 保存文件 file.transferTo(new File(filePath)); // 保存到数据库 RecordAttachment attachment = new RecordAttachment(); attachment.setRecordId(recordId); attachment.setFileName(originalName); attachment.setFilePath(filePath); attachment.setFileType(fileExt); attachment.setFileSize(file.getSize()); attachmentService.save(attachment); return Result.success("上传成功", attachment); } } ```

五、前端核心功能实现

5.1 病历列表页面

MedicalRecordList.vue核心代码:

``` ```

5.2 病历详情页面

MedicalRecordDetail.vue文件上传组件:

```