1. 项目背景与核心需求
中小制造企业在数字化转型过程中面临一个典型困境:既无法承担大型ERP系统的高额成本,又难以用Excel+纸质单据满足日益复杂的业务管理需求。我在为本地一家五金配件厂做技术咨询时,亲眼目睹仓库管理员每天要手工核对三套表格(采购入库单、销售出库单、库存台账),任何一笔差错都需要翻查半个月的纸质单据。这种低效的作业方式直接导致该厂库存准确率长期徘徊在78%左右,远低于行业90%的基准线。
这个毕业设计项目正是要解决这类痛点——用SSM+Vue技术栈构建一个轻量级但功能完备的进销存管理系统。与市面上通用型ERP不同,我们特别针对200人以下制造企业做了以下优化设计:
- 数据闭环设计:采购订单→产品入库→销售订单→出库单全链路状态联动,任何环节数据变动实时反映在库存台账
- 并发控制机制:采用MyBatis-Plus乐观锁+Redis分布式锁双重保障,经测试可承受200并发下单不超卖
- 考勤无缝集成:通过逆向工程解析考勤机厂商私有协议,打卡数据自动同步到人事系统
- 渐进式前端架构:Vue3+ElementPlus实现路由级懒加载,首屏加载时间控制在1.2秒内
2. 技术选型与架构设计
2.1 为什么选择SSM+Vue组合
在技术选型阶段,我们对比了三种主流方案:
| 方案 | 开发效率 | 学习成本 | 并发性能 | 适合场景 |
|---|---|---|---|---|
| SpringBoot+Thymeleaf | 高 | 低 | 中等 | 简单内部系统 |
| SpringCloud+React | 中 | 高 | 高 | 大型分布式系统 |
| SSM+Vue | 中高 | 中 | 中高 | 中小型单体应用 |
最终选择SSM+Vue基于以下考量:
- 技术匹配度:中小制造企业系统多为10-20张核心表的单体应用,SSM框架完全够用
- 人才储备:Java+Vue技术栈在高校和企业中普及度高,后续维护成本低
- 渐进式扩展:Vue3的Composition API比React更易上手,方便后续功能迭代
2.2 系统架构详解
系统采用经典的三层架构,但针对制造业特性做了特殊设计:
[前端层] ├─ Vue3 + ElementPlus (PC端) └─ UniApp (未来移动端扩展) [服务层] ├─ Spring MVC (RESTful API) ├─ MyBatis-Plus (ORM) ├─ Shiro (权限控制) └─ Redis (缓存/分布式锁) [数据层] ├─ MySQL 5.7 (主库) ├─ Elasticsearch (未来日志分析) └─ MinIO (文件存储)关键设计决策:
库存扣减方案:
// 乐观锁实现示例 @Transactional public boolean deductInventory(Long productId, int num) { Product product = productMapper.selectById(productId); int newStock = product.getStock() - num; if (newStock >= 0) { product.setStock(newStock); return productMapper.updateById(product) > 0; } return false; } // Redis分布式锁补充 String lockKey = "product_" + productId; try { boolean locked = redisTemplate.opsForValue() .setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS); if (locked) { // 执行库存扣减 } } finally { redisTemplate.delete(lockKey); }考勤数据同步:
- 使用JNA调用考勤机厂商DLL
- 定时任务每天23:00自动同步数据
- 原始数据经Hutool解析后存入MySQL
3. 核心功能实现细节
3.1 采购-库存联动机制
采购业务流的设计直接影响库存准确性。我们实现了以下关键逻辑:
状态机设计:
草稿 → 已审核 → 部分入库 → 已完成 ↘ 已取消入库单自动生成:
/* 采购订单明细表结构 */ CREATE TABLE `purchase_detail` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `purchase_id` bigint(20) NOT NULL COMMENT '采购单ID', `product_id` bigint(20) NOT NULL COMMENT '产品ID', `plan_quantity` int(11) NOT NULL COMMENT '计划数量', `received_quantity` int(11) DEFAULT '0' COMMENT '已入库数量', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;库存实时更新:
// 入库单审核服务 public void approveStockIn(StockIn stockIn) { // 1. 更新入库单状态 stockIn.setStatus(1); stockInMapper.updateById(stockIn); // 2. 增加库存 inventoryService.increase( stockIn.getProductId(), stockIn.getQuantity(), stockIn.getWarehouseId() ); // 3. 回写采购单已入库数量 purchaseDetailService.updateReceivedQuantity( stockIn.getPurchaseDetailId(), stockIn.getQuantity() ); }
3.2 销售出库防超卖方案
针对秒杀场景下的库存竞争问题,我们设计了多级防护:
前端限流:提交按钮防重复点击(Vue指令实现)
Vue.directive('throttle', { inserted(el, binding) { el.addEventListener('click', () => { if (!el.disabled) { el.disabled = true setTimeout(() => { el.disabled = false }, binding.value || 1000) } }) } })后端验证:
- 预扣减Redis库存
- 数据库最终一致性检查
补偿机制:
- 定时任务核对Redis与MySQL库存差异
- 自动生成库存调整单
4. 典型问题排查实录
4.1 考勤数据同步异常
问题现象:
- 考勤机数据偶尔出现乱码
- 同步进程卡死无响应
排查过程:
- 用Wireshark抓包分析考勤机通信协议
- 发现厂商DLL存在内存泄漏
- 考勤机TCP连接超时设置为30秒
解决方案:
// 改造后的同步服务 public void syncAttendance() { // 1. 建立连接 try (AttendanceMachineClient client = new AttendanceMachineClient()) { client.setTimeout(10_000); // 设置超时 // 2. 分页获取数据 int page = 1; while (true) { List<Record> records = client.getRecords(page, 100); if (records.isEmpty()) break; // 3. 转码处理 records.forEach(record -> { String name = new String(record.getName().getBytes("GBK"), "UTF-8"); // 入库逻辑... }); page++; } } catch (Exception e) { // 邮件告警 alertService.send("考勤同步异常", e.getMessage()); } }4.2 库存扣减性能优化
压测发现:
- 200并发下库存接口平均响应时间达320ms
- 出现5%的超卖现象
优化步骤:
- 增加Redis预扣减层
- 优化MySQL索引:
ALTER TABLE `inventory` ADD INDEX `idx_product_warehouse` (`product_id`, `warehouse_id`); - 调整事务隔离级别为READ_COMMITTED
优化结果:
- 平均响应时间降至85ms
- 超卖率降为0
5. 部署与运维建议
5.1 生产环境部署方案
最小化部署要求:
- 服务器:2核4G(推荐4核8G)
- 软件栈:
Nginx 1.18+ Tomcat 9.x MySQL 5.7+ (配置innodb_buffer_pool_size=2G) Redis 6.x
关键配置:
# application-prod.properties spring.datasource.hikari.maximum-pool-size=20 spring.redis.timeout=3000 server.tomcat.max-threads=2005.2 监控与日志
健康检查端点:
@RestController @RequestMapping("/monitor") public class MonitorController { @GetMapping("/health") public String health() { return "UP"; } @GetMapping("/redis") public boolean checkRedis() { try { return "PONG".equals(redisTemplate.getConnectionFactory() .getConnection().ping()); } catch (Exception e) { return false; } } }日志收集方案:
- 使用Logback按天归档日志
- ELK堆栈分析错误日志
- 关键操作记录审计日志
6. 项目扩展方向
在实际部署后,根据用户反馈可以考虑以下增强功能:
移动端适配:
- 使用UniApp开发跨平台APP
- 支持扫码入库/出库
数据分析模块:
- 集成Apache Doris实现OLAP分析
- 库存周转率可视化看板
供应链协同:
- 开放API供供应商查询采购订单状态
- 微信通知采购审批结果
这个项目最让我有成就感的,是看到试点工厂的仓库主管王师傅从每天加班对账到准时下班的变化。技术真正的价值,就体现在这些能让普通人工作更轻松的实际改进中。