SpringBoot药品管理系统设计实现

基于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,实现了药品全生命周期管理。

系统特色:

  1. 模块化设计:清晰的模块划分,便于维护和扩展
  2. 安全性高:完善的权限控制和数据加密
  3. 性能优良:多级缓存策略,数据库优化
  4. 可扩展性强:微服务架构预留,支持分布式部署
  5. 用户体验好:响应式设计,操作便捷

后续优化方向:

  1. 引入消息队列处理异步任务
  2. 实现分布式事务管理
  3. 增加大数据分析模块
  4. 开发移动端应用
  5. 集成智能预警算法

该系统能够有效提升药品管理效率,降低管理成本,保障药品安全,具有很高的实用价值和推广意义。

相关推荐
葫芦和十三3 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp3 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑4 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯4 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan7 小时前
多Agent之间的区别
后端
青石路8 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充9 小时前
1.面向对象设计思想
后端
IT_陈寒9 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro10 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗10 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端