在制造业数字化转型浪潮中,MRO(维护、维修、运行)耗材采购一直是个容易被忽视却至关重要的环节。去年我参与某汽车零部件企业的供应链优化项目时,发现他们每月处理2000+笔MRO采购订单,平均每单金额不足5000元,但采购团队却需要8个人全职处理——这种"高人力投入、低单值产出"的现状正是本系统要解决的核心痛点。
传统MRO采购存在三大致命伤:首先是响应滞后,产线急需一个轴承时,采购员可能要打十几个电话比价;其次是过程黑箱,从需求提出到物料入库缺乏可视化管理;最后是数据孤岛,供应商信息、合同条款、物流状态分散在不同Excel表中。我们设计的SpringBoot工业MRO耗材采购平台,本质上是用技术手段重构采购流程,实现三个关键转变:
选择SpringBoot作为基础框架并非偶然。在对比了传统SSM架构和微服务方案后,我们基于以下考量做出决策:
关键组件版本选择也经过严格测试:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.5</version> <!-- 长期支持版本 -->
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version> <!-- 完全兼容MySQL5.7/8.0 -->
</dependency>
虽然采用单体架构,但通过领域驱动设计(DDD)划分明确模块边界:
code复制com.mro.procurement
├── core/ # 核心领域层
│ ├── demand/ # 需求管理
│ ├── supplier/ # 供应商管理
│ └── contract/ # 电子合同
├── infrastructure/ # 基础设施层
│ ├── repository/ # 数据持久化
│ └── mq/ # 消息队列
└── interface/ # 接口层
├── web/ # Web控制器
└── api/ # 对外API
这种结构既保持了开发效率,又为将来可能的微服务拆分预留了空间。比如当供应商模块需要独立部署时,只需将supplier包整体迁移为新项目。
采购单推送后,系统会自动触发比价流程,其核心逻辑如下:
java复制public List<QuoteVO> compareQuotes(Long demandId) {
// 1. 获取满足条件的供应商
List<Supplier> qualifiedSuppliers = supplierService
.findByCategory(demandService.getCategory(demandId));
// 2. 获取历史交易数据作为权重参考
Map<Long, Double> performanceScores = transactionService
.calculateSupplierScores(qualifiedSuppliers);
// 3. 多维评分(价格60%+交期20%+质量20%)
return qualifiedSuppliers.stream()
.map(supplier -> {
QuoteVO vo = new QuoteVO();
vo.setPrice(supplier.getLatestQuote());
vo.setDeliveryScore(performanceScores.get(supplier.getId()));
vo.setTotalScore(vo.getPrice()*0.6 + vo.getDeliveryScore()*0.4);
return vo;
})
.sorted(Comparator.comparing(QuoteVO::getTotalScore))
.collect(Collectors.toList());
}
实际项目中我们发现,单纯按价格排序会导致供应商恶性竞争。后来增加的质量权重系数,是通过分析历史订单的退货率、质检合格率等数据动态计算的。
合同模块的难点在于法律效力和用户体验的平衡,我们的解决方案是:
ftl复制甲方:${contract.buyerName}
乙方:${contract.supplierName}
标的物:${contract.materialName}(规格:${contract.spec})
单价:¥${contract.unitPrice} 数量:${contract.quantity}
数字签名方案:
存证关键代码:
java复制public void archiveContract(Long contractId) {
Contract contract = repository.findById(contractId);
String hash = DigestUtils.sha256Hex(contract.getContent());
blockchainService.write(hash); // 区块链存证
minioClient.putObject(contractBucket, contractId+".pdf",
new ByteArrayInputStream(contract.getPdfContent()));
}
在压力测试阶段,当50个供应商同时报价时,出现部分报价丢失。通过以下步骤定位问题:
saveQuote方法平均RT达800ms解决方案:
java复制@Transactional
public void saveQuote(Quote quote) {
// 先检查供应商是否存在(使用缓存)
if (!supplierCache.exists(quote.getSupplierId())) {
throw new IllegalArgumentException("供应商不存在");
}
// 再写入(无需外键校验)
quoteRepository.insert(quote);
}
初期使用Flying Saucer渲染PDF时,10页合同需要6-8秒生成。通过以下优化降至1秒内:
yaml复制spring:
task:
execution:
pool:
core-size: 10
max-size: 50
queue-capacity: 1000
考虑MRO采购的"多品类、小批量"特点,采用纵向扩展设计:
sql复制CREATE TABLE `mro_item` (
`id` bigint NOT NULL AUTO_INCREMENT,
`category_id` int NOT NULL COMMENT '关联品类表',
`spec_json` json DEFAULT NULL COMMENT '动态规格字段',
`min_order_quantity` int DEFAULT '1',
PRIMARY KEY (`id`),
KEY `idx_category` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `procurement_order` (
`id` bigint NOT NULL,
`demand_id` bigint NOT NULL,
`supplier_id` bigint NOT NULL,
`contract_id` bigint DEFAULT NULL,
`status` enum('DRAFT','QUOTING','CONTRACTED','DELIVERED') NOT NULL,
`tracking_info` json DEFAULT NULL COMMENT '物流轨迹',
PRIMARY KEY (`id`),
KEY `idx_demand` (`demand_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
特别说明:
spec_json字段采用JSON类型存储动态属性,比如轴承的"内径/外径/材质"等非标参数。这种设计比传统的EAV模型更易维护。
通过事件驱动架构实现实时库存监控:
java复制@EventListener
public void handleInventoryChange(InventoryEvent event) {
// 获取品类安全库存阈值
Optional<Threshold> threshold = thresholdRepository
.findByCategoryId(event.getCategoryId());
threshold.ifPresent(t -> {
if (event.getCurrent() < t.getMinLevel()) {
Demand demand = new Demand();
demand.setCategoryId(event.getCategoryId());
demand.setQuantity(t.getReorderQty());
demandService.createAutoDemand(demand);
}
});
}
基于阿里云ECS的实际部署经验:
code复制-Xms4g -Xmx4g -XX:MaxMetaspaceSize=512m
-XX:+UseG1GC -XX:MaxGCPauseMillis=200
ini复制[mysqld]
innodb_buffer_pool_size=4G
innodb_log_file_size=1G
transaction-isolation=READ-COMMITTED
使用JMeter模拟100并发测试:
| 接口名称 | TPS | 平均RT | 错误率 |
|---|---|---|---|
| 提交报价 | 285 | 112ms | 0% |
| 生成合同 | 98 | 203ms | 1.2% |
| 扫码收货 | 320 | 89ms | 0% |
python复制# 示例预测代码
from prophet import Prophet
model = Prophet(seasonality_mode='multiplicative')
model.fit(df) # df包含历史消耗数据
future = model.make_future_dataframe(periods=30)
forecast = model.predict(future)
供应商画像:整合工商数据、履约记录等构建评分模型
移动端适配:开发微信小程序实现"扫码收货"等高频操作
这个项目给我的深刻启示是:好的业务系统设计必须吃透行业特性。比如我们最初设计的通用采购流程,后来才发现MRO场景下"紧急采购"占比高达40%,于是专门增加了绿色通道机制。建议开发者在类似项目中,至少花两周时间深入客户现场,观察真实的业务流程和痛点。