1. 项目背景与核心需求
这个仓库物资采购管理系统本质上要解决的是企业物资流转中的三个核心痛点:采购流程不透明、库存状态滞后、审批效率低下。我去年为一家中型制造企业实施类似系统时,发现他们手工记录采购单平均需要2.3天才能完成审批,而系统上线后这个时间缩短到了27分钟。
系统采用Vue+JavaWeb的技术栈有其必然性:前端需要快速响应的数据看板和表单交互(Vue的优势领域),后端要处理复杂的物资编码规则和供应商关系(Java的企业级生态优势)。特别提醒:物资编码一定要采用"品类+规格+批次"的三段式结构,这是血泪教训——我们第一个版本用简单自增ID,结果盘点时出现了大量混乱。
2. 技术架构设计
2.1 前端Vue架构要点
采用Vue CLI 4.x搭建的模块化架构,核心是这三个设计决策:
- 使用Vuex管理采购单状态流(包括草稿、待审、已驳回、已通过等7种状态)
- 基于Element UI二次开发表格组件,支持物资编码的模糊搜索
- 路由懒加载拆分为:采购模块(/purchase)、库存模块(/stock)、报表模块(/report)
实测中发现必须做性能优化的两个地方:
- 采购明细表格超过50条数据时,要启用虚拟滚动
- 使用vue-print-nb插件实现打印功能时,需动态注入CSS样式
2.2 后端JavaWeb实现
SpringBoot 2.5 + MyBatis Plus的经典组合,但有几个关键配置:
java复制// 物资编码生成规则
public class MaterialCodeGenerator {
// 示例:FIT-08-20230715-001
public static String generate(String category, String spec) {
return category + "-" + spec + "-" +
LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE) +
"-" + String.format("%03d", serialNumber.getAndIncrement());
}
}
数据库设计中最容易出错的是库存流水表:
sql复制CREATE TABLE inventory_flow (
id BIGINT PRIMARY KEY,
material_code VARCHAR(32) NOT NULL COMMENT '关联物资编码',
change_type TINYINT NOT NULL COMMENT '1入库 2出库 3盘点调整',
before_quantity DECIMAL(12,3) NOT NULL,
change_quantity DECIMAL(12,3) NOT NULL,
after_quantity DECIMAL(12,3) NOT NULL,
operator_id BIGINT NOT NULL,
operation_time DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 核心业务逻辑实现
3.1 采购申请工作流
采用状态机模式实现审批流程,这是比if-else更优雅的解决方案:
java复制public enum PurchaseState {
DRAFT {
@Override
public void submit(PurchaseOrder order) {
order.setState(PENDING_REVIEW);
// 发送企业微信通知
}
},
PENDING_REVIEW {
@Override
public void approve(PurchaseOrder order) {
if(order.getTotalAmount() > 50000) {
order.setState(PENDING_FINANCE_REVIEW);
} else {
order.setState(APPROVED);
}
}
},
// 其他状态...
}
3.2 库存实时预警
通过Redis的ZSET实现库存水位预警:
- 物资编码作为member
- 当前库存量作为score
- 定时任务检查低于安全库存的物资
redis复制ZADD inventory_warning 15 "FIT-08-20230715-001"
ZRANGEBYSCORE inventory_warning -inf 20 WITHSCORES
4. 典型问题解决方案
4.1 采购单并发修改
使用乐观锁避免多人同时修改:
xml复制<update id="updatePurchaseOrder">
UPDATE purchase_order
SET version = version + 1,
<!-- 其他字段 -->
WHERE id = #{id} AND version = #{version}
</update>
前端需要处理更新冲突的异常情况:
javascript复制async handleSubmit() {
try {
await this.$api.updateOrder(this.form)
} catch (e) {
if(e.response.status === 409) {
this.$confirm('该单据已被他人修改,是否重新加载?').then(() => {
this.fetchLatestData()
})
}
}
}
4.2 大批量数据导出
采用分页查询+流式导出避免OOM:
java复制@GetMapping("/export")
public void export(HttpServletResponse response) {
response.setContentType("application/vnd.ms-excel");
try (ExcelWriter writer = EasyExcel.write(response.getOutputStream())) {
int page = 1;
while (true) {
Page<Material> data = materialService.page(new Page<>(page, 1000));
if (data.getRecords().isEmpty()) break;
writer.write(data.getRecords(),
EasyExcel.writerSheet("物资数据").build());
page++;
}
}
}
5. 部署与运维实践
5.1 前端优化方案
通过webpack splitChunks优化打包:
javascript复制configureWebpack: {
optimization: {
splitChunks: {
chunks: 'all',
maxSize: 244 * 1024 // 拆分为不超过244KB的chunk
}
}
}
5.2 后端监控配置
SpringBoot Actuator关键端点配置:
yaml复制management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
metrics:
export:
prometheus:
enabled: true
tags:
application: ${spring.application.name}
6. 项目演进建议
-
二期可增加的功能:
- 供应商评价体系(质量、交期、服务三个维度)
- 智能补货预测(基于历史消耗数据的机器学习模型)
-
性能优化方向:
- 库存查询改用Elasticsearch
- 审批流引擎迁移到Activiti
-
移动端适配方案:
- 基于Vant的H5版本
- 企业微信微应用接入
这个系统最让我有成就感的是上线后客户反馈的一句话:"现在我能随时知道仓库里每一颗螺丝钉的位置"。技术实现上要注意物资编码的权威性和一致性,我们曾经因为两个部门使用不同编码规则导致库存数据对不上,最后不得不做了一次全库清洗。
