资讯动态

基于Spring Boot+Vue的医院药房库存管理系统设计与实现

发布时间:2026/8/24 1:42:59 来源:尧图企业网站定制
1. 项目背景与核心价值在医疗信息化高速发展的今天医院药房作为药品流通的核心环节其管理水平直接关系到医疗安全、运营效率和患者满意度。传统的药品库存管理多依赖人工记录和Excel表格不仅效率低下而且极易出现账实不符、药品过期、库存积压或短缺等问题。对于计算机、软件工程等相关专业的同学而言开发一个“医院药房药品库存管理系统”是一个绝佳的毕业设计或课程设计选题。它综合了数据库设计、前后端开发、业务流程梳理和系统优化等多个核心技能点具有很强的实战性和应用价值。本文将围绕一个功能完善的医院药房药品库存管理系统展开不仅提供一套可直接运行的完整源码更会深入剖析其核心功能模块并在此基础上提出一系列功能优化方案。无论你是正在寻找毕业设计/课程设计题目的同学还是希望学习企业级Web项目开发流程的开发者都能从本文中获得从需求分析、技术选型、编码实现到系统优化的全链路实战经验。学完本文你将能够独立搭建并理解一个具备药品入库、出库、盘点、效期预警、统计报表等核心功能的Web应用并掌握如何对其进行性能、安全性和用户体验的优化。2. 系统核心功能模块拆解一个完整的医院药房药品库存管理系统其核心业务围绕药品的“进、销、存”展开。下面我们将其拆解为几个关键模块进行详细说明。2.1 基础数据管理模块这是系统的基石负责维护所有静态基础信息。药品信息管理包括药品编码、通用名、商品名、规格、剂型、生产厂家、批准文号、单价等。药品编码应遵循院内或国家相关标准如医保编码确保唯一性。供应商管理记录药品供应商的名称、联系人、电话、地址等信息便于采购溯源。药房/库位管理对于大型医院可能有中心药库、门诊药房、住院药房等多个物理或逻辑库位需要管理其所属关系。用户与权限管理系统用户包括药库管理员、药房药师、采购员等。需实现基于角色的访问控制RBAC例如管理员拥有全部权限药师只能进行发药和盘点操作。2.2 库存核心业务流程模块这是系统最核心的部分实现了药品的日常流动。采购入库药库管理员根据采购计划生成入库单录入药品批次、生产日期、有效期至、入库数量、采购单价等信息。系统需自动更新库存总量和对应批次的库存。药品出库主要包括“药房申领”和“处方发药”两种场景。药房申领门诊/住院药房向中心药库申领药品生成申领单经库管审核后形成出库单减少中心库库存增加药房库存。处方发药药师根据医生开具的电子处方进行发药操作系统自动扣除相应药房的库存。这是与HIS医院信息系统集成的关键点。库存盘点定期如每月或随机对实物库存进行清点录入盘点数据系统自动生成盘点盈亏报表并支持根据盘点结果调整系统库存确保账实相符。库存调拨在不同药房之间进行药品调拨记录调出方和调入方。2.3 智能监控与预警模块这是体现系统价值、提升管理水平的“大脑”。库存量预警为每种药品设置上下限库存。当库存低于下限时系统预警提示需采购当库存高于上限时提示库存积压风险。药品效期预警这是药房管理的重中之重。系统需自动监控所有批次药品的有效期在药品过期前一定时间如3个月、1个月进行分级预警黄色预警、红色预警并提供近效期药品报表便于药师优先发放避免药品过期造成损失。滞销药品分析统计长期无流动的药品为采购决策提供数据支持。2.4 查询统计与报表模块为管理决策提供数据可视化支持。多维库存查询可按药品名称、供应商、库位、批次、效期等多条件组合查询实时库存。出入库流水查询追踪每一笔库存变动的来龙去脉形成完整的药品流转档案。业务统计报表生成日报、月报、年报如采购入库汇总表、药品出库汇总表、盘点盈亏表、效期预警报表等。通常以表格和图表如柱状图、折线图形式呈现。3. 技术栈选型与环境搭建本项目采用当前企业主流的“前后端分离”架构使得前端展示与后端业务逻辑解耦便于独立开发和部署。3.1 技术栈说明后端核心框架Spring Boot 2.7.x。它简化了Spring应用的初始搭建和开发过程内嵌Tomcat服务器实现开箱即用。持久层框架MyBatis-Plus 3.5.x。在MyBatis的基础上只做增强不做改变提供了强大的CRUD操作和条件构造器极大提高开发效率。数据库MySQL 8.0。关系型数据库用于存储所有业务数据。也可选用MariaDB。权限控制Spring Security JWT (JSON Web Token)。用于实现用户认证和接口访问授权保证系统安全。API文档Knife4j (Swagger增强)。自动生成美观的在线API文档方便前后端联调。前端框架Vue 3.x Composition API。渐进式JavaScript框架构建用户界面。构建工具Vite。新一代前端构建工具提供极速的服务启动和热更新。UI组件库Element Plus。基于Vue 3的桌面端组件库提供丰富的现成组件快速构建页面。状态管理Pinia。Vue官方推荐的状态管理库比Vuex更简洁。路由Vue Router 4.x。HTTP客户端Axios。开发工具与环境JDK17 或 11 (LTS版本)Node.js18.x 或更高IDEIntelliJ IDEA (后端) Visual Studio Code (前端)项目管理Maven 3.6 (后端) npm / yarn / pnpm (前端)版本控制Git3.2 后端项目初始化与配置创建Spring Boot项目使用 Spring Initializr 或IDE的Spring Boot向导创建项目。Project: MavenLanguage: JavaSpring Boot: 2.7.xDependencies: 勾选Spring Web,MyBatis Framework,MySQL Driver,Lombok(简化实体类代码)。添加额外依赖在pom.xml中手动添加以下依赖。!-- MyBatis-Plus -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency !-- Spring Security -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency !-- JWT -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.11.5/version scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-jackson/artifactId version0.11.5/version scoperuntime/scope /dependency !-- Knife4j API文档 -- dependency groupIdcom.github.xiaoymin/groupId artifactIdknife4j-spring-boot-starter/artifactId version3.0.3/version /dependency配置数据库与MyBatis-Plus在application.yml中配置。server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/hospital_pharmacy_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL生产环境关闭 global-config: db-config: logic-delete-field: deleted # 全局逻辑删除字段名 logic-delete-value: 1 # 逻辑已删除值 logic-not-delete-value: 0 # 逻辑未删除值 mapper-locations: classpath*:/mapper/**/*.xml knife4j: enable: true setting: language: zh_cn3.3 前端项目初始化与配置创建Vue项目使用Vite官方模板。npm create vuelatest hospital-pharmacy-frontend # 根据提示选择Vue Router, Pinia, ESLint cd hospital-pharmacy-frontend npm install安装Element Plus和Axiosnpm install element-plus axios npm install element-plus/icons-vue # 图标库配置Element Plus和Axios在src/main.js或src/main.ts中引入。import { createApp } from vue import { createPinia } from pinia import ElementPlus from element-plus import element-plus/dist/index.css import * as ElementPlusIconsVue from element-plus/icons-vue import App from ./App.vue import router from ./router import axios from axios const app createApp(App) // 注册所有图标 for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.use(createPinia()) app.use(router) app.use(ElementPlus) // 配置axios基础URL axios.defaults.baseURL http://localhost:8080/api // 请求拦截器用于添加JWT Token axios.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) // 响应拦截器统一处理错误 axios.interceptors.response.use( response response.data, error { console.error(API请求错误:, error) return Promise.reject(error) } ) // 将axios挂载到全局属性方便在组件中使用 this.$axios app.config.globalProperties.$axios axios app.mount(#app)4. 数据库设计与核心表结构良好的数据库设计是系统稳定高效运行的基础。以下是核心业务表的设计示例。-- 1. 药品信息表 CREATE TABLE medicine ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键ID, medicine_code varchar(64) NOT NULL COMMENT 药品编码唯一, generic_name varchar(255) NOT NULL COMMENT 通用名, trade_name varchar(255) DEFAULT NULL COMMENT 商品名, specification varchar(100) NOT NULL COMMENT 规格如0.25g*24片, dosage_form varchar(50) DEFAULT NULL COMMENT 剂型片剂、胶囊、注射液等, manufacturer varchar(255) DEFAULT NULL COMMENT 生产厂家, unit varchar(20) NOT NULL COMMENT 单位盒、瓶、支等, unit_price decimal(10,2) NOT NULL COMMENT 单价, stock_min int DEFAULT 0 COMMENT 库存下限, stock_max int DEFAULT 0 COMMENT 库存上限, status tinyint DEFAULT 1 COMMENT 状态1-启用0-停用, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_medicine_code (medicine_code) ) ENGINEInnoDB COMMENT药品信息表; -- 2. 供应商表 CREATE TABLE supplier ( id bigint NOT NULL AUTO_INCREMENT, supplier_name varchar(255) NOT NULL COMMENT 供应商名称, contact_person varchar(50) DEFAULT NULL, contact_phone varchar(20) DEFAULT NULL, address varchar(500) DEFAULT NULL, status tinyint DEFAULT 1, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB COMMENT供应商表; -- 3. 库位表 CREATE TABLE storage_location ( id bigint NOT NULL AUTO_INCREMENT, location_code varchar(50) NOT NULL COMMENT 库位编码, location_name varchar(100) NOT NULL COMMENT 库位名称如中心药库、门诊药房, parent_id bigint DEFAULT 0 COMMENT 父级ID用于树形结构, type tinyint DEFAULT 1 COMMENT 类型1-药库2-药房, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB COMMENT库位表; -- 4. 库存批次表核心表记录每个批次药品的详细信息 CREATE TABLE inventory_batch ( id bigint NOT NULL AUTO_INCREMENT, medicine_id bigint NOT NULL COMMENT 药品ID, batch_number varchar(100) NOT NULL COMMENT 批次号, production_date date NOT NULL COMMENT 生产日期, expiry_date date NOT NULL COMMENT 有效期至, location_id bigint NOT NULL COMMENT 所在库位ID, current_quantity int NOT NULL DEFAULT 0 COMMENT 当前库存数量, total_quantity int NOT NULL DEFAULT 0 COMMENT 历史总入库数量, unit_price decimal(10,2) NOT NULL COMMENT 入库单价, supplier_id bigint DEFAULT NULL COMMENT 供应商ID, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_medicine_location (medicine_id,location_id), KEY idx_expiry_date (expiry_date) COMMENT 用于效期预警查询, CONSTRAINT fk_batch_medicine FOREIGN KEY (medicine_id) REFERENCES medicine (id), CONSTRAINT fk_batch_location FOREIGN KEY (location_id) REFERENCES storage_location (id), CONSTRAINT fk_batch_supplier FOREIGN KEY (supplier_id) REFERENCES supplier (id) ) ENGINEInnoDB COMMENT库存批次表; -- 5. 入库单主表 CREATE TABLE stock_in_main ( id bigint NOT NULL AUTO_INCREMENT, in_order_no varchar(50) NOT NULL COMMENT 入库单号规则IN日期序列, supplier_id bigint DEFAULT NULL, location_id bigint NOT NULL COMMENT 入库库位, total_amount decimal(15,2) DEFAULT NULL COMMENT 入库总金额, status tinyint DEFAULT 1 COMMENT 状态1-待审核2-已入库3-已取消, remark varchar(500) DEFAULT NULL, create_user_id bigint DEFAULT NULL, create_time datetime DEFAULT CURRENT_TIMESTAMP, audit_user_id bigint DEFAULT NULL, audit_time datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_in_order_no (in_order_no) ) ENGINEInnoDB COMMENT入库单主表; -- 6. 入库单明细表 CREATE TABLE stock_in_detail ( id bigint NOT NULL AUTO_INCREMENT, main_id bigint NOT NULL COMMENT 主表ID, medicine_id bigint NOT NULL, batch_number varchar(100) NOT NULL, production_date date NOT NULL, expiry_date date NOT NULL, quantity int NOT NULL COMMENT 入库数量, unit_price decimal(10,2) NOT NULL, subtotal decimal(15,2) GENERATED ALWAYS AS ((quantity * unit_price)) STORED COMMENT 小计数量*单价, PRIMARY KEY (id), KEY idx_main_id (main_id), CONSTRAINT fk_detail_main FOREIGN KEY (main_id) REFERENCES stock_in_main (id) ON DELETE CASCADE ) ENGINEInnoDB COMMENT入库单明细表; -- 7. 出库单主表 明细表结构与入库类似略 -- 8. 用户表、角色表、权限表RBAC标准设计略设计要点说明库存批次表 (inventory_batch)是核心它记录了药品、批次、库位、数量、效期的精确对应关系是实现批次管理和效期预警的基础。采用“单据主表明细表”的模式记录出入库流水保证业务操作的原子性和可追溯性。使用逻辑删除(deleted字段)避免物理删除数据导致的历史记录丢失。为频繁查询的字段如expiry_date,medicine_id,location_id建立索引提升查询性能。5. 核心业务功能实现与代码讲解我们以“采购入库”和“效期预警”两个核心功能为例讲解后端Service层的实现逻辑。5.1 采购入库业务实现入库业务的核心是事务性既要保存入库单据又要更新对应批次的库存。// 文件路径src/main/java/com/hospital/pharmacy/service/impl/StockInServiceImpl.java Service Transactional(rollbackFor Exception.class) // 声明式事务异常则回滚 Slf4j public class StockInServiceImpl extends ServiceImplStockInMainMapper, StockInMain implements StockInService { Autowired private StockInDetailMapper detailMapper; Autowired private InventoryBatchService batchService; Autowired private MedicineService medicineService; Override public boolean saveStockInOrder(StockInMain main, ListStockInDetail details) { // 1. 参数校验 if (main null || CollectionUtils.isEmpty(details)) { throw new BusinessException(入库单数据不完整); } // 生成唯一的入库单号 (示例IN202405200001) String orderNo IN LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE) String.format(%04d, IdWorker.getId() % 10000); main.setInOrderNo(orderNo); main.setStatus(1); // 初始状态待审核 // 2. 保存入库单主表 if (!save(main)) { throw new BusinessException(保存入库单主表失败); } // 3. 遍历明细保存明细并更新库存 for (StockInDetail detail : details) { detail.setMainId(main.getId()); // 保存明细 if (detailMapper.insert(detail) 0) { throw new BusinessException(保存入库明细失败); } // 4. 核心更新或插入库存批次记录 // 构建查询条件同一药品、同一批次、同一库位 LambdaQueryWrapperInventoryBatch queryWrapper new LambdaQueryWrapper(); queryWrapper.eq(InventoryBatch::getMedicineId, detail.getMedicineId()) .eq(InventoryBatch::getBatchNumber, detail.getBatchNumber()) .eq(InventoryBatch::getLocationId, main.getLocationId()); InventoryBatch batch batchService.getOne(queryWrapper); if (batch ! null) { // 批次已存在更新库存数量 batch.setCurrentQuantity(batch.getCurrentQuantity() detail.getQuantity()); batch.setTotalQuantity(batch.getTotalQuantity() detail.getQuantity()); // 如果单价不同可以按加权平均等方式更新这里简单处理为以新单价为准根据业务 batch.setUnitPrice(detail.getUnitPrice()); if (!batchService.updateById(batch)) { throw new BusinessException(更新库存批次失败); } } else { // 批次不存在创建新的批次记录 batch new InventoryBatch(); batch.setMedicineId(detail.getMedicineId()); batch.setBatchNumber(detail.getBatchNumber()); batch.setProductionDate(detail.getProductionDate()); batch.setExpiryDate(detail.getExpiryDate()); batch.setLocationId(main.getLocationId()); batch.setCurrentQuantity(detail.getQuantity()); batch.setTotalQuantity(detail.getQuantity()); batch.setUnitPrice(detail.getUnitPrice()); batch.setSupplierId(main.getSupplierId()); if (!batchService.save(batch)) { throw new BusinessException(新增库存批次失败); } } // 5. (可选) 更新药品信息表中的参考单价 Medicine medicine medicineService.getById(detail.getMedicineId()); if (medicine ! null medicine.getUnitPrice().compareTo(detail.getUnitPrice()) ! 0) { medicine.setUnitPrice(detail.getUnitPrice()); medicineService.updateById(medicine); } } // 6. 计算并更新入库单总金额 (也可以在数据库中用触发器或生成列实现) BigDecimal totalAmount details.stream() .map(StockInDetail::getSubtotal) .reduce(BigDecimal.ZERO, BigDecimal::add); main.setTotalAmount(totalAmount); updateById(main); log.info(采购入库单创建成功单号{}, orderNo); return true; } Override public boolean auditStockInOrder(Long orderId, Long auditorId, boolean pass) { StockInMain order getById(orderId); if (order null || order.getStatus() ! 1) { throw new BusinessException(单据不存在或状态不可审核); } if (pass) { order.setStatus(2); // 审核通过状态变为已入库 // 审核通过后库存已经在save时更新了这里只需要更新状态。 // 如果业务要求审核后才更新库存则需要将库存更新逻辑移到这里。 } else { order.setStatus(3); // 审核不通过取消 // 如果审核不通过需要回滚之前增加的库存这里需要额外的库存冲减逻辑 // 这是一个重要的业务点实现略复杂需要根据实际需求设计。 } order.setAuditUserId(auditorId); order.setAuditTime(LocalDateTime.now()); return updateById(order); } }代码讲解事务管理Transactional注解确保整个入库操作保存单据、更新库存是一个原子操作要么全部成功要么全部回滚防止出现单据保存了但库存没更新的数据不一致情况。库存更新策略采用“存在则更新不存在则新增”的策略维护inventory_batch表。这是实现批次管理的关键。业务异常使用自定义的BusinessException在业务逻辑出错时抛出由Spring统一处理并返回给前端友好的错误信息。审核逻辑展示了简单的审核流程。实际项目中审核不通过可能需要复杂的库存冲减操作。5.2 效期预警功能实现效期预警是一个典型的后台定时任务或查询功能。// 文件路径src/main/java/com/hospital/pharmacy/service/impl/InventoryAlertServiceImpl.java Service Slf4j public class InventoryAlertServiceImpl implements InventoryAlertService { Autowired private InventoryBatchMapper batchMapper; Override public ListExpiryAlertVO getExpiryAlertList(Integer days) { // 参数默认值处理 if (days null || days 0) { days 90; // 默认预警未来90天内过期的药品 } LocalDate alertDate LocalDate.now().plusDays(days); // 使用MyBatis-Plus的Wrapper构建查询条件 LambdaQueryWrapperInventoryBatch queryWrapper new LambdaQueryWrapper(); // 查询条件有效期在当天之后且在预警日期之前且当前库存大于0 queryWrapper.le(InventoryBatch::getExpiryDate, alertDate) .gt(InventoryBatch::getExpiryDate, LocalDate.now()) .gt(InventoryBatch::getCurrentQuantity, 0) .orderByAsc(InventoryBatch::getExpiryDate); // 按效期升序排列 ListInventoryBatch batchList batchMapper.selectList(queryWrapper); // 将Entity转换为前端需要的VO (View Object) ListExpiryAlertVO alertList batchList.stream().map(batch - { ExpiryAlertVO vo new ExpiryAlertVO(); BeanUtils.copyProperties(batch, vo); // 计算剩余天数 long daysRemaining ChronoUnit.DAYS.between(LocalDate.now(), batch.getExpiryDate()); vo.setDaysRemaining((int) daysRemaining); // 设置预警级别 if (daysRemaining 30) { vo.setAlertLevel(紧急); // 红色预警 } else if (daysRemaining 90) { vo.setAlertLevel(警告); // 黄色预警 } else { vo.setAlertLevel(提示); } // 关联查询药品名称、库位名称这里假设VO已通过Mapper的关联查询或后续服务填充了这些字段 // 实际项目中建议在SQL层通过JOIN一次性查询出来性能更优。 return vo; }).collect(Collectors.toList()); return alertList; } // 另一种实现直接在Mapper.xml中编写复杂SQL进行关联查询和计算 // 文件路径src/main/resources/mapper/InventoryBatchMapper.xml /* select idselectExpiryAlert resultTypecom.hospital.pharmacy.model.vo.ExpiryAlertVO SELECT ib.*, m.generic_name, m.trade_name, m.specification, sl.location_name, DATEDIFF(ib.expiry_date, CURDATE()) AS days_remaining, CASE WHEN DATEDIFF(ib.expiry_date, CURDATE()) 30 THEN 紧急 WHEN DATEDIFF(ib.expiry_date, CURDATE()) 90 THEN 警告 ELSE 提示 END AS alert_level FROM inventory_batch ib LEFT JOIN medicine m ON ib.medicine_id m.id LEFT JOIN storage_location sl ON ib.location_id sl.id WHERE ib.expiry_date CURDATE() AND ib.expiry_date DATE_ADD(CURDATE(), INTERVAL #{days} DAY) AND ib.current_quantity 0 ORDER BY ib.expiry_date ASC /select */ }代码讲解查询逻辑核心是查询expiry_date在指定天数范围内的库存批次。使用LocalDate进行日期计算更安全、直观。预警分级根据剩余天数动态计算预警级别紧急、警告、提示便于前端进行不同颜色的高亮显示。性能考虑在InventoryBatchMapper.xml中注释的SQL示例是一种更高效的实现。它将关联查询药品名、库位名和计算字段剩余天数、预警级别都在数据库层完成减少了Java应用层的计算和数据库交互次数尤其在海量数据时优势明显。这是常见的优化手段。6. 系统功能优化方案在实现基础功能后我们可以从多个维度对系统进行优化提升其性能、安全性和用户体验。6.1 性能优化数据库索引优化除了主键索引务必为高频查询条件字段建立索引如inventory_batch表的(medicine_id, location_id)联合索引、expiry_date单字段索引。定期使用EXPLAIN分析慢SQL优化查询语句。缓存应用基础数据缓存药品信息、供应商信息、库位信息等变动不频繁的数据可以使用Redis或Caffeine进行缓存避免每次查询都访问数据库。// 示例使用Spring Cache Redis缓存药品信息 Cacheable(value medicine, key #id) public Medicine getMedicineById(Long id) { return getById(id); } CacheEvict(value medicine, key #medicine.id) public boolean updateMedicine(Medicine medicine) { return updateById(medicine); }热点数据缓存如当前登录用户信息、权限列表。异步处理对于耗时的操作如生成复杂的月度统计报表、导出大量数据到Excel可以采用异步任务如SpringAsync或消息队列如RabbitMQ来处理避免阻塞主请求线程提升接口响应速度。前端性能优化组件懒加载Vue Router的路由配置中使用() import(...)实现页面懒加载。图片等资源优化压缩图片使用CDN。API请求防抖与节流对于搜索框输入等频繁触发的请求使用防抖函数减少不必要的请求。6.2 安全优化接口安全JWT Token刷新机制设置较短的Access Token有效期如30分钟和较长的Refresh Token有效期如7天。前端在Access Token过期后使用Refresh Token静默获取新的Access Token提升用户体验和安全性。接口防刷对登录、短信验证码等接口使用IP限流或图形验证码防止恶意攻击。数据安全SQL注入防护坚持使用MyBatis-Plus的Wrapper或#{}预编译方式传参严禁字符串拼接SQL。XSS防护对用户输入的内容进行转义或过滤或使用前端框架如Vue的默认文本绑定避免直接使用v-html。敏感数据脱敏在日志或返回给前端的某些场景中对手机号、身份证号等敏感信息进行部分隐藏如138****1234。权限细化在RBAC的基础上实现数据权限控制。例如门诊药房的药师只能操作和查看本药房的库存数据不能看到住院药房的数据。这需要在查询时动态添加location_id过滤条件。6.3 业务功能增强库存快照与历史记录当前设计只能查看当前库存和流水。可以增加“库存快照表”每天凌晨记录所有批次药品的库存数量。结合流水可以回溯历史上任意时间点的精确库存满足审计和复盘需求。智能采购建议基于历史出库数据可关联出库单使用简单的移动平均法或更复杂的预测算法计算每种药品的未来消耗趋势结合当前库存、在途库存已采购未入库和效期自动生成采购建议单辅助采购决策。移动端支持开发微信小程序或H5页面供药师进行简单的盘点、查询、效期扫描结合扫码枪等移动化操作提升工作效率。与HIS/LIS系统集成这是医院实际应用的关键。需要定义标准的接口如WebService或HTTP API从HIS接收患者信息和电子处方完成发药扣减库存后将发药状态回传给HIS。这涉及到系统间数据一致性、接口幂等性等复杂问题。7. 常见问题与排查思路 (FAQ)在开发和部署过程中你可能会遇到以下典型问题问题现象可能原因排查思路与解决方案前端请求后端接口出现CORS错误浏览器同源策略限制前端(localhost:3000)访问后端(localhost:8080)端口不同。在后端配置全局CORS过滤器或使用CrossOrigin注解。Spring Boot配置示例ConfigurationWebMvcConfigurer的addCorsMappings方法。插入数据时create_time字段不自动更新1. 数据库表字段默认值未设置。2. MyBatis-Plus的TableField(fill FieldFill.INSERT)注解未配置或MetaObjectHandler未实现。1. 检查DDL语句确保字段定义为DEFAULT CURRENT_TIMESTAMP。2. 在实体类字段上添加注解并实现MetaObjectHandler的insertFill方法。查询速度慢特别是分页查询大数据量表1. 缺少索引。2. 查询了不必要的字段。3. 深度分页limit 100000, 10性能差。1. 使用EXPLAIN分析SQL添加合适索引。2. 在Mapper.xml中只SELECT需要的字段。3. 对于深度分页改用“上一页最大ID”查询法where id #{lastMaxId} limit #{pageSize}。JWT Token过期后前端无感知操作失败前端未拦截401状态码或未实现Token自动刷新逻辑。1. 在Axios响应拦截器中判断error.response.status 401。2. 跳转登录页或尝试使用Refresh Token调用刷新接口获取新Token。库存数量出现负数1. 并发出库未加锁导致超卖。2. 业务逻辑有BUG未校验出库数量是否大于当前库存。1.悲观锁在查询库存时使用SELECT ... FOR UPDATE。2.乐观锁在库存批次表增加version字段更新时带版本条件。3. 在出库业务逻辑开始处强制校验出库数量 当前库存。药品效期预警查询不准确1. 日期比较逻辑有误如使用了而不是。2. 服务器时区与数据库时区不一致。1. 仔细核对SQL或Java代码中的日期比较条件。2. 确保应用和数据库使用统一的时区如Asia/Shanghai在JDBC连接串中指定serverTimezone。8. 项目部署与上线建议环境分离至少准备开发(dev)、测试(test)、生产(prod)三套环境通过Spring Boot的application-{profile}.yml文件管理不同环境的配置数据库地址、Redis地址等。数据库脚本版本化使用Flyway或Liquibase工具来管理数据库表结构的变更确保不同环境数据库结构一致且变更可追溯。前后端独立部署后端使用mvn clean package打成可执行的JAR包在服务器上通过java -jar命令运行。推荐使用Docker容器化部署环境更一致。前端运行npm run build生成静态资源dist目录将其部署到Nginx或Apache等Web服务器上。并配置Nginx将API请求反向代理到后端服务。日志与监控使用Logback或Log4j2配置日志按天滚动区分INFO,ERROR级别。关键业务操作如入库、出库、登录必须记录操作日志。接入简单的监控如Spring Boot Actuator监控应用健康状态、JVM内存等。备份策略数据库备份必须定期如每日对生产数据库进行全量备份和增量备份。代码备份使用Git进行版本控制并推送到远程仓库如Gitee, GitHub私有库。文档备份数据库设计文档、部署文档、接口文档等应随项目代码一起管理。通过以上步骤你不仅能够完成一个功能完整的医院药房药品库存管理系统更能理解其背后的设计思想、技术细节和优化方向。这个项目足以作为一份优秀的毕业设计或课程设计作品也为将来从事企业级应用开发打下了坚实的基础。建议你在理解本文提供的代码和思路后亲自动手从头实现一遍并在过程中尝试解决自己遇到的新问题这才是提升技术能力的最佳途径。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价