SpringBoot+Vue3药店管理系统开发实践

📅 发布时间:2026/9/14 21:38:07
SpringBoot+Vue3药店管理系统开发实践
1. 项目概述与架构设计药店管理系统作为医药行业数字化转型的核心载体其技术选型直接影响系统的稳定性、扩展性和开发效率。这套基于SpringBootVue3MyBatis的前后端分离方案完美契合医药行业对数据准确性、操作实时性和合规性的严苛要求。后端采用SpringBoot 2.7.x框架搭建RESTful API服务通过分层架构实现业务逻辑解耦。控制层处理HTTP请求服务层实现药品进销存等核心业务逻辑数据访问层通过MyBatis 3.5.x操作MySQL 8.0数据库。特别设计了药品批次管理、效期预警等医药行业特有功能模块。前端使用Vue3.2TypeScript构建响应式管理界面搭配Element Plus组件库实现符合医药GSP规范的UI交互。通过Axios与后端通信采用JWT进行接口鉴权。系统特别强化了药品分类管理、处方审核等业务场景的前端交互设计。2. 核心功能模块实现2.1 药品管理模块开发药品基础信息表采用符合GSP规范的字段设计CREATE TABLE medicine ( id bigint NOT NULL AUTO_INCREMENT COMMENT 药品ID, code varchar(20) NOT NULL COMMENT 药品编码, name varchar(100) NOT NULL COMMENT 通用名称, spec varchar(50) NOT NULL COMMENT 规格, unit varchar(10) NOT NULL COMMENT 单位, manufacturer varchar(200) NOT NULL COMMENT 生产厂家, approval_number varchar(50) NOT NULL COMMENT 批准文号, category_id int NOT NULL COMMENT 分类ID, price decimal(10,2) NOT NULL COMMENT 零售价, cost_price decimal(10,2) NOT NULL COMMENT 成本价, stock int NOT NULL DEFAULT 0 COMMENT 库存数量, status tinyint NOT NULL DEFAULT 1 COMMENT 状态(1-正常 0-停售), PRIMARY KEY (id), UNIQUE KEY idx_code (code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT药品信息表;MyBatis动态SQL实现多条件药品查询select idselectByCondition resultTypeMedicine SELECT * FROM medicine where if testname ! null and name ! AND name LIKE CONCAT(%,#{name},%) /if if testcategoryId ! null AND category_id #{categoryId} /if if teststatus ! null AND status #{status} /if /where ORDER BY id DESC /select2.2 库存管理模块实现库存操作采用事务控制保证数据一致性Transactional public void stockIn(StockInDTO dto) { // 1. 更新库存记录 medicineMapper.updateStock(dto.getMedicineId(), dto.getAmount()); // 2. 创建入库记录 StockInRecord record new StockInRecord(); BeanUtils.copyProperties(dto, record); record.setOperator(SecurityUtils.getCurrentUserId()); stockInMapper.insert(record); // 3. 记录操作日志 operationLogService.logStockIn(dto); }库存预警功能通过定时任务实现Scheduled(cron 0 0 9 * * ?) // 每天9点执行 public void checkStockWarning() { ListMedicine lowStockMedicines medicineMapper.selectLowStock(10); // 库存低于10 lowStockMedicines.forEach(medicine - { String message String.format(药品[%s]库存不足当前库存%d, medicine.getName(), medicine.getStock()); notificationService.sendStockWarning(medicine.getId(), message); }); }3. 前后端交互设计3.1 API接口规范采用RESTful风格设计API示例药品接口GET /api/medicines 分页查询药品列表 POST /api/medicines 新增药品 GET /api/medicines/{id} 获取药品详情 PUT /api/medicines/{id} 修改药品信息 DELETE /api/medicines/{id} 删除药品统一响应格式{ code: 200, message: success, data: {...}, timestamp: 1630000000000 }3.2 Vue3前端实现药品列表页面核心代码script setup const queryParams reactive({ name: , categoryId: null, page: 1, size: 10 }) const { data, pending, refresh } useFetch(/api/medicines, { params: queryParams, watch: [queryParams] }) const handleSearch () { queryParams.page 1 refresh() } /script template el-form :modelqueryParams inline el-form-item label药品名称 el-input v-modelqueryParams.name / /el-form-item el-form-item el-button typeprimary clickhandleSearch查询/el-button /el-form-item /el-form el-table :datadata.list v-loadingpending el-table-column propcode label药品编码 / el-table-column propname label通用名称 / el-table-column propspec label规格 / el-table-column propstock label库存 / /el-table /template4. 系统安全与性能优化4.1 安全防护措施接口鉴权采用JWTSpring Security实现Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers(/api/auth/**).permitAll() .anyRequest().authenticated() .and() .addFilter(new JwtAuthenticationFilter(authenticationManager())) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } }数据脱敏敏感字段如身份证号在响应时进行脱敏处理public class DesensitizationUtil { public static String idCard(String idCard) { if (StringUtils.isBlank(idCard)) return ; return idCard.replaceAll((\\d{4})\\d{10}(\\w{4}), $1****$2); } }4.2 性能优化方案二级缓存MyBatisRedis实现CacheConfig(cacheNames medicine) Service public class MedicineServiceImpl implements MedicineService { Cacheable(key #id) public Medicine getById(Long id) { return medicineMapper.selectById(id); } CacheEvict(key #id) public void update(Medicine medicine) { medicineMapper.updateById(medicine); } }接口限流使用Guava RateLimiterAspect Component public class RateLimitAspect { private final RateLimiter limiter RateLimiter.create(100); // 100请求/秒 Around(annotation(rateLimit)) public Object around(ProceedingJoinPoint joinPoint) throws Throwable { if (limiter.tryAcquire()) { return joinPoint.proceed(); } throw new BusinessException(429, 请求过于频繁); } }5. 部署与运维方案5.1 容器化部署Docker Compose编排文件示例version: 3 services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: 123456 MYSQL_DATABASE: pharmacy ports: - 3306:3306 volumes: - ./mysql/data:/var/lib/mysql redis: image: redis:6 ports: - 6379:6379 backend: build: ./pharmacy-backend ports: - 8080:8080 depends_on: - mysql - redis frontend: build: ./pharmacy-frontend ports: - 80:805.2 监控方案SpringBoot Actuator健康检查# application.properties management.endpoints.web.exposure.include* management.endpoint.health.show-detailsalwaysPrometheus监控配置# prometheus.yml scrape_configs: - job_name: pharmacy-backend metrics_path: /actuator/prometheus static_configs: - targets: [backend:8080]6. 开发注意事项药品编码规范严格遵循国家药品编码规则建议采用以下格式处方药RX分类码5位序列号非处方药OTC分类码5位序列号医疗器械MD分类码5位序列号事务处理要点// 错误示范 - 大事务问题 Transactional public void complexOperation() { step1(); // 包含IO操作 step2(); // 包含远程调用 step3(); // 包含数据库操作 } // 正确做法 - 拆分事务 public void optimizedOperation() { step1(); // 非事务操作 transactionalStep2(); // 独立小事务 transactionalStep3(); // 独立小事务 }效期管理特殊处理// 效期预警计算方法 public ListMedicine getExpiringMedicines(int days) { LocalDate warningDate LocalDate.now().plusDays(days); return medicineMapper.selectExpiringBefore(warningDate); }前端性能优化技巧// 表格大数据量优化 const tableData ref([]) const loadData async () { const res await api.getMedicines() tableData.value res.data // 使用虚拟滚动优化渲染 useVirtualList(tableData, { itemHeight: 56 }) }MyBatis批量操作优化insert idbatchInsert useGeneratedKeystrue keyPropertyid INSERT INTO medicine (code, name, spec) VALUES foreach collectionlist itemitem separator, (#{item.code}, #{item.name}, #{item.spec}) /foreach /insert