ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

SpringBoot+Vue医院急诊系统全栈开发实战:架构设计与核心模块解析

SpringBoot+Vue医院急诊系统全栈开发实战:架构设计与核心模块解析 简介本资源是一套完整的基于Spring Boot的医院急诊系统毕业设计级源码面向计算机专业本科生、Java全栈初学者及医疗信息化项目实践者解决急诊业务流程数字化、前后端分离开发与MySQL数据管理等典型需求。压缩包共852个文件含138个Java后端逻辑文件、50个Vue组件页面、153个JS交互脚本、44个CSS样式文件及1个建库SQL脚本辅以SVG图标、GIF动效与JPG/PNG素材完整覆盖系统启动、用户认证、分诊调度、病历管理等核心模块整体大小为17.7MB。已有67人学习下载资源附带详细部署说明文档并包含.bat启动脚本、.yml配置文件及.bak备份模板便于快速导入IDEA/Eclipse环境、执行Navicat数据库初始化与本地调试特别适合课程设计答辩与毕设二次开发。1. 项目概述与核心价值最近在整理过往项目资料时翻出了一个几年前深度参与开发的医院急诊系统源码。这套系统基于当时主流的 SpringBoot Vue 技术栈构建后端用 Java前端用 Vue数据库是 MySQL算是一个比较经典的“前后端分离”单体应用。虽然现在微服务、云原生概念满天飞但回过头看这个项目的架构设计和业务逻辑实现对于想深入理解医疗信息化、特别是急诊业务流程的开发者来说依然有很高的参考价值。它不是一个简单的“增删改查”Demo而是包含了从分诊、抢救、留观到费用结算的完整急诊闭环管理。如果你正在学习 SpringBoot 全栈开发或者对医疗软件感兴趣想了解一个真实业务系统是如何从设计到落地的这套代码和附带的说明文档应该能给你不少启发。急诊系统的核心价值在于“急”和“序”。它需要在极短的时间内处理信息不全、病情多变的患者同时确保救治流程规范、资源调度有序、信息记录准确。这对软件系统的实时性、稳定性和业务逻辑的严谨性提出了极高要求。我们当时的设计目标就是通过技术手段将急诊科的标准化作业流程SOP固化到系统中减少人为差错提升救治效率。2. 技术栈选型与架构设计解析2.1 为什么是 SpringBoot Vue MySQL这个技术组合在几年前乃至现在都是企业中快速开发业务系统的“黄金搭档”。选型背后的逻辑很实际后端 SpringBoot 它极大地简化了 Spring 应用的初始搭建和开发过程。急诊系统后台需要处理复杂的业务逻辑、权限控制、事务管理以及与各种硬件设备如监护仪、打印机的接口对接。SpringBoot 的“约定大于配置”理念让我们能快速集成 MyBatis数据访问、Spring Security安全、Redis缓存、RabbitMQ消息队列用于如检验报告推送等关键组件。内嵌的 Tomcat 服务器也省去了复杂的部署配置通过一个jar包就能独立运行这对于需要快速部署和更新的医院环境很友好。前端 Vue.js 急诊系统的前端界面需要高度的交互性和实时性。比如分诊大屏需要实时刷新候诊队列医生工作站需要快速切换患者、填写电子病历。Vue 的响应式数据绑定和组件化开发使得构建这类动态复杂的单页面应用SPA变得高效且易于维护。相较于当时的 Angular 和 ReactVue 的学习曲线更平缓对于医院内部可能存在的技术栈转型团队更友好。我们使用了 Vue CLI 搭建工程配合 Vue Router 管理路由Vuex 做状态管理Element UI 作为基础组件库快速构建了清晰、易用的操作界面。数据库 MySQL 选择 MySQL 是基于其成熟、稳定、开源且生态完善。急诊系统的数据关系虽然复杂患者、医嘱、病历、费用等但事务强一致性的要求很高例如扣费、发药必须保证原子性。MySQL 的 InnoDB 存储引擎提供了可靠的 ACID 事务支持。同时考虑到医院数据量的长期增长一个三甲医院急诊科年接诊量可达数十万人次我们在设计之初就考虑了分表和历史数据归档策略。MySQL 丰富的监控和优化工具也便于 DBA 进行性能调优。注意 技术选型没有绝对的好坏只有是否适合当前场景。对于初创团队或项目SpringBoot 和 Vue 的“快”是首要优势。如果项目规模极大并发量超高或许需要考虑 Spring Cloud 微服务或更前沿的技术但那会带来巨大的复杂度和运维成本。这个急诊系统项目证明了经典技术栈足以支撑起一个核心业务系统的稳定运行。2.2 整体架构设计思路系统采用典型的前后端分离架构。前端 Vue 项目独立部署通过 Nginx 提供静态文件服务并配置反向代理后端 SpringBoot 项目提供 RESTful API两者通过 HTTP/HTTPS 协议进行 JSON 格式的数据通信。后端分层架构Controller 层 接收前端请求进行参数校验我们使用了 Hibernate Validator并调用对应的 Service 方法。这一层很“薄”只负责协议转换和流量分发。Service 层 业务逻辑的核心所在地。所有急诊相关的业务规则如分诊级别的自动判定逻辑、抢救记录的生成规则、药品库存的校验与扣减等都在这里实现。为了保证事务我们通常在 Service 方法上使用Transactional注解。Mapper 层DAO层 使用 MyBatis 框架通过 XML 映射文件或注解方式定义数据库的 CRUD 操作。这里会编写复杂的动态 SQL 来应对多条件查询比如根据时间、科室、医生、患者姓名等多维度组合查询急诊记录。Entity 层Model层 与数据库表结构对应的 Java 实体类。我们使用了 Lombok 插件来简化 Getter/Setter 等样板代码的编写。前端模块化设计 前端项目按功能模块划分views/triage/: 分诊台相关页面患者登记、分诊评估、队列管理。views/doctor/: 医生工作站页面病历书写、医嘱开具、检查申请。views/nurse/: 护士工作站页面执行医嘱、护理记录、费用记账。views/charge/: 收费处页面预交金管理、结算、发票打印。views/board/: 急诊大屏展示页面候诊队列、抢救室状态、医生排班。api/: 集中管理所有向后端发送请求的接口函数。store/modules/: Vuex 状态管理模块用于共享跨组件的状态如当前登录用户信息、全局字典数据。3. 核心业务模块深度解析3.1 急诊预检分诊模块这是急诊的“第一道关卡”直接关系到患者能否得到及时、恰当的救治。系统实现了标准的分诊流程。业务流程患者登记 通过读取身份证、医保卡或手动输入快速创建患者基本信息。对于“三无”患者无身份、无家属、无经费系统支持创建临时档案。分诊评估 护士根据患者的主诉、生命体征体温、脉搏、呼吸、血压、血氧饱和度、疼痛评分等填写结构化分诊评估单。系统内置了常见的分诊工具逻辑如改良早期预警评分MEWS或急诊严重指数ESI的自动计算辅助。分级与分区 根据评估结果系统自动建议分诊级别如濒危、危重、急症、非急症并推荐就诊区域抢救室、诊室、观察区。护士可以确认或调整。患者被分配到一个唯一的急诊号并进入对应级别的候诊队列。队列管理与大屏展示 分诊台和大屏实时同步显示各队列的患者列表、等待时间、当前接诊医生等信息。系统支持“过号召回”、“优先安排”等队列调整操作。技术实现要点实时推送 大屏的实时更新是通过 WebSocket 实现的。当分诊台新增患者或队列状态变化时后端通过 WebSocket 主动推送消息给所有连接的大屏客户端。并发控制 分诊高峰期可能出现多名护士同时操作。关键操作如“分配医生”使用了数据库乐观锁通过版本号字段或 Redis 分布式锁防止同一患者被重复分配。字典管理 症状、体征、分诊级别等所有下拉选项均通过后端字典表统一管理前端通过接口动态获取保证了数据的规范性和可维护性。3.2 急诊电子病历与医嘱模块这是医生工作的核心要求快速、规范、可追溯。病历书写 我们采用了结构化病历模板与自由文本相结合的方式。对于现病史、既往史等提供丰富的模板和常用短语库支持点选和快速录入。体格检查部分则是高度结构化的表单确保关键体征不遗漏。所有病历修改均留有操作日志。医嘱管理 医嘱分为长期医嘱和临时医嘱。系统实现了完整的医嘱生命周期管理开具 医生开立医嘱系统会实时进行合理性校验如药品的剂量、频次、配伍禁忌基于内置的合理用药知识库、库存状态、患者过敏史等。核对 护士对医嘱进行核对确认无误后执行。执行 护士执行医嘱发药、治疗、检查并在系统中记录执行时间和执行人。停止 医生停止医嘱。技术实现要点前端富文本编辑器 病历的自由文本部分我们集成了Quill编辑器并对其进行了定制防止 XSS 攻击并支持保存为 HTML 或纯文本两种格式。医嘱校验引擎 这是一个相对独立的服务模块。它将药品、检查、治疗项目及其规则成人/儿童剂量、性别限制、相互作用等配置在数据库中。当医生保存医嘱时Service 层会调用校验引擎传入患者信息和医嘱内容引擎返回校验结果和提示信息。这种设计使得业务规则易于维护和扩展。事务一致性 一个“开立医嘱并扣减库存”的操作必须在一个事务内完成。我们使用 Spring 的Transactional来保证如果扣减库存失败则整个医嘱开立操作回滚。3.3 急诊留观与费用管理模块留观管理 对于需要留观的患者系统提供床位管理、留观病历、护理计划等功能。床位状态空床、占用、待消毒实时可视。护士可以排班并记录护理记录。费用管理 急诊费用产生点分散药房、检验科、治疗室需要实时汇总。自动记账 医嘱执行时如发药、做检查系统自动触发记账将费用项目关联到患者账户。预交金管理 患者就诊时缴纳预交金系统支持多次缴纳。所有消费实时冲抵预交金余额不足时系统会对医生和护士进行提醒。结算与发票 支持医保结算、自费结算等多种方式。与市医保平台通过 HTTPS 接口进行实时对接完成费用上传和医保报销计算。结算后系统调用热敏打印机或针式打印机打印发票和费用清单。技术实现要点分布式事务的妥协 严格来说“执行医嘱”和“记账”应该是一个分布式事务涉及业务系统和财务系统。但在实际中我们采用了“最终一致性”的补偿机制。先确保医嘱执行成功然后异步发送消息到消息队列RabbitMQ由独立的记账服务消费消息并完成记账。如果记账失败会有告警并触发人工干预对账。这是在高性能和高一致性之间做的典型权衡。接口集成 与医保、LIS检验系统、PACS影像系统的接口是重点。我们为每个外部系统定义了清晰的接口协议通常是 WebService 或 HTTPXML/JSON并使用 Apache HttpClient 或 FeignClient 进行调用。接口调用均有超时、重试和日志记录确保稳定性。数据一致性校验 每日定时任务会跑批核对医嘱执行记录、费用明细和库存变动生成对账报表这是保障系统数据准确性的最后一道防线。4. 数据库设计与关键表结构数据库设计遵循第三范式同时针对高频查询做了适当的反范式优化如增加冗余字段以减少关联查询。以下是几个核心表及其作用1.er_patient_visit(急诊患者就诊表)这是整个急诊流程的主线索表每一条记录代表一次急诊就诊。CREATE TABLE er_patient_visit ( visit_id varchar(32) NOT NULL COMMENT 急诊就诊ID主键, patient_id varchar(32) NOT NULL COMMENT 患者ID关联患者主索引, triage_level tinyint(4) NOT NULL COMMENT 分诊级别1濒危2危重3急症4非急症, chief_complaint varchar(500) DEFAULT NULL COMMENT 主诉, visit_time datetime NOT NULL COMMENT 就诊时间分诊时间, doctor_id varchar(32) DEFAULT NULL COMMENT 接诊医生ID, status tinyint(4) NOT NULL DEFAULT 1 COMMENT 状态1候诊2就诊中3留观4离院5死亡, prepay_balance decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT 预交金余额, create_time datetime NOT NULL, update_time datetime NOT NULL, PRIMARY KEY (visit_id), KEY idx_patient_id (patient_id), KEY idx_visit_time (visit_time), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT急诊就诊记录;设计心得visit_id使用了雪花算法生成的分布式ID避免自增ID在分库分表时的麻烦。status字段是驱动整个流程状态机的关键所有业务操作都会校验当前状态是否允许。2.er_medical_order(急诊医嘱表)CREATE TABLE er_medical_order ( order_id varchar(32) NOT NULL, visit_id varchar(32) NOT NULL, order_type tinyint(4) NOT NULL COMMENT 医嘱类型1药品2检验3检查4治疗, order_content varchar(1000) NOT NULL COMMENT 医嘱内容JSON格式存储药品规格、剂量等详细信息, order_status tinyint(4) NOT NULL DEFAULT 1 COMMENT 状态1已开立2已核对3已执行4已停止, frequency varchar(50) DEFAULT NULL COMMENT 频次如BID, doctor_id varchar(32) NOT NULL COMMENT 开嘱医生, nurse_id varchar(32) DEFAULT NULL COMMENT 执行护士, create_time datetime NOT NULL, update_time datetime NOT NULL, PRIMARY KEY (order_id), KEY idx_visit_id (visit_id), KEY idx_status (order_status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT急诊医嘱;设计心得order_content使用 JSON 格式存储动态的医嘱详情这样在新增医嘱项目类型时无需频繁修改表结构提高了灵活性。但缺点是查询和统计时需要解析 JSON对数据库有一定压力。对于需要复杂查询的字段我们仍会拆分成单独的列。3.er_fee_detail(急诊费用明细表)CREATE TABLE er_fee_detail ( fee_id varchar(32) NOT NULL, visit_id varchar(32) NOT NULL, order_id varchar(32) DEFAULT NULL COMMENT 关联的医嘱ID, fee_item_code varchar(50) NOT NULL COMMENT 收费项目编码, fee_item_name varchar(200) NOT NULL COMMENT 收费项目名称, unit_price decimal(10,2) NOT NULL COMMENT 单价, quantity decimal(10,3) NOT NULL COMMENT 数量, total_amount decimal(10,2) NOT NULL COMMENT 总金额, charge_time datetime NOT NULL COMMENT 记账时间, charge_status tinyint(4) NOT NULL DEFAULT 1 COMMENT 状态1已记账2已结算3已退费, PRIMARY KEY (fee_id), KEY idx_visit_id (visit_id), KEY idx_order_id (order_id), KEY idx_charge_time (charge_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT急诊费用明细;设计心得 这张表是写多读多的表。除了常规索引我们还按charge_time做了按月分表例如er_fee_detail_202401以应对数据量的快速增长。查询时由中间件或应用层根据时间范围路由到对应的物理表。5. 关键代码实现与避坑指南5.1 后端基于SpringBoot的RESTful API设计我们遵循 RESTful 风格设计 API力求资源定义清晰HTTP 方法使用得当。示例患者就诊查询接口RestController RequestMapping(/api/er/visits) Api(tags 急诊就诊管理) public class ErVisitController { Autowired private ErVisitService erVisitService; /** * 分页条件查询急诊就诊记录 * param queryDTO 查询条件患者姓名、时间范围、状态等 * param page 页码 * param size 每页大小 * return 分页结果 */ GetMapping ApiOperation(分页查询就诊记录) public CommonResultPageInfoErVisitVO queryVisitList(ErVisitQueryDTO queryDTO, RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size) { // 1. 参数校验使用JSR-303注解在DTO上已完成基础校验 // 2. 构造分页对象 PageHelper.startPage(page, size); // 3. 调用Service查询 ListErVisitVO list erVisitService.queryVisitList(queryDTO); // 4. 包装分页结果 PageInfoErVisitVO pageInfo new PageInfo(list); return CommonResult.success(pageInfo); } /** * 根据ID获取就诊详情包含患者信息、分诊信息等 * param visitId 就诊ID * return 就诊详情VO */ GetMapping(/{visitId}) ApiOperation(获取就诊详情) public CommonResultErVisitDetailVO getVisitDetail(PathVariable String visitId) { // 业务校验visitId是否存在当前用户是否有权限查看 ErVisitDetailVO detail erVisitService.getVisitDetailById(visitId); if (detail null) { return CommonResult.failed(就诊记录不存在); } return CommonResult.success(detail); } /** * 更新就诊状态如开始就诊、留观、离院 * param visitId 就诊ID * param statusUpdateDTO 状态更新请求体 * return 操作结果 */ PutMapping(/{visitId}/status) ApiOperation(更新就诊状态) public CommonResult updateVisitStatus(PathVariable String visitId, Valid RequestBody VisitStatusUpdateDTO statusUpdateDTO) { // 此处包含复杂的业务逻辑和状态机校验 boolean success erVisitService.updateVisitStatus(visitId, statusUpdateDTO); return success ? CommonResult.success(null) : CommonResult.failed(状态更新失败); } }避坑指南DTO/VO 分离 坚决不要用 Entity 对象直接接收前端请求或返回给前端。我们定义了QueryDTO用于接收查询参数UpdateDTO用于接收更新请求VO(View Object) 用于返回给前端。这保证了领域模型的纯净性和 API 的稳定性。全局异常处理 使用ControllerAdvice和ExceptionHandler定义全局异常处理器。将业务异常如BusinessException、参数校验异常、系统异常统一捕获并转换为结构化的错误信息CommonResult.failed(message)返回给前端。这避免了在 Controller 里写大量的try-catch。接口文档 使用 Swagger2/3通过springfox或springdoc-openapi自动生成 API 文档。在 Controller 和 Model 上使用Api,ApiOperation,ApiModelProperty等注解极大提升了前后端协作效率。5.2 前端Vue组件化开发与状态管理示例分诊队列组件 (TriageQueue.vue)template div classtriage-queue el-table :dataqueueData stylewidth: 100% stripe row-clickhandleRowClick el-table-column propqueueNumber label队列号 width100/el-table-column el-table-column proppatientName label姓名 width120/el-table-column el-table-column proptriageLevel label分级 width80 template slot-scopescope el-tag :typegetLevelTagType(scope.row.triageLevel) {{ getLevelText(scope.row.triageLevel) }} /el-tag /template /el-table-column el-table-column propwaitingTime label等待时间 width100 template slot-scopescope {{ formatWaitingTime(scope.row.waitingTime) }} /template /el-table-column el-table-column propchiefComplaint label主诉 show-overflow-tooltip/el-table-column el-table-column label操作 width150 template slot-scopescope el-button sizemini click.stopcallPatient(scope.row)呼叫/el-button el-button sizemini typeprimary click.stopassignDoctor(scope.row)分诊/el-button /template /el-table-column /el-table !-- 分诊对话框 -- triage-dialog :visible.syncdialogVisible :patient-infoselectedPatient successonTriageSuccess/ /div /template script import { mapState, mapActions } from vuex; import TriageDialog from ./components/TriageDialog.vue; import { formatDuration } from /utils/date; export default { name: TriageQueue, components: { TriageDialog }, data() { return { selectedPatient: null, dialogVisible: false }; }, computed: { // 从 Vuex store 中获取实时队列数据 ...mapState(triage, [queueData]) }, mounted() { // 组件挂载时开始轮询或建立WebSocket连接获取最新队列 this.fetchQueueData(); this.startPolling(); }, beforeDestroy() { this.stopPolling(); }, methods: { ...mapActions(triage, [fetchQueueData, callPatient]), // 格式化等待时间 formatWaitingTime(seconds) { return formatDuration(seconds); }, // 获取分诊级别对应的标签类型和文本 getLevelTagType(level) { const map { 1: danger, 2: warning, 3: primary, 4: info }; return map[level] || info; }, getLevelText(level) { const map { 1: 濒危, 2: 危重, 3: 急症, 4: 非急症 }; return map[level] || 未知; }, // 分诊操作 assignDoctor(row) { this.selectedPatient row; this.dialogVisible true; }, onTriageSuccess() { this.dialogVisible false; this.fetchQueueData(); // 刷新队列 this.$message.success(分诊成功); }, // 简单的轮询机制实际项目可能用WebSocket startPolling() { this.pollingTimer setInterval(() { this.fetchQueueData(); }, 10000); // 每10秒刷新一次 }, stopPolling() { if (this.pollingTimer) { clearInterval(this.pollingTimer); } } } }; /script避坑指南组件拆分 保持组件单一职责。TriageQueue只负责展示队列和触发操作具体的“分诊”弹窗逻辑封装在子组件TriageDialog中。这样父子组件通过props和$emit通信结构清晰。状态管理 像“分诊队列数据”这种多个组件都需要访问的全局状态我们将其放在 Vuex 的triage模块中。避免了复杂的组件间事件传递$emit/$on。性能优化 对于实时性要求高的大屏轮询Polling不是最佳选择它会给服务器带来不必要的压力。强烈推荐使用 WebSocket进行服务端推送。我们在生产环境就使用了SockJSStomp来实现。示例中的轮询仅作演示。防抖与节流 对于搜索框输入、窗口 resize 等频繁触发的事件一定要使用防抖debounce或节流throttle函数例如 Lodash 的_.debounce以提升性能。5.3 前后端交互与安全1. 认证与授权我们采用JWT (JSON Web Token)进行无状态认证。登录 用户输入工号密码后端验证后生成一个包含用户ID、角色等信息的 JWT Token 返回给前端。后续请求 前端将 Token 放在 HTTP 请求头的Authorization: Bearer token字段中。后端校验 通过一个 Spring Security 的过滤器或拦截器来校验 Token 的有效性和权限。权限控制在多个层面实现接口层面 使用PreAuthorize(hasRole(DOCTOR))或PreAuthorize(hasAuthority(triage:write))注解。前端菜单/按钮层面 根据用户角色动态生成路由和菜单按钮通过v-ifhasPermission(triage:write)控制显示。2. API 安全SQL 注入 坚持使用 MyBatis 的#{}预编译占位符杜绝拼接 SQL 字符串。XSS 攻击 后端对用户输入的富文本内容使用 Jsoup 等库进行 HTML 过滤白名单机制。前端在显示时使用 Vue 的v-html要格外小心或者对内容进行转义。CSRF 攻击 由于我们采用前后端分离且使用 JWTCSRF 风险较低但仍在关键操作如修改密码上增加了额外的验证码或二次确认。数据脱敏 返回给前端的患者敏感信息如身份证号、手机号在序列化时进行部分脱敏处理如510***********1234。6. 部署、监控与性能调优6.1 部署架构生产环境部署采用经典的三层架构负载均衡层 使用 Nginx 做反向代理和负载均衡将请求分发到多个后端 SpringBoot 应用实例。同时Nginx 也托管前端 Vue 的静态资源dist目录。应用层 多个 SpringBoot 应用实例部署在独立的 Tomcat 或直接以java -jar方式运行。通过 Nginx 的upstream实现负载均衡。数据层 MySQL 采用主从复制一主一从或多从读写分离。应用层通过中间件如 Sharding-JDBC或配置多数据源来区分读写操作。Redis 作为缓存和会话存储如果不用 JWT 无状态会话。6.2 监控与日志应用监控 集成 Spring Boot Actuator暴露/actuator/health,/actuator/metrics等端点配合 Prometheus 和 Grafana 监控应用状态JVM 内存、GC、线程池、接口 QPS、响应时间等。业务日志 使用 SLF4J Logback将日志按级别INFO, ERROR和模块输出到不同文件。关键业务操作如开立医嘱、结算必须打印操作日志包含操作人、时间、对象ID、前后状态变化便于审计和问题追溯。链路追踪 在微服务化之前我们通过在每个请求的 MDC (Mapped Diagnostic Context) 中放入一个唯一的traceId并在日志中打印该traceId可以将一个用户请求在整个系统中的所有日志串联起来极大方便了问题排查。6.3 性能调优实战记录问题1 分诊大屏列表查询缓慢现象 高峰期分诊大屏刷新列表的接口响应时间超过 2 秒。排查查看慢查询日志发现SELECT * FROM er_patient_visit WHERE status 1 ORDER BY triage_level ASC, visit_time ASC这条 SQL 在数据量达到 10 万条后变得很慢。使用EXPLAIN分析发现虽然status和visit_time有索引但ORDER BY涉及两个字段且triage_level区分度不高只有4个值导致大量文件排序filesort。解决方案查询优化 大屏通常只关心当前活跃状态为候诊、就诊中的患者且数量有限。修改查询为SELECT ... WHERE status IN (1,2) ORDER BY visit_time ASC LIMIT 100。status IN (1,2)能有效利用索引LIMIT减少了排序和传输的数据量。引入缓存 这个列表数据实时性要求高但变化频率相对可接受每分钟几次。我们将查询结果放入 Redis 缓存设置 30 秒过期。前端请求先读缓存同时后端有一个定时任务每 20 秒刷新一次缓存。这样 99% 的请求都命中缓存响应时间降到毫秒级。索引优化 为(status, visit_time)建立了联合索引覆盖了查询和排序条件。问题2 医嘱保存时合理性校验耗时过长现象 医生保存复杂医嘱包含十几种药品时页面会“卡住”几秒钟。排查 发现校验逻辑中对每种药品都需要查询一次药品信息表、一次库存表、一次患者过敏史表并且要进行复杂的规则匹配计算。串行执行导致总耗时累加。解决方案批量查询 将多个药品的查询条件合并改为SELECT * FROM drug WHERE id IN (?,?,...)和SELECT * FROM stock WHERE drug_id IN (?,?,...)将 N 次数据库交互减少为 2 次。缓存预热 将不经常变动的数据如药品基本信息、配伍禁忌规则在应用启动时加载到本地内存如 Guava Cache或 Redis 中校验时直接内存查询避免了数据库 IO。异步与补偿 对于最耗时的“合理用药知识库”深度校验我们将其改为异步操作。医嘱先保存成功然后发送一个消息到消息队列由后台服务进行深度校验。如果校验出严重问题再通过系统消息通知医生进行修正。这是一种“先通过后严审”的折中方案保证了开医嘱流程的流畅性。7. 常见问题排查与解决方案速查在实际开发和运维中我们遇到了各种各样的问题。下面这个表格总结了一些典型问题及其排查思路和解决方案希望能帮你少走弯路。问题现象可能原因排查步骤解决方案前端页面打开空白控制台报4041. Nginx配置错误未正确代理前端或后端请求。2. 后端服务未启动。3. 前端路由模式为history但Nginx未配置try_files。1. 检查浏览器开发者工具Network面板看具体哪个资源JS/CSS/API404。2. 检查Nginx的error.log和access.log。3. 直接访问后端API地址看是否通。1. 修正Nginx配置确保静态资源路径和API代理路径正确。2. 启动后端服务。3. 对于Vuehistory模式在Nginx location / 中添加try_files $uri $uri/ /index.html;。登录成功但后续接口请求返回401/4031. 前端未正确携带Token存储或发送问题。2. Token已过期。3. 用户权限不足。1. 检查浏览器Application/Local Storage或Cookie中Token是否存在。2. 检查请求头Authorization是否正确。3. 查看后端日志确认JWT解析是否失败或权限校验未通过。1. 确保登录后正确存储Token并在axios拦截器中统一设置请求头。2. 实现Token自动刷新逻辑使用refresh token。3. 检查用户角色和接口要求的权限是否匹配。页面数据不更新或显示旧数据1. 浏览器缓存了静态资源或API响应。2. Vuex状态未及时更新。3. 组件key未正确设置导致Vue复用错误组件。1. 打开开发者工具Network勾选Disable cache刷新看是否正常。2. 检查Vue Devtools查看Vuex中对应state的值。3. 检查列表渲染时是否为每个项设置了唯一的:key。1. 为Webpack输出文件添加hash后缀[name].[contenthash].js。2. 在获取新数据后正确提交mutation更新Vuex state。3. 确保列表项的key是唯一且稳定的如ID而不是数组索引。数据库CPU或连接数异常升高1. 存在慢查询。2. 数据库连接未正确关闭连接泄漏。3. 遭遇SQL注入攻击或恶意爬虫。1. 开启MySQL慢查询日志分析long_query_time以上的SQL。2. 使用SHOW PROCESSLIST;查看当前连接和执行状态。3. 检查应用连接池配置如HikariCP和使用情况。1. 优化慢SQL添加合适索引避免SELECT *优化子查询。2. 确保所有数据库操作都在try-with-resourcesJava或finally块中关闭连接/会话。3. 配置防火墙规则限制非法IP访问加强入参校验。事务不回滚1. 异常未被Spring捕获如非RuntimeException。2. 方法内部try-catch吞掉了异常。3. 方法不是public的导致AOP代理失效。1. 检查方法上是否有Transactional注解。2. 检查抛出的异常是否是RuntimeException或Error或者是否在Transactional(rollbackForException.class)中指定。3. 检查方法访问权限。1. 确保抛出的是RuntimeException或在注解中指定rollbackFor。2. 在catch块中如果需要回滚手动抛出异常或调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()。3. 将事务方法定义为public。文件上传失败或大小受限1. Spring Boot默认文件上传大小限制1MB。2. Nginx客户端请求体大小限制。3. 磁盘空间不足。1. 查看后端日志是否有MaxUploadSizeExceededException。2. 查看Nginx错误日志 (error.log)。3. 检查服务器磁盘使用情况。1. 在application.yml中配置spring.servlet.multipart.max-file-size和max-request-size。2. 在Nginx配置中增加client_max_body_size 20m;。3. 清理磁盘或增加存储空间。这个项目从零到一的过程充满了挑战也收获颇丰。最大的体会是技术永远是为业务服务的。再炫酷的技术如果不能稳定、高效地支撑起“分秒必争”的急诊业务都是空中楼阁。在编码之前花足够的时间去理解业务流程、与医护人员沟通、设计表结构和接口往往比后期埋头调优更重要。这套源代码和文档希望能为你打开一扇窗看到如何将 SpringBoot、Vue 这些技术实实在在地应用到一个复杂且重要的行业系统中。如果你在运行或研究代码时遇到任何问题欢迎随时交流讨论。本文还有配套的精品资源点击获取
返回列表