简介:这份资源是面向高校计算机专业学生与Java初学者的一套药品信息管理系统完整项目,基于Spring Boot、Vue与MySQL技术栈开发,可作为毕业设计、课程设计或企业级后台管理练手项目。系统分为管理员与员工两类角色:管理员负责管理员信息、员工信息、药品类别与药品信息管理,并以表格和图表形式统计库存;员工可修改个人资料、管理供应商信息、查看药品信息,并完成药品入库、出库与库存统计等操作,业务闭环较为完整。压缩包共约2000个文件,以1430个md说明文档、507个js脚本、54个json配置为主,另含sql建库脚本、docx论文与html页面,整体约120.75MB,目录结构清晰,便于按模块查阅源码与文档。资源还附带需求分析、演示视频与设计实现论文,能帮助读者快速理解系统架构、数据库设计与前后端交互逻辑,对照源码完成部署与二次开发。目前已有98人学习下载,适合需要完整项目方案与排错参考的读者。
1. 药品信息管理系统到底在管什么:从一张入库单说起
药房库管老张每天要面对三件事:供货商送来的药品批号对不对、库存里哪些药还有三个月过期、处方发药时系统能不能立刻扣减对应批次的库存。这三件事背后,就是一套药品信息管理系统要解决的核心问题——批号追溯、效期预警、库存联动。基于 Spring Boot + Vue + MySQL 的药品信息管理系统,本质上是把药品的基础档案、采购入库、销售出库、库存盘点、效期告警这几条业务线串成一个闭环,让每一盒药的流向都能在数据库里查到记录。
这套技术栈适合谁?适合正在做课程设计或毕业设计的计算机专业学生,也适合中小药店、社区诊所里想自己搭一套轻量管理工具的开发者。Spring Boot 负责后端接口和业务逻辑,Vue 负责前端页面交互,MySQL 存数据,三者配合能覆盖从药品录入到报表导出的完整流程。需求分析阶段要搞清楚角色权限——管理员、库管员、普通店员看到的功能不一样,这是后面做权限拦截的基础。演示视频里通常会走一遍“新增药品→入库→出库→库存查询”的流程,源码和论文则分别对应实现细节和设计文档。这一章先把业务边界划清楚,后面再讲怎么把环境跑起来、表怎么设计、接口怎么写。
2. 环境搭建与项目骨架:把 Spring Boot、Vue、MySQL 三件套跑通
2.1 后端 Spring Boot 工程的初始化与依赖选择
拿到源码后第一步不是急着改代码,而是先把后端跑起来。我一般用 IntelliJ IDEA 社区版打开后端目录,确认pom.xml里的 Spring Boot 版本和 JDK 版本匹配。药品信息管理系统常见的依赖包括spring-boot-starter-web(提供 REST 接口)、mybatis-plus-boot-starter或spring-boot-starter-data-jpa(操作数据库)、mysql-connector-java(MySQL 驱动)、lombok(简化实体类)。如果源码用的是 MyBatis-Plus,那基本不用写太多 XML,实体类加注解就能映射表。
<!-- pom.xml 关键依赖片段 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3</version> <!-- 按源码实际版本调整 --> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>逻辑说明:spring-boot-starter-web内嵌 Tomcat,启动后默认监听 8080 端口;MyBatis-Plus 的BaseMapper提供了单表增删改查,药品档案、库存记录这类表用它能省掉大量重复 SQL。参数方面,如果 8080 被占用,在application.yml里改server.port即可,比如改成 9090。注意 MySQL 驱动版本要和本机 MySQL 版本对应,MySQL 8.0 以上用com.mysql.cj.jdbc.Driver,5.7 用com.mysql.jdbc.Driver,写错会报Loading class ... is deprecated或直接连接失败。
2.2 Vue 前端环境配置与依赖安装
前端部分通常是 Vue 2 或 Vue 3 项目,用 Vue CLI 或 Vite 构建。先确认本机 Node.js 版本,Vue 2 项目建议 Node 14~16,Vue 3 + Vite 建议 Node 16 以上。进入前端目录后执行npm install或yarn install,如果卡在某个包下载不动,换国内镜像源再试。安装完成后npm run serve启动开发服务器,默认端口 8080 或 8081,如果和后端端口冲突,在vue.config.js里改devServer.port。
# 前端启动常用命令 npm install # 安装依赖,首次执行时间较长 npm run serve # 开发模式启动,支持热更新 npm run build # 打包生成 dist 目录,用于部署逻辑说明:npm run serve启动的是开发服务器,接口请求通常通过vue.config.js里的proxy转发到后端,避免跨域。参数上,proxy的target要写成后端实际地址,比如http://localhost:9090。如果打包后要放进 Spring Boot 的static目录,publicPath要改成./,否则刷新页面会 404。这里有个血泪经验:前端路由用 history 模式时,Spring Boot 需要配置一个 fallback 到index.html的控制器,不然直接访问/drug/list这种路径会报 404。
2.3 MySQL 建库建表与初始数据导入
数据库是药品信息管理系统的地基。先在 MySQL 里建一个库,字符集用utf8mb4,排序规则utf8mb4_general_ci。然后执行源码里附带的.sql文件,通常会创建drug_info(药品档案)、stock_in(入库记录)、stock_out(出库记录)、inventory(库存)、user(用户)等表。导入前注意检查 SQL 文件里有没有CREATE DATABASE语句,如果没有就手动建库再USE。
-- 建库与建表示例(简化版) CREATE DATABASE drug_mgr DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE drug_mgr; CREATE TABLE drug_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, drug_name VARCHAR(100) NOT NULL COMMENT '药品名称', batch_no VARCHAR(50) NOT NULL COMMENT '生产批号', expire_date DATE NOT NULL COMMENT '有效期至', stock_qty INT DEFAULT 0 COMMENT '库存数量', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT '药品基础信息表';逻辑说明:batch_no和expire_date是药品管理的核心字段,批号用于追溯,效期用于预警。stock_qty不建议直接手动改,而是通过入库、出库操作自动增减,避免数据不一致。参数上,expire_date用DATE类型就够,不需要精确到秒。导入数据时如果遇到Incorrect string value报错,检查数据库和表的字符集是不是utf8mb4,以及连接串里有没有加characterEncoding=utf8。
3. 核心业务表设计与接口实现:药品档案、库存、效期怎么落库
3.1 药品档案表与库存表的字段设计要点
药品档案表不是简单记个名字就行。实际业务里,同一种药可能有不同厂家、不同规格、不同批号,所以drug_info表里通常要有drug_code(药品编码)、drug_name、spec(规格)、manufacturer(生产厂家)、unit(单位)、category(类别)这些字段。库存表inventory则要关联drug_id和batch_no,因为同一药品不同批号的效期不一样,必须分开管理。常见做法是库存表用drug_id + batch_no做联合唯一索引,入库时如果已有相同批号就累加数量,没有就新增一条。
| 字段名 | 类型 | 说明 | 是否必填 |
|---|---|---|---|
| drug_code | VARCHAR(50) | 药品编码,唯一 | 是 |
| drug_name | VARCHAR(100) | 药品名称 | 是 |
| spec | VARCHAR(50) | 规格,如 0.25g*24粒 | 是 |
| batch_no | VARCHAR(50) | 生产批号 | 是 |
| expire_date | DATE | 有效期至 | 是 |
| stock_qty | INT | 当前库存 | 是 |
| warn_threshold | INT | 库存预警阈值 | 否 |
逻辑说明:warn_threshold是给库存预警用的,比如设成 10,当stock_qty低于 10 时前端标红提醒。batch_no和expire_date组合起来能算出近效期药品列表。参数上,expire_date建议加索引,因为效期查询是高频操作。注意不要用drug_name做唯一键,同名不同厂的药很常见,用drug_code更稳妥。
3.2 入库与出库接口的编写与库存联动
入库接口要做两件事:写一条入库记录,同时更新库存。出库接口同理,但要先校验库存够不够。用 MyBatis-Plus 的话,可以在 Service 层用@Transactional保证两个操作要么都成功要么都回滚。下面是一个简化的入库接口示例。
@RestController @RequestMapping("/api/stock") public class StockController { @Autowired private InventoryService inventoryService; @PostMapping("/in") @Transactional(rollbackFor = Exception.class) public Result stockIn(@RequestBody StockInDTO dto) { // 1. 写入库记录 StockIn record = new StockIn(); record.setDrugId(dto.getDrugId()); record.setBatchNo(dto.getBatchNo()); record.setQty(dto.getQty()); record.setInTime(new Date()); stockInMapper.insert(record); // 2. 更新库存:存在则累加,不存在则新增 Inventory inv = inventoryService.getByDrugIdAndBatch(dto.getDrugId(), dto.getBatchNo()); if (inv == null) { inv = new Inventory(); inv.setDrugId(dto.getDrugId()); inv.setBatchNo(dto.getBatchNo()); inv.setStockQty(dto.getQty()); inventoryService.save(inv); } else { inv.setStockQty(inv.getStockQty() + dto.getQty()); inventoryService.updateById(inv); } return Result.ok("入库成功"); } }逻辑说明:@Transactional注解保证入库记录和库存更新在同一个事务里,任何一步失败都会回滚。参数上,StockInDTO里至少要包含drugId、batchNo、qty,前端传参时注意qty要大于 0。出库接口要多一步stockQty >= qty的判断,不够就抛异常返回“库存不足”。这里有个踩坑点:并发出库时可能两个请求同时读到相同库存然后都扣减,导致超卖。解决办法是在库存表加乐观锁版本号,或者用UPDATE inventory SET stock_qty = stock_qty - ? WHERE drug_id = ? AND batch_no = ? AND stock_qty >= ?这种原子操作。
3.3 效期预警与库存预警的查询实现
效期预警的 SQL 逻辑不复杂:查expire_date在当前日期到未来 90 天之间的库存记录,按expire_date升序排列。库存预警则是查stock_qty < warn_threshold的记录。这两个查询可以放在一个/api/warn/list接口里返回,前端用两个 Tab 展示。
-- 近效期药品查询(90天内) SELECT d.drug_name, d.spec, i.batch_no, i.expire_date, i.stock_qty FROM inventory i JOIN drug_info d ON i.drug_id = d.id WHERE i.expire_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 90 DAY) ORDER BY i.expire_date ASC; -- 库存不足预警 SELECT d.drug_name, d.spec, i.stock_qty, d.warn_threshold FROM inventory i JOIN drug_info d ON i.drug_id = d.id WHERE i.stock_qty < d.warn_threshold;逻辑说明:BETWEEN ... AND ...包含边界值,DATE_ADD(CURDATE(), INTERVAL 90 DAY)算出 90 天后的日期。参数上,90 天这个阈值可以做成配置项,不同药店要求不一样。注意expire_date如果存的是字符串而不是DATE类型,比较会出错,建表时一定要用日期类型。前端拿到数据后用表格展示,近效期行标黄,已过期行标红,这是演示视频里比较出效果的部分。
4. 避坑与排查:药品管理系统落地时最容易翻车的 5 个地方
4.1 中文乱码:从数据库到前端一路问号
现象:新增药品时名称显示成???或者乱码。原因通常是 MySQL 库、表、连接串三处字符集不一致。解决:建库时指定utf8mb4,连接串加useUnicode=true&characterEncoding=utf8,Spring Boot 的application.yml里url也要带上这个参数。如果已经建了表,用ALTER TABLE drug_info CONVERT TO CHARACTER SET utf8mb4;改一下。
4.2 端口冲突:8080 被占导致启动失败
现象:Spring Boot 启动报Port 8080 was already in use,或者 Vue 启动报端口被占用。原因:本机装了其他服务(比如另一个 Tomcat、Nginx)占了 8080。解决:后端改server.port=9090,前端改devServer.port=8081,同时检查vue.config.js里的proxy目标端口有没有同步改。
4.3 跨域请求被拦截:前端调后端接口报 CORS 错误
现象:浏览器控制台报Access-Control-Allow-Origin相关错误。原因:前端开发服务器和后端端口不同,浏览器同源策略拦截。解决:后端加一个全局 CORS 配置类,允许前端地址访问;或者在前端vue.config.js里配proxy转发。两种方式选一种就行,不要同时用,否则可能重复加头导致报错。
4.4 库存扣减出现负数:并发下的超卖问题
现象:出库操作后库存变成负数。原因:两个请求同时读到相同库存,各自扣减后写回,后写的覆盖了先写的。解决:用原子 SQLUPDATE inventory SET stock_qty = stock_qty - ? WHERE drug_id = ? AND batch_no = ? AND stock_qty >= ?,根据返回的影响行数判断是否成功。或者给库存表加version字段做乐观锁。
4.5 打包部署后刷新 404:Vue history 模式与 Spring Boot 静态资源冲突
现象:npm run build后把dist放进 Spring Boot 的static目录,访问首页正常,但刷新/drug/list就 404。原因:Vue 用 history 模式时,前端路由由 JavaScript 控制,刷新时浏览器直接请求后端,后端没有对应路径。解决:Spring Boot 加一个ErrorController或者配置类,把所有未匹配的请求转发到index.html。或者前端改用 hash 模式,但 URL 会带#,看个人取舍。
5. 从能跑到好用:药品管理系统的进阶技巧与验证方法
5.1 用 Spring Boot Admin 监控接口健康状态
项目跑起来之后,怎么知道后端接口是不是正常?我一般会加spring-boot-starter-actuator暴露健康检查端点,再配一个 Spring Boot Admin 服务端来可视化监控。这样能实时看到/health、/metrics这些信息,接口响应慢或者数据库连接池满了能提前发现。配置上,management.endpoints.web.exposure.include=health,info,metrics按需开放,不要全部暴露到公网。
5.2 用 ECharts 做药品销售统计图
演示视频里如果只有表格会显得单薄,加一个 ECharts 柱状图展示近 7 天出库量,或者饼图展示药品类别占比,效果会好很多。Vue 里用echarts的init方法绑定一个div,数据从后端/api/stat/sales接口拿。注意图表容器要有明确高度,否则渲染不出来。参数上,xAxis的type设成category,yAxis设成value,数据更新时调setOption而不是重新init。
5.3 验证系统是否真正可用的三个检查点
第一个检查点:新增一个药品,入库 100 盒,出库 30 盒,查库存是不是 70。第二个检查点:把某个批号的效期改成 60 天后,看预警列表里有没有它。第三个检查点:用普通店员账号登录,看能不能看到删除药品的按钮——如果能看到,说明权限拦截没生效。这三个点过了,系统基本就能拿去演示了。
5.4 源码阅读与二次开发的入手顺序
拿到源码后不要从头到尾读,按这个顺序效率最高:先看application.yml确认数据库和端口配置,再看entity包了解表结构,然后看controller包知道有哪些接口,最后看service包理解业务逻辑。前端先看router知道有哪些页面,再看api目录知道调了哪些后端接口。二次开发时,加新功能就照着已有的 Controller-Service-Mapper 三层抄一遍,改改表名和字段就行。
我自己的习惯是,每次改完代码先跑一遍那三个检查点,确认没把原来的功能弄坏。药品管理系统的核心就是库存和效期,这两块不出错,其他都是锦上添花。希望帮到你。
本文还有配套的精品资源,点击获取