医院药品管理系统全栈实战:SpringBoot+Vue+MySQL从0到1开发详解

发布时间:2026/10/17 7:40:03

医院药品管理系统全栈实战:SpringBoot+Vue+MySQL从0到1开发详解 医院药品管理系统从0到1SpringBootVueMySQL全栈源码拆解与落地笔记最近刚把手头一套医院药品管理系统HIS药品管理子系统的全栈源码整理完毕技术栈是SpringBoot后端Vue前端MySQL数据库结构清晰、可以直接跑起来。前后断断续续做了快两个月踩了不少坑也沉淀了一些实打实的经验。趁这会儿记忆还热乎把整个项目从设计思路、数据库建模、后端核心逻辑、前端页面架构到最后的联调部署全流程捋一遍给正在做同类项目或者想拿这套源码练手的同学一份能直接抄作业的参考。这套系统解决的是医院药房日常管理里最头疼的几个问题药品信息散乱、库存账实不符、近效期药品靠人工盯着、发药退药靠手写单据、进销存数据统计全靠月底翻Excel。它把这套流程全部线上化入库有人管、出库有记录、库存有预警、效期有提醒、账目有流水同时给管理员和药房操作人员提供不同的操作权限。如果你正在找毕业设计参考、接手同类医院项目或者想学前后端分离项目的完整落地过程这篇笔记都是按真实开发流程写的可以直接对着源码边看边跑。先交代一下这套源码的整体面貌。后端用的是SpringBoot 2.7.xJava 1.8MyBatis-Plus做ORMSpring SecurityJWT做登录鉴权前端是Vue 2.6 Element UI Axios Vue Router Vuex数据库MySQL 8.0核心业务表大概十四张。功能上覆盖了系统管理、药品档案、供应商管理、入库管理、出库管理、库存盘点、效期预警、过期药品处理、药品退换货、统计报表这十来个模块可以说医院药房日常用得上的功能基本都齐了。这里先说明一下为了让这篇记录尽量通用涉及具体医院名称、人和供应商的部分我都用某医院、某医药公司这类代称替代代码本身不涉及任何业务敏感数据可以放心照搬思路。1. 项目到底解决什么问题医院药品管理的真实痛点先说个背景。之前接触过一个药房信息化的模拟项目也就是这套源码的前身当时是给一家二甲规模的医院做的需求调研规模不大药房也就六七个窗口但每天处理处方单、出入库单、退药单加起来几百张完全是常事。调研过程中让我印象最深的几个痛点也是这套系统核心功能的设计依据痛点一库存账实不符。药房负责人跟我说过一句很扎心的话月底盘点账上1000盒的药货架上可能只有960盒多了少了都不知道差在哪。原因是之前完全是手工登记入库登记一笔、出库划掉一笔单据容易丢员工交接不清时间一长账面根本不可信。系统里引入库存流水表之后每一笔数量变化都有单据号可追溯账实对不上时可以直接按流水反向排查。痛点二近效期药品靠人盯。药房每个月要人工翻一遍货架看哪些药快过期了工作量极大还容易漏。系统里加了效期预警功能药品入库时录入有效期系统每天自动扫描把60天内过期的药标黄、30天内标红还能导出近效期药品清单药房工作人员按清单处理就行。痛点三发药环节容易错。人手紧张的时候拿错药、拿错规格的情况虽然不多但一旦发生就是医疗事故。系统的出库发药模块要求必须关联处方信息药品批次、厂家、有效期在发药时都能查得到减少了张冠李戴的概率。痛点四统计报表靠人工。医院每个月要向上级报药品使用情况、库存金额、周转数据以前是药房自己用Excel做费时费力。系统里做了库存统计、入库统计、出库统计、供应商供货统计这几张报表按时间范围筛选即可数据实时、口径统一。系统上线后药房员工最直观的感受是入库扫码/手录一键完成库存实时更新发药时系统自动扣库存库存不足会拦截近效期提醒自动推送到首页盘点时用系统导出的盘点单盘盈盘亏在系统里走审批流程再也不用来回翻纸质单据了。这套系统的定位用白话说就是给医院药房做了一套全流程可追溯的进销存预警系统一口价的账本谁入库、谁出库、库里有啥、快过期了没有随时打开电脑就能看清楚。如果你接手的项目跟这套源码定位类似核心业务流程就是“供应商→入库→库存→出库→患者”围绕这条主线把每个环节的状态、单据、数量、金额都管清楚系统的基本盘就稳了。2. 技术选型为什么是这套组合前后端分离的取舍这部分把技术选型逻辑讲清楚。很多同学拿到源码第一步就急着跑起来但如果不理解为什么要这么选后面改起来容易碰壁。2.1 为什么用SpringBoot而不是传统SSH/SSMSpringBoot的核心优势是“约定大于配置”省掉了大量繁琐的XML配置。药品管理系统这类业务系统绝大多数代码是写业务逻辑入库、出库、库存计算、权限判断而不是配置环境。SpringBoot把Web容器内嵌进来直接mvn spring-boot:run就能启动配合MyBatis-Plus单表CRUD几乎不用手写SQL开发效率提升非常明显。另外Spring系列在医疗行业的信息系统里占有率特别高原因跟技术本身关系不大主要是生态成熟、招人容易、稳定的版本选择多。医院这类单位对系统稳定性的要求高于一切不会有人冒险去用太激进的新技术栈SpringBoot 2.x目前就是比较均衡的选择。2.2 为什么前端选Vue而非JSP或者Thymeleaf老一代药品管理系统的界面大多是JSPJQuery页面和Java代码耦合很重。这套系统当初明确要前后端分离前端独立部署后端只提供JSON接口原因有三第一药房的操作界面按钮多、表格多、弹窗多用Vue的组件化开发写起来比JQuery直接操作DOM清爽得多第二前后端分离后医院将来如果想接大屏、接自助设备可以直接复用后端的API前端重新做一个界面就行不用动后端逻辑第三团队分工舒服前端写页面、后端写接口两边只需要约定好接口文档。Vue 2.6 Element UI这套组合在中小型管理系统里属于“久经考验”的搭配。Element UI的表格、表单、弹窗、分页组件覆盖了药房业务95%的场景不需要自己造轮子。2.3 权限方案的设计取舍药品管理系统涉及药房操作员、药房主管、系统管理员三类角色权限控制必须精确到按钮级别。我用的方案是Spring Security JWT。这里有一个常见争议为什么不用Shiro实话说如果系统只有“登录几种角色”Shiro确实更简单但是Spring Security与SpringBoot的集成更原生后续如果要接入OAuth2、LDAP、SSO这些企业级认证扩展空间更大。考虑到医院信息科大概率后续要求对接统一身份认证平台选Spring Security更稳妥。JWT方案的好处是服务端无状态前端存token请求时放在Authorization头里适合前后端分离部署。缺点是token一旦签发在有效期内没法主动失效所以我把token有效期设成8小时并且配合用户的上下班时间刚好够用。如果医院有更严格的安全要求可以再加上Redis做token黑名单那是后话。2.4 数据库选型MySQL在这个场景下的合理性药品管理系统的数据量按一家中型医院算核心的业务流水表库存流水、出入库单据明细一年大概几十万到百万行级别MySQL 8.0完全扛得住而且运维成本低、资料多、DBA好招。我见过有些同学一上来就纠结要不要用Oracle或者PostgreSQL说实话在这个体量的系统里属于技术过剩。MySQL配合定期备份、合理的索引设计足够支撑医院药品管理的日常运转。3. 数据库设计是系统的地基核心表结构与关键关系从我的经验来看药品管理系统出问题80%是数据库设计埋的雷。要么缺约束导致脏数据要么表结构不合理导致统计报表写不出来。下面把核心表结构拆出来讲这是整个源码里最值得细看的部分。3.1 用户与角色权限三张基础表用户表sys_user存账号、密码BCrypt加密、姓名、所属药房科室、状态角色表sys_role存角色编码ADMIN/OPERATOR/MANAGER用户角色关联表sys_user_role做多对多关联。这套设计是Spring Security标准RBAC模型的基础一个用户可以拥有多个角色每个角色在代码里通过hasRole(ADMIN)这类表达式控制接口权限。权限控制上有一个细节值得注意操作按钮的权限是放在前端控制的菜单/按钮由后端的权限码动态渲染但后端每个写操作接口都会再做一次权限校验。前端控制用户体验后端控制数据安全两层缺一不可。3.2 药品档案表字段设计是门学问药品档案表drug_info是整个系统的核心基础表字段包括药品编码系统内部使用的唯一编码建议规则是“分类码流水号”药品名称、通用名、商品名同一个药可能有多个商品名通用名用于归类统计规格、剂型、单位、包装规格比如 0.25g×24粒/盒单位是盒生产厂家、批准文号国药准字、医保类别甲类/乙类/自费零售价、进货价、最高库存、最低库存最低库存用于库存预警存储条件常温/阴凉/冷藏、是否处方药、状态启用/停用设计这张表时最容易犯的错是把厂家信息直接写在药品表里。我见过有的项目把厂家名称、联系人、电话全塞进drug_info后来供应商多了改个电话得把所有关联药品全改一遍。正确的做法是维护一张供应商表drug_supplier药品表里只存supplier_id需要查厂家信息时关联查。药品编码这个字段要重点说。医院内部流程里药品编码是药品的身份证入库单、出库单、盘点单全都要引用它。编码规则设计不好后期加药、分类统计都会痛苦。我给这套系统定的规则是4位分类码4位流水号比如11010001表示“抗生素类第1个药品”既保证了唯一性又保留了分类统计的能力。3.3 库存与批次药品管理的关键难点药品库存管理跟普通商品库存最大的区别在于批次和效期。同一款药品不同批次的生产日期、有效期不同混在一起管理会出现“先进先出发错批次”的风险。我设计了两张表来管理库存表drug_stock一个药品一个批次对应一条记录字段包括药品ID、批次号、生产日期、有效期、当前库存数量、所在库位比如“门诊药房A货架3层”。库存流水表stock_flow记录每一笔库存变动的明细字段包括变动类型入库/出库/盘点调整/退库/报废、关联单据号、药品ID、批次号、变动前数量、变动后数量、操作人、操作时间。这两张表配合起来的效果是系统里任何一个库存数字的变动都可以追溯到具体的操作人和单据。账实不符的时候按时间范围查流水一眼就能看出是某笔入库漏录了还是某次出库数量录错了。批次的逻辑很多同学第一次写容易漏掉这里单独强调一下药品入库时必须要求录入批次号和有效期否则后续的近效期预警和先进先出都没法做。药品出库时如果同一药品有多个批次系统默认优先出有效期最近的先过期先出这就是最基本的FEFOFirst Expiry First Out策略。3.4 入出库单据业务的凭证核心入库单stock_in_order和出库单stock_out_order分别管理“进”和“出”两大主线都包含单号、单据类型、关联供应商/HIS处方、经办人、审核人、状态待审核/已审核/已驳回、备注。入库单明细表和出库单明细表用于记录具体每个药品数量、批次、价格信息。单表和明细表的关系是标准的“主从表”结构单表一条记录明细表可能有多条用order_id关联。为什么要分单表和明细表因为药房业务有时候需要整单审核、整单驳回分两张表才能保证操作的原子性。比如入库单上有20个药品其中1个药品录错了整单驳回让操作员重新修改审核人才好处理。3.5 其他辅助表预警、盘点、退换货近效期预警表drug_warning用于记录系统每天扫描后生成的预警记录包含药品ID、批次号、有效期、剩余天数、预警类型30天/60天、处理状态。这个表的存在让预警功能有历史记录可查而不是只在首页弹个提示。盘点表stock_take管理盘点批次和盘点结果盘盈盘亏数量记录在盘点明细里经过审核后生成出入库调整单自动修正库存。这里要注意一个业务规则盘点产生的库存调整也要走库存流水否则账永远对不上。退换货表drug_return处理两种情况供应商退换入库后发现质量问题的退回和患者退药处方发药后退回。这两种退换在流程上不一样前者是冲减库存并通知供应商后者是增加库存并记录原因所以设计上我加了一个type字段区分。4. 后端核心模块实现接口设计与业务逻辑关键点后端代码的结构按模块分包controller、service、mapper、entity、dto、common。下面说几个核心模块的实现要点这些也是面试或者自我排查时最容易被问到的部分。4.1 登录认证与接口安全登录接口/api/auth/login接收用户名密码校验通过后生成JWT返回前端。密码用BCrypt加密存储即使数据库泄露密文也无法逆向还原。这里插一句千万不要用MD5直接存密码MD5撞库太容易了这是老生常谈但总有人犯。JWT的工具类核心就是生成token和解析token。生成时把用户ID和用户名放进去过期时间8小时。接口安全拦截这块我写了一个JWT过滤器继承OncePerRequestFilter从请求头Authorization里取出token校验通过就放入Spring Security上下文否则直接返回401。每个写接口增删改上面用PreAuthorize注解控制权限例如入库接口PreAuthorize(“hasAnyRole(‘OPERATOR’,’ADMIN’)”)审核入库单PreAuthorize(“hasRole(‘ADMIN’)”)实操中有一个坑Spring Security的拦截规则如果配置不当会导致静态资源和登录接口被拦截。我的配置是放行/api/auth/**和swagger文档路径其余请求全部需要认证。4.2 药品入库流程事务处理是底线入库的完整流程不只是插入一条入库单记录那么简单它涉及四步操作保存入库单主表记录状态为待审核保存入库单明细记录每个药品的批次、数量、单价审核通过后写入库存表存在该药品批次则累加数量不存在则新插入一条写入库存流水变动前数量为0或原库存变动后数量为新库存。这四步操作必须放在同一个事务里任何一个环节失败都要回滚所有已操作的数据否则会出现入库单审核通过了但库存没加上的问题这是药品库存管理的严重事故。关键代码结构上我用了Transactional(rollbackFor Exception.class)标注在审核方法上。操作意图对应的判断逻辑也要说清楚入库时系统会校验采购单是否已存在、药品ID是否有效、数量是否大于0、单价是否合理超过参考价一定比例时给出提醒但不阻断因为医院采购价和合同价有时会有浮动。4.3 药品出库流程Druid级别的数据一致性发药出库比入库复杂因为它要先判断库存是否充足再决定扣减哪个批次。核心逻辑我分了三步校验药品、数量合法性汇总同一药品所有批次的可出库数量总和不足则直接抛出业务异常提示“库存不足”按FEFO规则排序批次有效期升序依次扣减库存每个批次单独扣直到数量足够扣减完成后写库存流水并同步生成出库单记录。写到这里有一个代码层面的具体细节值得分享。因为扣减批次是逐条update库存表高并发场景下可能出现超卖所以我在库存表的更新语句里加了条件WHERE stock_id ? AND quantity ?要么扣减成功返回1要么返回0认为库存不足。联合唯一索引drug_id, batch_no也加了防止并发插入同一个批次导致重复记录。4.4 库存预警定时任务与查询优化近效期预警用Spring自带的Scheduled注解每天凌晨执行一次扫描。扫描逻辑很简单查drug_stock中所有有效期小于等于60天的批次生成预警记录如果同药同批次已有未处理的预警则不重复生成。这里有个业务细节药品有效期和失效日期不是一回事。通常药品有效期指到月末最后一天比如“有效期至2026/03”意思是可以用到2026年3月31日。录入的时候统一存成到期日比如存2026-03-31预警计算直接用到期日减当前日期避免每个月还要处理“月底边界”的问题。这个细节是我在跟药房老师傅确认后改的算是一个比较典型的行业知识。4.5 统计报表SQL技巧与性能优化月度入库统计、出库统计、库存金额统计都通过聚合SQL实现。以入库统计为例查询条件是时间范围分组维度是药品ID统计指标是入库数量和入库金额。这类统计SQL在数据量小的时候很轻松但流水表到了几十万行之后没有索引就是灾难。我加了两个组合索引(drug_id, create_time)和(create_time)统计接口的查询时间从原来的几秒钟降到了几百毫秒。索引这块建议在正式上线前就要设计好别等数据量大了再补。后端这里再多说一个容易被忽略的点统一返回格式。所有接口统一返回{code: 200, message: “success”, data: xxx}结构前端的axios拦截器统一处理返回和错误。这个约定虽然简单但可以少写很多样板代码。5. 前端实现要点VueElement UI的页面工程前端部分的工程结构是src/api接口封装、src/router路由表、src/storeVuex状态管理、src/views页面、src/utils工具函数。下面讲几个关键实现点以及实际开发中容易踩的坑。5.1 路由与权限控制路由分为公共路由登录页和动态路由业务页面。用户登录成功后后端返回该用户的角色和权限码列表前端根据权限码动态生成菜单和路由。实现方案是vue-router的addRoutes方法。首次登录时只注册公共路由拿到权限后再动态注册业务路由。菜单侧边栏根据路由表递归生成有权限的才显示。这个方案的坑在于刷新页面时路由是空的必须重新拉取用户信息并动态注册否则刷新后会白屏。我的处理是在main.js的全局前置守卫里加了权限校验逻辑本地有token但store里没有用户信息时先调一次getInfo接口重新加载权限再继续路由跳转。5.2 Axios封装与错误处理axios统一封装在api/request.js里做了三件事第一请求拦截器里加上token从localStorage取出后放Authorization头第二响应拦截器里统一处理后端返回结构code为200时返回data否则抛出错误提示第三响应状态码401时清除本地登录信息跳回登录页。这里有一个细节后端返回的业务异常比如库存不足HTTP状态码依然是200只是body里的code不是200。axios拦截器按业务code判断弹出对应提示这样前端不需要在每个请求里重复写错误判断。5.3 核心业务页面实战药品列表页是使用频率最高的页面实现了分页查询、多条件筛选名称、分类、厂家、状态、新增/编辑弹窗、库存详情抽屉。这里Element UI的table组件配合分页组件用得最多需要注意的点是编辑弹窗的表单校验用async-validator必填项、数字范围、日期格式都做了校验规则这是前端防止脏数据的第一道防线。入库单管理页和出库单管理页采用的是“主表列表详情抽屉创建弹窗”的结构。创建入库单时添加药品明细用的是动态表格操作员搜索药品选中后添加到明细列表可以修改数量、单价、批次也可以删除行最后提交整单。这个交互对于药房操作员来说比较直观比起在Excel里一条条录效率高很多。库存查询页呈现的是当前所有药品每个批次的库存情况支持按有效期范围筛选结合库存预警模块的标红标黄逻辑在表格里给近效期批次加颜色标识一眼就能看出哪些药需要优先处理。首页仪表盘放了几个核心数据卡片库存总品种数、库存总金额、近效期药品数、今日出库单数下面接一个近效期TOP10的列表。这个页面是药房主管每天打开系统第一眼看的数据全部来自后端聚合接口。前端这个部分还有一个小技巧药品名称、厂家这些下拉选项在多个页面都会用到我把它放到了Vuex里做全局缓存第一次加载后就不重复请求了。不过缓存有个副作用——药品数据被其他人修改后缓存里的下拉列表可能不是最新的。我的策略是下拉数据每天第一次登录时拉取配合“手动刷新”按钮解决强一致需求。6. 从源码到跑起来环境准备、配置修改与启动验证拿到源码后第一件事不是看代码是先把它跑起来。这一节给出完整的从零启动步骤照着做基本不会出问题。6.1 环境准备清单JDK 1.8推荐8u202以上版本Maven 3.6后端依赖管理Node.js 14.x前端构建注意不要用太新的版本Vue2项目在Node 18以上构建时偶尔会有OpenSSL报错MySQL 8.05.7也可以但建表语句里如果用了utf8mb4_0900_ai_ci5.7会不识别5.7用户要改一下collateIDE后端Idea、前端VSCode6.2 数据库初始化把源码中的sql/init.sql导入MySQL。这个脚本会自动建库、建表、插入初始数据包括一个管理员账号admin/admin123和一个操作员账号operator/operator123还有一些演示用的药品数据。导入后建议先看看几张核心表的关联关系比如drug_stock里是否有数据、drug_info的编码规则是否清晰这样后面测试功能时心里有数。如果用的是MySQL 5.7版本导入时大概率遇到utf8mb4_0900_ai_ci不识别的问题处理方式是打开sql文件全局替换utf8mb4_0900_ai_ci为utf8mb4_general_ci再重新导入即可。6.3 后端配置与启动用Idea打开backend目录等待Maven自动拉取依赖。修改application.yml里的数据库连接信息spring: datasource: url: jdbc:mysql://localhost:3306/drug_manage?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的数据库密码修改后直接运行DrugManageApplication.java的main方法看到SpringBoot启动成功的日志端口8080就OK了。想验证后端是否正常可以访问http://localhost:8080/api/auth/login用Postman发一个POST请求body带{username:admin,password:admin123}能返回token说明后端没问题。需要注意SpringBoot默认端口是8080如果被占用在配置文件里加server.port8081换个端口就行前端联调时地址同步修改。6.4 前端安装与启动命令行进入frontend目录依次执行npm install npm run serve第一次install会比较慢如果报node-sass相关的错大概率是Node版本太高导致编译失败。可以换成dart-sasssass包解决或者直接Node切到14版本。启动成功后访问http://localhost:8080后端地址默认配置在src/utils/request.js里baseURL/api通过devServer里的proxy代理到8080端口。如果要连生产环境的后端直接把baseURL改成后端实际地址或者在nginx里做反向代理。6.5 功能验证清单跑通核心链路才算真的启动成功推荐按下面这条业务链路做一次完整验证确保前后端都正常工作用admin登录系统能正常进入首页看到统计卡片数据进入“药品管理”页面能看药品列表新增一条测试药品进入“入库管理”做一张入库单添加刚刚新增的药品数量50提交并审核去“库存查询”页面确认该药品库存变成了50批次有效期正确回到“出库管理”创建出库单数量10确认库存变成40查看“库存流水”能看到刚才这笔入库和出库对应的两条流水记录把某个药品的库存改成低于预警值或者直接在库里更新刷新首页看预警提示是否出现。这条链路能走通系统就基本没问题了。如果中间某一步报错优先看后端控制台日志前端大部分报错都能在后端日志里找到对应的异常栈信息。7. 常见问题与排查技巧实录这一节整理了我实际调试这套系统时遇到过的问题也是给使用源码的同学提前排雷。按出现频率从高到低列一下。7.1 跨域请求被拦截前端http://localhost:8080访问后端http://localhost:8081时浏览器会拦截跨域请求。我用了两种方案配合开发环境在vue.config.js里配置devServer.proxy把/api开头的请求转发到后端浏览器看到的还是同源请求不会触发跨域生产环境用Nginx把前后端挂在同一个域名下/api路径转发到后端服务。如果在生产环境仍然出现跨域报错还有一种兜底方案在后端写一个WebMvcConfigurer配置CorsFilter允许指定域名跨域访问。但我的建议是尽量通过代理或Nginx解决后端不要把跨域开得太开放否则存在安全隐患。7.2 后端启动时报端口被占用处理方案是找到占用端口的进程并停掉或者直接改后端的server.port。Windows下用netstat -ano | findstr 8080查看PID任务管理器结束进程Linux下用lsof -i:8080。改了端口之后记得同步修改前端代理配置里的target地址。7.3 npm install失败sass编译报错Node版本太新时node-sass下载和编译很容易出问题。我的处理方式是package.json里把node-sass替换为sassdart-sass然后重新安装。Element UI配dart-sass可能会在深选择器/deep/上报警告替换成::v-deep写法即可功能不受影响。7.4 登录接口返回401/500排查方向有三个数据库是否连接成功看数据库地址和账号密码token密钥配置是否正确application.yml里的jwt.secret要一致后端是否真的启动了看端口日志。如果是数据库连接失败通常日志里会出现Communications link failure检查MySQL服务是否启动、账号密码是否对、url里的serverTimezone是否写了Asia/Shanghai。7.5 入库审核通过了但库存没有增加这是事务没生效的典型表现。检查审核方法上有没有加Transactional注解这个注解最终有没有生效。一个常见的坑是自己在类里调用同类的方法Transactional会失效。解决办法是把审核逻辑拆到另一个Service类里Spring代理才能拦截到事务。7.6 前端页面刷新后404这是因为前端用了history模式的路由刷新时Nginx找不到对应的路径。解决办法是在Nginx配置里加一行try_files $uri $uri/ /index.html;让所有未匹配到的路径都回到index.html由前端路由自己处理。开发模式下Vue自带的devServer不会出现这个问题部署到Nginx后记得处理。7.7 定时任务不执行检查两点启动类或配置类上有没有加EnableScheduling注解方法上的Scheduled表达式是否正确。我初期犯过一个错把每天的扫描时间写成了0 0 0 * * ?秒 分 时这个表达式含义是“每天的0点0分0秒”但我本意是凌晨1点写成了0 0 1 * * ?才对。这类表达式很容易写错建议写完跑一次测试验证。8. 数据库索引与性能优化小小的一张表也可能是瓶颈这里单独开一节因为药品系统的数据量虽然不算大公司级别但药房每天高频查询的几张表如果索引设计不当体验会明显变差。优先加上索引的几个位置drug_stock表(drug_id, batch_no) 联合唯一索引这是保证批次库存唯一性的关键stock_flow表(drug_id, create_time) 组合索引用于查询某个药品的历史流水stock_in_order表和stock_out_order表order_no字段建唯一索引state字段建普通索引create_time建普通索引sys_user表username字段建唯一索引登录时靠它定位用户。查询优化另一个方向是列表页的分页查询尽量用MyBatis-Plus的分页插件不要自己用LIMIT拼SQL后还要回表查count插件会把count和列表查询一起处理性能更好。药品档案的模糊搜索用了LIKE ‘%关键词%’这种写法数据量大了会全表扫描不过药品表撑死几千条数据影响不大。如果真的要做到高性能模糊搜索可以后续接ES但对这套系统来说属于过度设计。9. 源码之外这套系统的扩展方向与上线的落地经验最后说点源码之外的体会。如果你拿着这套系统真正推到医院场景有几点经验供参考第一跟药房老师傅沟通时别只问“你需要什么功能”要问“你每天工作是怎么做的”。这套系统里很多细节比如效期到期日按月末处理、盘点要走审核流程、退药要区分原因都是跟一线人员反复确认后调整的纯粹按教科书里设计的流程做到实际场景里反而是废的。第二药品编码和批次的管理规则一定要在项目初期就定清楚。中途换规则会让历史数据全部作废这是我在另一个模拟项目里亲历的惨痛教训。第三权限管理不要图省事只做“管理员/普通用户”两个角色。医院药房实际上还有夜班值班人员的权限差异、不同科室药房的隔离需求建议在RBAC模型上把角色设计得粒度细一些后面扩展就轻松了。第四备份策略是医院这类机构的高压线。数据库必须每天备份建议用脚本定时导出SQL文件并在异机保存。药品管理系统里存的是进销存数据和部分患者用药记录一旦丢了恢复成本极高。最后分享一个开发期的小技巧把SpringBoot的spring.jpa.show-sql或MyBatis-Plus的日志输出打开开发阶段每个接口执行的SQL都打印出来排查“该查到的没查到”“该更新的没更新”这类问题时效率能提升好几倍。上线前再关掉SQL日志就行。这套系统本身是一个比较标准的全栈业务系统麻雀虽小五脏俱全。如果你正在学习SpringBootVue的项目开发完全可以拿它当练手源码先跑通再改再扩展。如果是在做一个真实的医院药房项目这套设计思路和表结构也能作为需求分析和建模的重要参考。有问题欢迎一起交流。
延伸阅读

更多相关文章

2026/10/17 7:40:03

RetinaNet实战:校园建筑物识别与分类的工程要点

我先把话放前面:校园建筑物识别与分类这个方向,看着像纯计算机视觉任务,真做起来却是一个要同时处理数据采集、标注规范、目标检测选型、训练调参、部署集成的综合工程。我们用RetinaNet做了一套校园建筑物识别与分类系统,识别对象…

2026/10/17 7:40:03

面试算法题集中训练:六道经典题解析与实战技巧

1. 面试算法题训练的整体思路拆解1.1 为什么选择集中刷题而不是零散练习很多同学准备面试时习惯今天刷一道明天刷一道,看起来每天都在学,实际上效率很低。我自己踩过这个坑,后来发现集中刷同一家公司的题目效果要好得多。原因很简单&#xff…

2026/10/17 7:35:03

如何利用查找我的iPhone找回手机?从关机追踪到超宽带精确定位

说实话,我以前对“查找我的iPhone”这功能一直停留在“就是看看朋友在哪”的认知上。直到前两年自己真丢过一次手机:打车回家才发现手机落在后座,打电话过去已经关机,地图上那个橘色圆点从下午六点开始就再没动过。那十几个小时真…

2026/10/17 8:40:06

共享浏览器与Session克隆:跨节点状态同步的工程实践

简介:这份资源围绕“克隆Session、共享浏览器”这一技术主题,面向具备一定Web开发与网络基础、希望研究跨设备会话迁移的开发者。它要解决的核心问题是:如何捕获并解析HTTP请求中的Session ID与Cookie,将其安全传输到异地设备&…

2026/10/17 8:40:06

智能体通信协议基础:消息分层、语义一致性与多轮对话实战

我已经把第十章内容整理成一篇面向从业者的博文。直接从正文开始,不加任何前置说明。开头做过多智能体系统的人一定经历过这种尴尬:智能体A用一套自定义协议兴冲冲给智能体B发了条消息,B按自己的“理解”回了串状态码,两边都觉得自…

2026/10/17 8:40:06

非final字段加锁隐患:对象身份与synchronized并发安全

1. 从一次编译警告说起的排查过程大概一年前,我在重构一个并发模块的时候,代码里出现了一个很不起眼的警告:WARNING: Synchronization on a non-final field编译能过,测试也能跑,但IDEA直接在synchronized(lock)那一行…

2026/10/17 8:40:06

.NET MAUI实战迁移与跨平台部署避坑指南

简介:这是一本面向.NET开发者进阶跨平台应用开发的实战指南,聚焦.NET MAUI框架的核心原理与工程落地,特别适合已有C#和XAML基础、希望掌握MVVM架构、多平台适配及CI/CD部署全流程的中高级开发者。全书系统覆盖基础控件使用、双向数据绑定机制…

2026/10/17 8:40:06

Ubuntu 22.04离线部署TigerVNC无头远程桌面实战

项目一拿到手,我脑子里浮现的第一句话就是:又得跑机房一趟了。Ubuntu 22.04 服务器装离线远程桌面,这活儿看起来简单,实际坑比想象的多。关键词是“离线”、“TigerVNC”、“无头”。很多生产环境没有显示器、没有公网出口&#x…

2026/10/17 8:35:06

DVWA靶场实战:反射型XSS从原理到过滤绕过与防御详解

1. 从一次“偶发弹窗”说起:反射型XSS到底是怎么回事DVWA(Damn Vulnerable Web Application)是圈内几乎人人上手过的Web安全靶场模拟项目,专门为安全教学和漏洞训练设计。这次要拆解的是其中的反射型XSS(Reflected XSS…

2026/10/16 5:33:43

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/16 4:03:48

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/15 13:42:16

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/17 0:04:32

PlanarSLAM:面向曼哈顿世界的点线面联合SLAM系统

简介:本资源是面向机器人视觉与SLAM研究者的C开源RGB-D SLAM系统实现,专为结构化室内场景(如办公室、走廊)设计,通过融合点、线、平面三类几何特征并引入“曼哈顿世界”假设,显著提升位姿跟踪鲁棒性与稠密地…

2026/10/17 0:04:32

TDH Workflow调度实战:从DAG依赖驱动到任务编排与故障排查

1. 从一个调度脚本说起:TDH Workflow到底在解决什么问题第一次接触TDH的Workflow,是在一个数据清洗项目里。当时团队每天要跑几十个ETL任务,任务之间有严格的先后依赖——A表清洗完才能跑B表的关联,B表跑完才能触发C表的聚合。最开…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑