公司动态

Spring Boot+Vue医院药房库存管理系统:从业务设计到FIFO出库实战

📅 2026/8/24 20:48:58
Spring Boot+Vue医院药房库存管理系统:从业务设计到FIFO出库实战
1. 先搞清楚这个项目到底要解决什么问题医院药房药品库存管理系统听起来是个很传统的毕业设计选题但恰恰因为传统才更容易做出问题。很多同学做这类项目容易陷入一个误区把功能列表做得很长增删改查、报表统计一个不少但整个系统用起来却非常“学生气”和真实的药房管理场景脱节。这个项目的核心价值不在于你用了多新的技术栈而在于你是否真的理解了药房库存管理的核心痛点并针对性地进行了功能优化。这个项目适合两类人一是计算机、软件工程、信息管理等相关专业正在寻找一个有业务深度、能体现设计能力的毕业设计或课程设计题目的同学二是希望找一个完整、规范、可二次开发的实战项目来练手学习如何将技术应用于具体业务场景的开发者。它的关键能力不是炫技而是业务逻辑的严谨性和数据流转的闭环设计。一个优秀的库存管理系统应该能清晰地回答药是怎么进来的、怎么出去的、现在还有多少、什么时候该补货、哪些药快过期了、整个流程谁负责。所以拿到这个题目别急着去写代码。第一步应该是把药房的日常运作流程画出来找到那些让管理员头疼的“坑”比如手动盘点易错、近效期药品容易被忽略、请领与发放对不上账等。你的“优化”功能必须冲着解决这些实际痛点去。2. 从零开始环境搭建与项目骨架在开始任何编码之前先把环境准备好。一个典型的医院药房管理系统后端常采用Spring Boot前端用Vue.js或React数据库用MySQL。这是目前课程设计中最主流、资料最全、也最稳妥的技术选型组合能确保你遇到的大部分问题都能找到社区解决方案。2.1 后端环境准备 (Spring Boot)我建议使用Spring Boot 2.7.x或3.x版本搭配Maven进行依赖管理。JDK 版本对应选择 8、11 或 17。在pom.xml中除了基础的 Web、JPA 依赖有几个关键依赖必须加上数据库连接与ORM:spring-boot-starter-data-jpa和mysql-connector-java。权限控制:spring-boot-starter-security或更灵活的Sa-Token。药房系统必须区分药房管理员、采购员、发药员等角色。数据校验:spring-boot-starter-validation用于验证药品批次、数量等输入。接口文档:springdoc-openapi-ui(对应 Swagger UI)方便你调试接口也给答辩演示增色。缓存(可选但建议):spring-boot-starter-data-redis用于缓存药品目录等不常变动的数据。创建一个干净的目录结构这是良好设计的开始src/main/java/com/hospital/pharmacy/ ├── controller/ # 控制器接收前端请求 ├── service/ # 业务逻辑层 │ ├── impl/ # 业务逻辑实现 ├── repository/ # 数据访问层 (JPA接口) ├── entity/ # 实体类对应数据库表 ├── dto/ # 数据传输对象用于前后端交互 ├── vo/ # 视图对象用于返回给前端的数据封装 ├── config/ # 配置类 (安全、Redis等) └── HospitalPharmacyApplication.java # 启动类2.2 前端环境准备 (Vue 3 Element Plus)前端我推荐Vue 3配合Element Plus组件库。使用Vite作为构建工具速度更快。# 使用 npm 创建项目 npm create vuelatest hospital-pharmacy-frontend # 按照提示选择需要的特性务必加上 Router, Pinia cd hospital-pharmacy-frontend npm install element-plus element-plus/icons-vue axios npm install --save-dev sass在main.js或main.ts中引入 Element Plus 和样式import { createApp } from vue import App from ./App.vue import router from ./router import { createPinia } from pinia import ElementPlus from element-plus import element-plus/dist/index.css import * as ElementPlusIconsVue from element-plus/icons-vue const app createApp(App) const pinia createPinia() for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.use(router) app.use(pinia) app.use(ElementPlus) app.mount(#app)2.3 数据库设计 (MySQL)这是整个系统的基石设计不好后面代码写得再漂亮也白搭。不要一上来就建表先画E-R图。核心实体至少包括药品信息表 (drug):id,drug_code(药品编码),drug_name,specification(规格),manufacturer(生产厂家),unit(单位盒/瓶/支),price,category(分类抗生素、心脑血管...),stock_alert(库存预警下限)。药品批次表 (drug_batch):id,drug_id(外键),batch_number(批次号),production_date(生产日期),expiry_date(有效期至),purchase_price(进价),current_quantity(当前库存量)warehouse_location(货架位置)。这是关键优化点必须按批次管理库存才能实现“先进先出”和效期预警。入库记录表 (stock_in):id,batch_id(关联批次),in_quantity,in_time,operator_id,supplier(供应商)invoice_number(发票号)。出库记录表 (stock_out):id,batch_id,out_quantity,out_time,operator_id,prescription_id(关联处方可选)department(领用科室)patient_info(患者信息用于门诊发药)。库存盘点表 (inventory_check):id,drug_id,batch_id,system_quantity,physical_quantity(实际盘点数量),difference,check_time,checker_id,status(待审核/已确认)。用户与角色表: 标准的user,role,user_role关系。建表时注意所有涉及数量的字段使用DECIMAL类型避免浮点数精度问题。日期时间字段使用DATETIME。为drug_batch(expiry_date),stock_in(in_time),stock_out(out_time)等字段建立索引优化查询速度。使用外键约束或至少在应用层保证数据逻辑一致性。3. 核心业务逻辑与“优化功能”实现系统的基础CRUD药品信息管理、用户管理是基本功这里不赘述。我们重点拆解几个体现“优化”的核心业务模块。记住写业务代码时Service层是大脑它负责协调多个RepositoryDAO的操作并处理所有业务规则。3.1 批次管理与“先进先出”出库策略这是药房管理的核心优化。没有批次管理库存就是一潭浑水。实体与关系Drug和DrugBatch是一对多关系。一次入库操作会产生一个新的DrugBatch记录。入库逻辑 (StockInService):前端传入药品ID、入库数量、批次号、生产日期、有效期、供应商等信息。Service层首先根据药品ID检查Drug是否存在。然后检查是否已有相同批次号(batch_number)的批次。如果有则更新该批次的current_quantity这适用于补货。如果没有则创建一条新的DrugBatch记录。同时生成一条StockIn记录关联这个批次。更新药品总库存可以设计一个Drug实体中的total_quantity字段每次入库时累加。但更规范的做法是通过SUM(drug_batch.current_quantity)实时计算避免数据不一致。出库逻辑 (StockOutService)- 实现“先进先出”(FIFO): 这是优化的关键。出库时如科室请领、门诊发药不能简单地从总库存里扣必须指定从哪个批次出。Service Transactional public class StockOutService { Autowired private DrugBatchRepository batchRepository; public void outStock(Long drugId, BigDecimal requiredQuantity, String department) { ListDrugBatch batches batchRepository .findByDrugIdAndCurrentQuantityGreaterThanOrderByProductionDateAsc(drugId, BigDecimal.ZERO); // 按生产日期升序排列最早生产的在最前面 BigDecimal remaining requiredQuantity; for (DrugBatch batch : batches) { if (remaining.compareTo(BigDecimal.ZERO) 0) break; BigDecimal canTake batch.getCurrentQuantity().min(remaining); // 本次从这个批次取出的数量 batch.setCurrentQuantity(batch.getCurrentQuantity().subtract(canTake)); batchRepository.save(batch); // 记录出库明细关联到这个批次 StockOutRecord record new StockOutRecord(); record.setBatch(batch); record.setOutQuantity(canTake); record.setDepartment(department); // ... 设置其他字段 stockOutRepository.save(record); remaining remaining.subtract(canTake); } if (remaining.compareTo(BigDecimal.ZERO) 0) { throw new InsufficientStockException(药品库存不足所需数量: requiredQuantity , 可用数量: (requiredQuantity.subtract(remaining))); } } }注意这里使用了Transactional注解确保在一个数据库事务中完成所有批次的扣减和记录保存要么全部成功要么全部回滚防止数据不一致。3.2 效期预警与库存预警这两个预警功能是“优化”的直观体现能极大提升管理效率。效期预警在DrugBatchRepository中创建一个方法查询距离过期日期expiry_date还有一定天数例如30天、60天的批次。Query(SELECT b FROM DrugBatch b WHERE b.expiry_date BETWEEN :today AND :warningDate AND b.current_quantity 0) ListDrugBatch findBatchesExpiringSoon(Param(today) LocalDate today, Param(warningDate) LocalDate warningDate);可以创建一个定时任务使用Spring的Scheduled每天凌晨运行将预警结果统计后存入一张expiry_warning表或直接发送通知如系统消息、邮件毕业设计实现系统消息即可。前端在“库存查询”或单独的“效期预警”页面调用接口展示这些批次信息并用不同颜色如黄色高亮显示。库存预警在Drug实体中设置stock_alert库存下限字段。同样通过定时任务或实时查询找出total_quantity或批次库存总和小于等于stock_alert的药品。生成库存预警信息甚至可以关联生成“采购计划申请单”。3.3 库存盘点与差异处理盘点不是简单的修改库存数字而是一个流程。流程设计创建盘点任务管理员发起盘点选择要盘点的药品范围全部或部分系统冻结这些药品的当前库存快照记录system_quantity。实地盘点药房人员使用系统如移动端或打印的盘点单记录实际数量physical_quantity。录入结果将实际数量录入系统系统自动计算差异difference physical_quantity - system_quantity。差异审核差异不为零的记录进入“待审核”状态。需要主管人员确认差异原因损耗、录入错误、盗窃等。确认调整审核通过后系统根据确认的差异反向生成一条特殊的“盘点调整”入库或出库记录并更新对应批次的current_quantity。绝对不要直接去改批次库存表必须通过正式的出入库流水来调整保证账目有迹可循。3.4 统计报表与数据可视化这是毕业设计答辩的亮点。使用ECharts或AntV等图表库。核心报表药品出入库流水账按时间、药品、科室多维度查询。这是最基本也是最重要的报表。库存周转率分析计算某段时间出库成本/平均库存成本。周转率低的药品可能是滞销品需要关注。效期分布图用饼图或柱状图展示未来3个月、6个月、1年内将过期的药品数量和金额占比。科室领用排行统计各科室的药品领用金额、数量TOP榜。供应商供货分析分析各供应商的药品质量效期情况、价格、到货及时率。实现时后端提供聚合查询的接口返回结构化的数据。前端负责调用接口并渲染图表。报表最好支持选择时间范围、导出Excel等功能。4. 前后端交互与关键API设计前后端分离项目API设计要清晰、规范。建议统一使用RESTful风格返回格式统一的JSON。4.1 统一响应体创建一个通用的Result类来包装所有接口响应。Data public class ResultT { private Integer code; // 200成功500失败401未认证... private String msg; private T data; private Long timestamp System.currentTimeMillis(); public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMsg(操作成功); result.setData(data); return result; } public static T ResultT error(String msg) { ResultT result new Result(); result.setCode(500); result.setMsg(msg); return result; } }在Controller中统一返回Result对象。4.2 关键API示例药品批次分页查询(带效期预警过滤)GET /api/drug-batches?page1size10drugName阿莫西林expiringSoontrue出库操作(实现FIFO)POST /api/stock-out{ drugId: 1, quantity: 10, department: 内科门诊, patientInfo: 张三, operatorId: 2 }创建盘点任务POST /api/inventory-check/task提交盘点结果POST /api/inventory-check/{taskId}/submit获取库存周转率报表GET /api/report/turnover?startDate2023-01-01endDate2023-12-314.3 前端请求封装与状态管理使用Axios拦截器统一处理请求和响应。// request.js import axios from axios; import { ElMessage } from element-plus; import router from /router; const service axios.create({ baseURL: /api, // 注意配置Vite代理解决跨域 timeout: 10000 }); service.interceptors.request.use( config { const token localStorage.getItem(token); if (token) { config.headers[Authorization] Bearer ${token}; } return config; }, error { return Promise.reject(error); } ); service.interceptors.response.use( response { const res response.data; if (res.code ! 200) { ElMessage.error(res.msg || 请求失败); // 如果是401跳转到登录页 if (res.code 401) { router.push(/login); } return Promise.reject(new Error(res.msg || Error)); } else { return res.data; // 直接返回后端Result里的data字段 } }, error { ElMessage.error(error.message || 网络错误); return Promise.reject(error); } ); export default service;使用Pinia管理全局状态如用户信息、权限列表等。5. 项目部署、测试与答辩准备5.1 本地运行与测试启动后端运行HospitalPharmacyApplication的 main 方法。确保MySQL服务已启动数据库已创建。启动前端在frontend目录下执行npm run dev。配置代理在vite.config.js中配置代理解决开发环境跨域问题。export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, // 后端地址 changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ) } } } })测试流程务必完整走通以下核心流程用户登录、权限控制不同角色看到不同菜单。新增一种药品信息。为该药品进行一次入库操作创建批次。模拟一次出库操作观察是否按FIFO扣减了最早批次的库存。触发一次库存预警将库存量改到预警线以下。创建一个盘点任务并完成盘点、审核、调整的全流程。5.2 部署上线用于演示毕业设计答辩通常需要现场演示。最简单的部署方式是后端打包mvn clean package生成target/*.jar文件。前端打包npm run build生成dist文件夹。部署后端将 jar 包上传到服务器或本地另一台机器用java -jar your-app.jar运行。可以使用nohup或配置为系统服务。部署前端将dist文件夹内的所有文件放到 Nginx 或 Apache 的静态资源目录下。配置Nginx让 Nginx 同时提供前端静态文件并将/api路径的请求代理到后端Java服务。server { listen 80; server_name localhost; # 或你的域名/IP location / { root /path/to/your/frontend/dist; index index.html; try_files $uri $uri/ /index.html; # 支持Vue Router的history模式 } location /api/ { proxy_pass http://localhost:8080/; # 后端服务地址 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }5.3 文档与答辩要点毕业设计论文/报告绪论讲清楚传统药房管理的问题、系统优化的必要性。相关技术简要介绍 Spring Boot、Vue、MySQL 等突出你的技术选型理由。系统分析详细的功能需求分析、用例图、E-R图、数据流图。这是重点体现你的分析能力。系统设计架构图前后端分离、数据库详细设计表结构、核心类图、时序图如出库时序。系统实现展示关键代码片段如FIFO算法、预警查询配上界面截图。系统测试列出测试用例表功能测试、界面测试附上测试结果截图。总结与展望总结成果客观说明不足如未实现移动端扫码并提出未来可扩展的方向如与医院HIS系统对接、引入RFID技术。答辩PPT精简文字多用图表架构图、E-R图、界面图。演示路线先从“问题与优化目标”切入 - 展示系统整体架构 - 演示核心业务流程登录-入库-出库-预警-盘点- 展示关键报表 - 总结。准备好回答老师可能问的问题“你的系统和普通进销存有什么区别”突出批次管理、效期预警、药房特殊流程。“如果两个人同时发同一种药怎么防止库存超卖”提到数据库事务、乐观锁/悲观锁概念。“数据量大了之后查询报表慢怎么办”提到数据库索引、查询优化、考虑分库分表或读写分离的扩展思路。“你的系统安全性怎么保证”提到接口权限控制、SQL防注入、密码加密存储。6. 常见问题排查与优化建议在实际开发和调试中你肯定会遇到各种问题。这里列几个典型的排查思路前端页面空白控制台报跨域错误 (CORS)检查首先确认后端服务是否已成功启动localhost:8080。解决确保后端已配置CORS使用CrossOrigin注解或全局配置或者前端Vite代理配置正确见5.1节。数据库连接失败检查application.yml或application.properties中的数据库URL、用户名、密码是否正确。MySQL服务是否运行。解决核对配置确保数据库已创建CREATE DATABASE pharmacy_db;。出库时库存扣减出现负数或数据混乱检查这是典型的并发问题。你的“先进先出”扣减逻辑在多人同时操作时可能读取到相同的未扣减批次列表。解决在扣减批次库存的Service方法上添加Transactional保证原子性。对于高并发场景可以考虑使用数据库悲观锁SELECT ... FOR UPDATE或乐观锁版本号机制。毕业设计场景讲清楚事务的概念和重要性即可。效期预警查询速度慢检查是否在drug_batch.expiry_date字段上建立了索引。解决为经常用于查询条件的字段如expiry_date,drug_id,batch_number建立数据库索引。使用EXPLAIN语句分析查询计划。前端页面卡顿特别是报表页面检查是否一次性从后端请求了海量数据如所有出入库记录。解决后端必须支持分页查询。报表的聚合计算尽量在后端完成前端只负责渲染结果。对于复杂的统计可以考虑在后台定时计算并缓存结果。给开发者的最后建议这个项目的价值一半在代码一半在业务理解。不要只满足于让系统“跑起来”要多思考“如果我是药房管理员我还需要什么功能哪个操作可以更省力哪个数据视图更能帮助我决策”。把这些思考体现在你的设计和优化中这才是区分普通作业和优秀毕业设计的关键。先从最简单的单批次药品入库、出库流程跑通再逐步叠加预警、盘点、报表等复杂功能每一步都做好测试和数据验证。