1. 项目概述:新冠物资管理系统的技术实现
新冠物资管理系统是一个面向医疗机构、防疫部门和物资调配中心的信息化管理平台。我在2020年疫情初期参与开发过类似系统,深知这类平台需要快速响应、高并发处理和实时数据更新的特点。本系统采用SpringBoot+Vue+MySQL的技术栈,实现了从物资入库、库存管理到调拨分发的全流程数字化管控。
这套源码最大的优势在于开箱即用——已经整合了前后端通信、数据库设计和基础业务逻辑,开发者只需按文档配置环境就能启动运行。系统包含六大核心模块:用户权限管理、物资档案管理、入库出库记录、库存预警监控、调拨申请审批和统计报表生成。每个模块都采用前后端分离架构,接口文档完整,便于二次开发。
提示:系统默认采用RBAC权限模型,内置了管理员、仓库主管和普通操作员三种角色,实际部署时可根据机构层级灵活调整权限分配方案。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构解析
2.1 SpringBoot后端设计
后端采用SpringBoot 2.7.x构建,这是我经过多个生产环境验证的稳定版本。项目结构遵循标准的Maven多模块设计:
code复制covid-supplies
├── supply-core // 实体类与工具包
├── supply-system // 系统模块(权限/日志)
├── supply-admin // 后台管理API
└── supply-quartz // 定时任务模块
关键配置要点:
- 数据层使用MyBatis-Plus 3.5.x,省去了基础CRUD代码编写
- 接口文档通过Swagger UI自动生成,访问路径
/doc.html - 采用Hutool工具包处理Excel导入导出,实测支持万级数据秒级导出
- 物资唯一编码规则:
机构代码(4位)+物资类型(2位)+日期(6位)+序列号(4位)
数据库连接池配置示例(application.yml):
yaml复制spring:
datasource:
druid:
initial-size: 5
max-active: 20
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
2.2 Vue前端工程化实践
前端基于Vue 2.6 + ElementUI 2.15构建,采用典型的SPA架构。我在项目中特别优化了几处关键点:
- 路由懒加载:按功能模块拆分chunk,首屏加载时间从4s降至1.2s
javascript复制const Inventory = () => import('./views/Inventory.vue')
- Axios拦截器:统一处理401跳转和业务异常提示
javascript复制service.interceptors.response.use(
response => {
if (response.data.code === 500) {
Message.error(response.data.msg)
return Promise.reject()
}
return response.data
}
)
- 表格性能优化:万级数据采用虚拟滚动方案
html复制<el-table
:data="tableData"
height="calc(100vh - 200px)"
v-loading="loading"
row-key="id">
</el-table>
2.3 MySQL数据库设计
物资管理系统的数据库设计有几个特殊考量点:
- 库存表需要记录批次号和有效期
- 调拨流程需要完整的操作日志
- 需要支持多条件联合统计查询
核心表结构示例:
sql复制CREATE TABLE `supply_stock` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`material_id` varchar(20) NOT NULL COMMENT '物资编码',
`batch_no` varchar(30) NOT NULL COMMENT '生产批次',
`quantity` int(11) NOT NULL DEFAULT '0' COMMENT '当前数量',
`expire_date` date DEFAULT NULL COMMENT '失效日期',
`location_code` varchar(10) NOT NULL COMMENT '库位编码',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_material_batch` (`material_id`,`batch_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物资库存表';
注意:所有时间字段统一使用datetime类型,避免时区转换问题。库存变更记录需要额外建立流水表实现溯源。
3. 系统部署实操指南
3.1 环境准备与初始化
推荐使用以下环境组合(经压力测试验证):
- JDK 1.8(注意不要用11+版本,避免与MyBatis兼容问题)
- Node.js 14.x(16.x会导致部分依赖编译失败)
- MySQL 5.7(8.0需要调整默认编码配置)
数据库初始化步骤:
- 创建数据库并授权
sql复制CREATE DATABASE `supply_db` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
GRANT ALL PRIVILEGES ON supply_db.* TO 'supply_user'@'%' IDENTIFIED BY 'Supply@1234';
- 执行初始化脚本
bash复制mysql -u supply_user -p supply_db < /path/to/init.sql
- 修改后端配置
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/supply_db?useSSL=false&serverTimezone=Asia/Shanghai
username: supply_user
password: Supply@1234
3.2 前后端启动流程
后端启动(开发模式):
bash复制mvn spring-boot:run -Dspring-boot.run.profiles=dev
前端启动要点:
- 安装依赖时建议使用淘宝镜像
bash复制npm install --registry=https://registry.npm.taobao.org
- 开发环境启动命令
bash复制npm run serve -- --port 8081
- 生产打包注意事项
bash复制VUE_APP_API_BASE=/api npm run build
3.3 常见部署问题解决
- 前端跨域问题:开发环境下需配置vue.config.js
javascript复制devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
- MySQL时区异常:连接串需显式指定时区
code复制jdbc:mysql://localhost:3306/supply_db?serverTimezone=Asia/Shanghai
- 静态资源404:Nginx需要添加以下配置
nginx复制location / {
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
}
4. 核心业务逻辑实现
4.1 物资入库流程
入库业务涉及三个关键操作:
- 扫描物资二维码获取基础信息
- 校验批次号和有效期
- 更新库存并生成入库单
核心Java代码片段:
java复制@Transactional
public void acceptMaterial(StockInDTO dto) {
// 校验物资有效性
Material material = materialMapper.selectByCode(dto.getMaterialCode());
if (material == null) {
throw new BusinessException("物资编码不存在");
}
// 批次号生成规则:供应商缩写+年月日+4位随机数
String batchNo = generateBatchNumber(dto.getSupplier());
// 库存记录
Stock stock = new Stock();
stock.setMaterialId(material.getId());
stock.setBatchNo(batchNo);
stock.setQuantity(dto.getQuantity());
stock.setExpireDate(dto.getExpireDate());
stockMapper.insert(stock);
// 记录操作日志
logService.saveLog("入库操作", "新增库存:"+dto.getQuantity());
}
4.2 库存预警机制
系统通过定时任务+阈值配置实现智能预警:
- 每天凌晨2点执行库存检查
- 预警规则可配置(数量下限/近效期天数)
- 支持邮件/短信多通道通知
Quartz配置示例:
java复制@Scheduled(cron = "0 0 2 * * ?")
public void checkStockWarning() {
List<Material> materials = materialMapper.selectAll();
materials.forEach(material -> {
int stock = stockMapper.selectSumByMaterial(material.getId());
if (stock < material.getSafetyStock()) {
String msg = String.format("[%s]库存不足! 当前:%d, 安全库存:%d",
material.getName(), stock, material.getSafetyStock());
alertService.sendWarning(msg);
}
});
}
4.3 调拨审批流程
采用状态机模式管理调拨生命周期:
code复制[申请中] → [主管审批] → [出库确认] → [入库确认] → [已完成]
↘ [已拒绝]
前端状态流转实现:
javascript复制// 审批操作处理
handleApprove() {
this.$confirm('确定通过该调拨申请?').then(() => {
updateTransferStatus(this.form.id, 'APPROVED').then(res => {
this.$message.success('审批通过')
this.loadData()
})
})
}
5. 性能优化实战经验
5.1 数据库查询优化
物资查询常见痛点:
- 多条件组合筛选响应慢
- 分页查询count性能差
- 大批量导出内存溢出
优化方案:
- 建立联合索引
sql复制ALTER TABLE supply_stock ADD INDEX idx_query (material_id, location_code, expire_date);
- 分页查询改造
java复制// 原写法(性能差)
Page<Stock> page = new Page<>(current, size);
stockMapper.selectPage(page, queryWrapper);
// 优化写法(避免count查询)
Page<Stock> page = new Page<>(current, size, false);
- 流式导出实现
java复制@GetMapping("/export")
public void exportStock(HttpServletResponse response) {
response.setContentType("application/vnd.ms-excel");
try (OutputStream out = response.getOutputStream()) {
ExcelWriter writer = ExcelUtil.getWriter();
writer.write(stockService.streamQuery(), true);
writer.flush(out, true);
}
}
5.2 前端渲染优化
针对ElementUI表格的专项优化:
- 大数据量采用虚拟滚动
html复制<el-table
:data="tableData"
height="calc(100vh - 200px)"
row-key="id"
v-el-table-infinite-scroll="loadMore">
</el-table>
- 复杂表格列使用自定义组件
javascript复制{
label: '操作',
width: 180,
fixed: 'right',
render: (h, { row }) => {
return h(OperationCell, { props: { row } })
}
}
- 防抖处理搜索输入
javascript复制watch: {
searchForm: {
handler: _.debounce(function() {
this.loadData()
}, 500),
deep: true
}
}
5.3 缓存策略设计
采用多级缓存架构:
- 本地缓存(Caffeine):高频访问的基础数据
java复制@Cacheable(value = "materialCache", key = "#code")
public Material getByCode(String code) {
return materialMapper.selectByCode(code);
}
- Redis缓存:分布式锁和会话管理
java复制// 库存操作加锁
String lockKey = "stock:" + materialId;
try {
boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS);
if (!locked) throw new BusinessException("操作过于频繁");
// 业务逻辑...
} finally {
redisTemplate.delete(lockKey);
}
6. 安全防护方案
6.1 接口安全设计
- JWT令牌认证流程:
java复制@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/auth/login").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
- 敏感数据脱敏处理:
java复制@JsonSerialize(using = SensitiveSerializer.class)
private String phoneNumber;
public class SensitiveSerializer extends JsonSerializer<String> {
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) {
gen.writeString(value.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"));
}
}
6.2 操作日志审计
采用AOP记录关键操作:
java复制@Aspect
@Component
public class OperationLogAspect {
@AfterReturning(pointcut = "@annotation(log)", returning = "result")
public void afterReturning(JoinPoint joinPoint, OperationLog log, Object result) {
String username = SecurityUtils.getCurrentUsername();
String operation = log.value();
logService.saveLog(username, operation);
}
}
日志表设计要点:
sql复制CREATE TABLE `sys_operation_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT '操作人',
`module` varchar(50) NOT NULL COMMENT '模块名',
`operation` varchar(100) NOT NULL COMMENT '操作内容',
`params` text COMMENT '请求参数',
`ip` varchar(50) DEFAULT NULL COMMENT 'IP地址',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user` (`user_id`),
KEY `idx_module` (`module`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='操作日志表';
7. 二次开发建议
7.1 扩展功能方向
根据实际部署经验,建议优先考虑以下扩展:
- 移动端适配:增加PWA支持或开发微信小程序
- 智能预测:基于历史数据预测物资需求
- 物联网集成:对接智能货架和温湿度传感器
- 多机构协同:支持跨医院物资调配
7.2 定制化开发要点
- 修改系统名称和logo:
javascript复制// src/settings.js
module.exports = {
title: '新冠物资管理系统',
logo: '/logo.png'
}
- 调整权限模型:
java复制// 自定义权限注解
@PreAuthorize("@ss.hasPermi('supply:transfer:approve')")
public void approveTransfer(Long id) {
// 审批逻辑
}
- 扩展物资字段:
sql复制ALTER TABLE `supply_material`
ADD COLUMN `specification` varchar(100) COMMENT '规格参数',
ADD COLUMN `storage_condition` varchar(50) COMMENT '存储条件';
8. 生产环境部署方案
8.1 Docker容器化部署
后端Dockerfile示例:
dockerfile复制FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
前端Nginx配置:
nginx复制server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
}
}
8.2 高可用架构建议
对于大型医疗机构,推荐部署方案:
- 后端服务:2台ECS + SLB负载均衡
- 数据库:RDS MySQL主从架构
- 缓存:Redis集群版
- 文件存储:OSS对象存储
关键监控指标:
- 接口响应时间(P99 < 500ms)
- 数据库连接池使用率(<80%)
- JVM内存占用(<70%)
8.3 灾备恢复策略
- 数据库每日全量备份+binlog增量
bash复制mysqldump -u root -p supply_db > backup_$(date +%F).sql
- 应用级容灾方案:
- 前端静态资源部署到CDN
- 后端服务实现健康检查接口
- 配置自动故障转移机制
- 数据恢复演练流程:
mermaid复制graph TD
A[停止应用] --> B[恢复最近备份]
B --> C[应用binlog]
C --> D[验证数据完整性]
D --> E[启动应用]
9. 项目演进路线
9.1 技术栈升级路径
- SpringBoot 3.x迁移:
- 需要JDK17+环境
- 注意Jakarta EE包名变更
- 测试与MyBatis的兼容性
- Vue3升级方案:
- 使用官方迁移工具
- 逐步替换Options API为Composition API
- 验证Element Plus兼容性
- MySQL到分布式演进:
- 第一阶段:主从读写分离
- 第二阶段:分库分表(按机构分片)
- 第三阶段:引入TiDB等NewSQL方案
9.2 微服务改造建议
当系统需要支持多机构协同管理时,可考虑拆分为:
- 用户中心服务
- 物资基础服务
- 库存交易服务
- 审批流程服务
使用SpringCloud Alibaba技术栈:
yaml复制dependencies:
- spring-cloud-starter-alibaba-nacos-discovery
- spring-cloud-starter-alibaba-sentinel
- spring-cloud-starter-openfeign
10. 开发经验与避坑指南
10.1 典型问题解决方案
- 批量导入内存溢出:
- 使用SAX模式解析Excel
- 分批次提交数据库
- 增加进度提示
优化后的导入代码:
java复制@Transactional(rollbackFor = Exception.class)
public void batchImport(MultipartFile file) {
ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
List<Material> list = new ArrayList<>(BATCH_SIZE);
reader.addHeaderAlias("物资名称", "name");
reader.readAll().forEach(row -> {
list.add(BeanUtil.toBean(row, Material.class));
if (list.size() >= BATCH_SIZE) {
materialMapper.batchInsert(list);
list.clear();
}
});
if (!list.isEmpty()) {
materialMapper.batchInsert(list);
}
}
- 并发库存扣减问题:
- 乐观锁方案:
sql复制UPDATE supply_stock
SET quantity = quantity - #{num}
WHERE id = #{id} AND quantity >= #{num}
- 分布式锁方案:
java复制public boolean reduceStock(Long id, int num) {
String lockKey = "stock:" + id;
try {
// 尝试获取锁
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if (Boolean.TRUE.equals(locked)) {
// 查询当前库存
Stock stock = stockMapper.selectById(id);
if (stock.getQuantity() >= num) {
stock.setQuantity(stock.getQuantity() - num);
return stockMapper.updateById(stock) > 0;
}
}
return false;
} finally {
redisTemplate.delete(lockKey);
}
}
10.2 性能调优记录
- JVM参数优化(8G内存机器示例):
code复制-server -Xms6g -Xmx6g -XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m -XX:+UseG1GC
-XX:MaxGCPauseMillis=200
- MyBatis调优要点:
- 开启二级缓存
- 合理设置fetchSize
- 避免N+1查询问题
- 前端打包优化:
javascript复制// vue.config.js
configureWebpack: {
optimization: {
splitChunks: {
chunks: 'all',
maxSize: 244 * 1024 // 控制chunk大小
}
}
}
10.3 代码质量保障
- 静态检查方案:
- 后端:SpotBugs + Checkstyle
- 前端:ESLint + Prettier
- 单元测试覆盖率要求:
- Service层 ≥80%
- Controller层 ≥60%
- 复杂工具类100%
- 接口测试策略:
- 使用Postman自动化测试
- 关键路径接口100%覆盖
- 边界条件测试用例
11. 项目资源与扩展学习
11.1 配套文档清单
源码包中包含的完整文档:
code复制/docs
├── 部署手册.md
├── 接口文档.pdf
├── 数据库设计.xmind
└── 二次开发指南.md
11.2 推荐学习资源
- SpringBoot进阶:
- 《Spring Boot实战》
- 官方文档:spring.io/projects/spring-boot
- Vue深度实践:
- Vue Mastery视频课程
- Vue3 Composition API手册
- MySQL优化:
- 《高性能MySQL》第4版
- 阿里云数据库最佳实践
11.3 社区支持渠道
- 技术交流群:提供项目专属QQ群号
- Issue跟踪:GitHub仓库问题区
- 定期更新:每季度发布功能增强版
12. 版权与商用说明
- 开源协议:Apache License 2.0
- 商用授权:需联系作者获取商业授权书
- 二次开发:修改后需保留原始版权声明
- 免责条款:不承担因系统使用导致的直接或间接损失
13. 致谢与更新计划
特别感谢在疫情初期参与系统原型设计的医疗信息化专家团队。根据用户反馈,我们计划在下一版本中:
- 增加物资效期智能预警看板
- 集成电子签名审批流程
- 支持多维度数据透视分析
- 优化移动端操作体验
项目维护团队将持续跟进防疫政策变化,及时调整系统功能。欢迎通过官方渠道提交需求建议,共同完善这套抗疫工具。
