基于SpringBoot的药品管理系统的设计与实现
引言
在当今医疗行业快速发展的背景下,药品管理成为医疗机构运营中的重要环节。传统的手工记录和管理方式效率低下、容易出错,且难以满足现代化医疗管理的需求。因此,开发一套高效、准确、安全的药品管理系统显得尤为重要。本文将详细介绍如何基于SpringBoot框架设计和实现一个完整的药品管理系统。
一、系统需求分析
1.1 功能需求
- 药品信息管理:药品的增删改查、分类管理、库存预警
- 采购管理:采购订单管理、供应商管理、入库记录
- 销售管理:处方管理、销售记录、退货处理
- 库存管理:实时库存监控、库存盘点、效期管理
- 报表统计:销售统计、库存分析、财务统计
- 系统管理:用户管理、角色权限、操作日志
1.2 非功能需求
- 响应时间:系统操作响应时间不超过2秒
- 并发支持:支持至少100用户同时在线操作
- 数据安全:敏感数据加密存储,操作留痕
- 易用性:界面友好,操作简便
二、技术选型
2.1 后端技术栈
yaml
框架: SpringBoot 2.7+
安全框架: Spring Security
持久层: MyBatis-Plus 3.5+
数据库: MySQL 8.0+
缓存: Redis
API文档: Swagger3/Knife4j
验证框架: Hibernate Validator
2.2 前端技术栈
yaml
框架: Vue.js 3.x
UI库: Element Plus
构建工具: Vite
状态管理: Pinia
HTTP客户端: Axios
2.3 开发工具
yaml
IDE: IntelliJ IDEA + VS Code
版本控制: Git
项目管理: Maven
API测试: Postman
三、系统设计
3.1 系统架构设计
┌─────────────────────────────────────┐
│ 表示层 (前端) │
│ Vue.js + Element Plus │
└───────────────┬─────────────────────┘
│ HTTP/HTTPS
┌───────────────▼─────────────────────┐
│ 控制层 (Controller) │
│ Spring MVC + RESTful API │
└───────────────┬─────────────────────┘
│ 服务调用
┌───────────────▼─────────────────────┐
│ 业务层 (Service) │
│ 业务逻辑处理 + 事务管理 │
└───────────────┬─────────────────────┘
│ 数据访问
┌───────────────▼─────────────────────┐
│ 持久层 (Mapper) │
│ MyBatis-Plus + MySQL │
└───────────────┬─────────────────────┘
│ 数据存储
┌───────────────▼─────────────────────┐
│ 数据库 │
│ MySQL + Redis │
└─────────────────────────────────────┘
3.2 数据库设计
核心表结构示例:
药品信息表 (drug)
sql
CREATE TABLE drug (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
drug_code VARCHAR(50) UNIQUE NOT NULL COMMENT '药品编码',
drug_name VARCHAR(100) NOT NULL COMMENT '药品名称',
generic_name VARCHAR(100) COMMENT '通用名称',
specification VARCHAR(100) COMMENT '规格',
manufacturer VARCHAR(200) COMMENT '生产厂家',
drug_type VARCHAR(20) COMMENT '药品类型',
unit VARCHAR(20) COMMENT '单位',
purchase_price DECIMAL(10,2) COMMENT '采购价',
sale_price DECIMAL(10,2) COMMENT '销售价',
stock_quantity INT DEFAULT 0 COMMENT '库存数量',
min_stock INT COMMENT '最小库存',
max_stock INT COMMENT '最大库存',
expiry_date DATE COMMENT '有效期至',
status TINYINT DEFAULT 1 COMMENT '状态(1:正常 0:停用)',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
库存记录表 (inventory_record)
sql
CREATE TABLE inventory_record (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
drug_id BIGINT NOT NULL COMMENT '药品ID',
batch_no VARCHAR(50) COMMENT '批次号',
quantity INT NOT NULL COMMENT '数量',
unit_price DECIMAL(10,2) COMMENT '单价',
total_amount DECIMAL(10,2) COMMENT '总金额',
record_type VARCHAR(20) COMMENT '记录类型(入库/出库/盘点)',
operation_type VARCHAR(20) COMMENT '操作类型(采购入库/销售出库等)',
operator_id BIGINT COMMENT '操作人',
operation_time DATETIME DEFAULT CURRENT_TIMESTAMP,
remark VARCHAR(500) COMMENT '备注'
);
3.3 核心功能模块设计
3.3.1 药品管理模块
java
@Service
public class DrugService {
@Autowired
private DrugMapper drugMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 添加药品信息
*/
@Transactional
public Result addDrug(DrugDTO drugDTO) {
// 校验药品编码唯一性
if (drugMapper.selectByDrugCode(drugDTO.getDrugCode()) != null) {
return Result.error("药品编码已存在");
}
Drug drug = BeanUtil.copyProperties(drugDTO, Drug.class);
drug.setCreateTime(LocalDateTime.now());
drug.setUpdateTime(LocalDateTime.now());
int result = drugMapper.insert(drug);
// 清除缓存
redisTemplate.delete("drug:list");
return result > 0 ? Result.success() : Result.error("添加失败");
}
/**
* 库存预警检查
*/
public List<Drug> checkStockWarning() {
LambdaQueryWrapper<Drug> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.le(Drug::getStockQuantity, Drug::getMinStock);
queryWrapper.eq(Drug::getStatus, 1);
return drugMapper.selectList(queryWrapper);
}
}
3.3.2 库存管理模块
java
@Component
public class InventoryManager {
/**
* 库存变更操作
*/
@Transactional
public Result updateStock(Long drugId, Integer quantity,
String operationType, String batchNo) {
// 获取药品信息
Drug drug = drugMapper.selectById(drugId);
if (drug == null) {
return Result.error("药品不存在");
}
// 计算新库存
int newStock = drug.getStockQuantity() + quantity;
if (newStock < 0) {
return Result.error("库存不足");
}
// 更新库存
drug.setStockQuantity(newStock);
drug.setUpdateTime(LocalDateTime.now());
drugMapper.updateById(drug);
// 记录库存变更日志
InventoryRecord record = new InventoryRecord();
record.setDrugId(drugId);
record.setQuantity(Math.abs(quantity));
record.setOperationType(operationType);
record.setBatchNo(batchNo);
record.setOperationTime(LocalDateTime.now());
inventoryRecordMapper.insert(record);
// 发送库存预警通知
checkAndSendWarning(drug);
return Result.success();
}
}
四、关键功能实现
4.1 药品效期管理
java
@Service
public class ExpiryManager {
@Scheduled(cron = "0 0 8 * * ?") // 每天8点执行
public void checkDrugExpiry() {
LocalDate warningDate = LocalDate.now().plusDays(30);
LambdaQueryWrapper<Drug> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.le(Drug::getExpiryDate, warningDate);
queryWrapper.gt(Drug::getStockQuantity, 0);
List<Drug> expiringDrugs = drugMapper.selectList(queryWrapper);
// 发送预警通知
expiringDrugs.forEach(this::sendExpiryWarning);
}
private void sendExpiryWarning(Drug drug) {
// 发送邮件、短信或系统通知
String message = String.format("药品【%s】批次【%s】将于%s过期",
drug.getDrugName(), drug.getBatchNo(), drug.getExpiryDate());
notificationService.sendWarning(message);
}
}
4.2 采购管理流程
java
@Service
public class PurchaseService {
/**
* 采购流程
*/
@Transactional
public Result processPurchase(PurchaseOrderDTO orderDTO) {
// 1. 创建采购单
PurchaseOrder order = createPurchaseOrder(orderDTO);
// 2. 审核采购单
if (!approvePurchaseOrder(order)) {
return Result.error("采购单审核未通过");
}
// 3. 执行采购入库
return executePurchase(order);
}
private Result executePurchase(PurchaseOrder order) {
order.getItems().forEach(item -> {
// 更新库存
inventoryManager.updateStock(
item.getDrugId(),
item.getQuantity(),
"PURCHASE_IN",
item.getBatchNo()
);
// 更新药品信息
updateDrugInfo(item);
});
// 更新采购单状态
order.setStatus("COMPLETED");
order.setUpdateTime(LocalDateTime.now());
purchaseOrderMapper.updateById(order);
return Result.success("采购入库完成");
}
}
4.3 权限控制实现
java
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/login", "/api/register").permitAll()
.antMatchers("/api/drug/**").hasAnyRole("ADMIN", "PHARMACIST")
.antMatchers("/api/purchase/**").hasRole("PURCHASER")
.antMatchers("/api/sale/**").hasRole("SALES")
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class)
.csrf().disable();
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
}
五、系统优化方案
5.1 缓存策略
java
@Service
@CacheConfig(cacheNames = "drug")
public class DrugService {
@Cacheable(key = "'list:' + #page + ':' + #size")
public Page<DrugVO> getDrugList(int page, int size) {
Page<Drug> drugPage = Page.of(page, size);
LambdaQueryWrapper<Drug> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByDesc(Drug::getUpdateTime);
return drugMapper.selectPage(drugPage, queryWrapper)
.convert(drug -> convertToVO(drug));
}
@CachePut(key = "'detail:' + #drug.id")
public DrugVO updateDrug(DrugDTO drugDTO) {
// 更新逻辑
return convertToVO(updatedDrug);
}
@CacheEvict(key = "'detail:' + #id")
public void deleteDrug(Long id) {
// 删除逻辑
}
}
5.2 数据库优化
sql
-- 创建索引
CREATE INDEX idx_drug_code ON drug(drug_code);
CREATE INDEX idx_drug_name ON drug(drug_name);
CREATE INDEX idx_expiry_date ON drug(expiry_date);
CREATE INDEX idx_stock_status ON drug(stock_quantity, status);
-- 分区表(按月份分区)
ALTER TABLE inventory_record
PARTITION BY RANGE (YEAR(operation_time) * 100 + MONTH(operation_time)) (
PARTITION p202401 VALUES LESS THAN (202402),
PARTITION p202402 VALUES LESS THAN (202403)
);
5.3 性能监控
java
@Aspect
@Component
@Slf4j
public class PerformanceMonitor {
@Around("@annotation(org.springframework.web.bind.annotation.GetMapping) || " +
"@annotation(org.springframework.web.bind.annotation.PostMapping)")
public Object monitor(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
try {
return joinPoint.proceed();
} finally {
long duration = System.currentTimeMillis() - startTime;
String methodName = joinPoint.getSignature().getName();
log.info("方法 {} 执行耗时: {} ms", methodName, duration);
// 记录到监控系统
if (duration > 1000) { // 超过1秒记录警告
log.warn("方法 {} 执行较慢: {} ms", methodName, duration);
}
}
}
}
六、部署方案
6.1 Docker部署配置
dockerfile
# Dockerfile
FROM openjdk:11-jre-slim
VOLUME /tmp
COPY target/pharmacy-system.jar app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
EXPOSE 8080
yaml
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_HOST=mysql
depends_on:
- mysql
- redis
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: pharmacy_db
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:alpine
volumes:
- redis_data:/data
6.2 Nginx配置
nginx
upstream pharmacy_backend {
server app1:8080;
server app2:8080;
server app3:8080;
}
server {
listen 80;
server_name pharmacy.example.com;
location /api {
proxy_pass http://pharmacy_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
}
七、总结
本文详细介绍了基于SpringBoot的药品管理系统的设计与实现过程。系统采用前后端分离架构,后端使用SpringBoot+MyBatis-Plus技术栈,前端采用Vue3+Element Plus,实现了药品全生命周期管理。
系统特色:
- 模块化设计:清晰的模块划分,便于维护和扩展
- 安全性高:完善的权限控制和数据加密
- 性能优良:多级缓存策略,数据库优化
- 可扩展性强:微服务架构预留,支持分布式部署
- 用户体验好:响应式设计,操作便捷
后续优化方向:
- 引入消息队列处理异步任务
- 实现分布式事务管理
- 增加大数据分析模块
- 开发移动端应用
- 集成智能预警算法
该系统能够有效提升药品管理效率,降低管理成本,保障药品安全,具有很高的实用价值和推广意义。