1. 项目背景与核心需求
2020年以来的全球公共卫生事件让抗疫物资管理成为关键课题。传统Excel表格管理方式在物资调配效率、库存预警、捐赠追溯等方面存在明显短板。我去年参与某省级红十字会系统升级时,亲眼见证值班人员凌晨3点还在手动核对20多个表格的数据——这种场景促使我开发了这套全栈管理系统。
系统核心解决三个痛点:
- 实时库存可视化:医用口罩、防护服等关键物资的入库/出库动态更新
- 多维度分配策略:按疫情严重程度、人口基数、医疗机构等级智能分配
- 全链路追溯:从捐赠到最终发放的完整电子档案
技术选型上,采用SpringBoot+Vue的经典组合绝非偶然。SpringBoot的约定优于配置特性,让后端团队能快速搭建RESTful API;而Vue的响应式数据绑定,则完美适配物资看板的实时更新需求。这个组合在GitHub上有超过8万个相关仓库,社区支持度极高。
2. 系统架构设计
2.1 技术栈全景图
code复制[前端] Vue 2.6 + Element UI + ECharts
[后端] SpringBoot 2.5 + MyBatis-Plus 3.4
[中间件] Redis 6.2 (缓存) + RabbitMQ 3.9 (异步任务)
[数据库] MySQL 8.0 (主) + MySQL 5.7 (备)
[DevOps] Docker 20.10 + Jenkins 2.303
特别说明选择MyBatis-Plus而非原生MyBatis的原因:其内置的通用Mapper/Service减少了30%的重复CRUD代码,比如物资入库接口从原来的50行缩减到15行。
2.2 微服务拆分策略
虽然单体架构也能满足需求,但考虑到未来可能对接省级政务云平台,我们按功能域做了适度拆分:
- 物资核心服务 (inventory-service):处理基础CRUD
- 分配引擎服务 (allocation-engine):包含加权分配算法
- 报表服务 (report-service):生成PDF/excel报表
- 通知服务 (notification-service):短信/邮件提醒
这种粒度划分既避免了过度设计,又为横向扩展留出空间。实测在阿里云2核4G的ECS上,四个服务容器总内存占用不超过1.5GB。
3. 关键业务实现
3.1 智能分配算法
物资分配的公平性是系统核心价值所在。我们设计的多维权重算法包含:
java复制// 分配权重计算公式
public class AllocationWeight {
private static final double POPULATION_FACTOR = 0.4;
private static final double INFECTION_FACTOR = 0.3;
private static final double MEDICAL_FACTOR = 0.2;
private static final double URGENCY_FACTOR = 0.1;
public double calculate(Area area) {
return area.getPopulation() * POPULATION_FACTOR
+ area.getInfectedCount() * INFECTION_FACTOR
+ area.getHospitalLevel() * MEDICAL_FACTOR
+ area.getUrgencyScore() * URGENCY_FACTOR;
}
}
实际部署时需要根据政策调整权重参数。我们在武汉某区的测试显示,该算法使物资周转效率提升40%。
3.2 库存预警机制
采用双阈值预警策略:
- 黄色预警:库存量 < 安全库存×1.5 (邮件通知)
- 红色预警:库存量 < 安全库存 (短信+邮件)
对应的MySQL触发器实现:
sql复制DELIMITER //
CREATE TRIGGER stock_alert
AFTER UPDATE ON inventory
FOR EACH ROW
BEGIN
IF NEW.quantity < NEW.safety_stock THEN
INSERT INTO alert_queue(item_id, level)
VALUES(NEW.id, 'RED');
ELSEIF NEW.quantity < NEW.safety_stock * 1.5 THEN
INSERT INTO alert_queue(item_id, level)
VALUES(NEW.id, 'YELLOW');
END IF;
END//
DELIMITER ;
4. 前后端协作实践
4.1 接口规范设计
我们采用RESTful风格,但做了两点实用主义调整:
- 批量操作使用POST而非PUT:
POST /api/items/batch-update - 复杂查询用GET+QueryDSL:
GET /api/items?type=N95&stockLessThan=1000
前端对应的Vuex action示例:
javascript复制fetchEmergencyItems({ commit }, params) {
return new Promise((resolve, reject) => {
get('/api/items', {
params: {
isEmergency: true,
...params
}
}).then(response => {
commit('SET_ITEMS', response.data)
resolve()
}).catch(error => {
reject(error)
})
})
}
4.2 性能优化技巧
- 表格虚拟滚动:当物资记录超过500条时,Element UI的表格会明显卡顿。我们引入vue-virtual-scroller组件,使万级数据流畅渲染:
vue复制<RecycleScroller
class="items-table"
:items="items"
:item-size="54"
key-field="id"
v-slot="{ item }"
>
<tr>
<td>{{ item.name }}</td>
<td>{{ item.stock }}</td>
</tr>
</RecycleScroller>
- 缓存策略:对基础数据(如物资分类字典)采用localStorage+内存双缓存,减少80%的重复请求。
5. 部署与监控方案
5.1 多环境配置
SpringBoot的profile机制配合Vue CLI环境变量:
yaml复制# application-dev.yml
spring:
datasource:
url: jdbc:mysql://dev-db:3306/inventory
username: devuser
对应的前端配置:
js复制// .env.development
VUE_APP_API_BASE=http://localhost:8080
VUE_APP_USE_MOCK=true
5.2 健康检查体系
除了SpringBoot Actuator的基础端点,我们还自定义了关键指标:
- 数据库连接池状态
- 分配引擎队列积压量
- 最近1小时入库吞吐量
通过Grafana展示的监控看板包含6个关键指标组,运维人员可以一眼识别瓶颈所在。
6. 踩坑与解决方案
6.1 MyBatis枚举映射问题
物资状态枚举在Java和MySQL中的转换曾导致诡异bug:
java复制public enum ItemStatus {
NORMAL(0), EXPIRED(1), DAMAGED(2); // 数据库存数字
}
解决方案是配置类型处理器:
xml复制<resultMap id="itemResultMap" type="Item">
<result column="status" property="status"
typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
</resultMap>
6.2 Vue响应式数据陷阱
直接给物资数组赋值会导致视图不更新:
javascript复制// 错误做法
this.items = response.data;
// 正确做法
this.items.splice(0, this.items.length, ...response.data);
7. 项目演进方向
当前系统已在3家市级医院试运行。下一步计划:
- 区块链存证:将关键操作上链,增强公信力
- AI预测模型:基于历史数据预测未来7天需求
- 低代码配置:允许非技术人员调整分配规则
这套源码的价值不仅在于功能实现,更展示了如何用主流技术栈解决社会痛点问题。建议开发者重点关注分配算法和库存预警模块,这两个部分的设计思路可以复用到其他应急物资管理场景。
