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. 集成智能预警算法

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

相关推荐
、BeYourself2 小时前
SpringAI-ChatClient Fluent API 详解
java·后端·springai
星辰_mya2 小时前
reids哨兵集群与选主
java·开发语言
BD_Marathon2 小时前
SpringBoot快速入门
java·spring boot·后端
期待のcode2 小时前
Java的多态
java·开发语言
本当迷ya3 小时前
SpringBoot 接入飞书多维表格,快速跑通MVP应用
后端
挖土机_0083 小时前
AI 是否真的能完全替代程序员?从我试用 AI 开发到前后端架构与页面开发的真实分析
后端·ai编程
证能量少女3 小时前
2026大专Java开发工程师,考什么证加分?
java·开发语言
FPGAI3 小时前
Java学习之基础概念
java·学习
芒克芒克3 小时前
Java集合框架总结(面试八股)
java·开发语言·面试