温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片!
一、 项目背景与意义
在制造业数字化转型浪潮下,传统的生产计划管理方式(如Excel表格、纸质单据)已难以满足现代企业对于生产敏捷性、资源优化和实时决策的需求。生产计划作为连接销售、采购、库存与车间执行的枢纽,其管理效率直接影响到订单交付周期、生产成本与客户满意度。
基于SpringBoot的工业生产计划管理系统旨在构建一个集成的、可扩展的数字化管理平台,其核心意义在于:
- 提升计划编制效率:通过算法辅助(如MRP运算)自动生成主生产计划(MPS)与物料需求计划(MRP),大幅缩短人工排程时间。
- 实现资源精细化管理:对设备、人力、物料等资源进行统一视图管理,优化资源配置,减少资源闲置与瓶颈。
- 增强生产过程可视化与可控性:实时跟踪计划执行进度,通过看板、甘特图等方式直观展示,支持动态调整以应对插单、设备故障等异常。
- 促进部门协同:打通销售、计划、采购、生产、仓储等部门的信息流,确保数据同源、实时同步,减少沟通成本与信息误差。
- 为决策提供数据支撑:积累生产历史数据,通过报表分析为产能规划、效率提升、成本控制提供量化依据。
二、 技术栈选型
本系统采用前后端分离的微服务架构,后端基于SpringBoot生态,前端采用现代Vue.js框架,确保系统的高性能、可维护性与可扩展性。
2.1 后端技术栈
- 核心框架:Spring Boot 2.7+(提供快速启动、自动配置、内嵌容器)
- Web层:Spring MVC
- 数据持久层:
- ORM框架:MyBatis-Plus(增强CRUD操作,简化开发)
- 数据库连接池:HikariCP
- 数据库:
- 主库:MySQL 8.0(存储业务核心数据,如订单、计划、物料清单BOM)
- 缓存:Redis(存储会话、热点数据、计划锁,提升并发性能)
- 安全与权限:Spring Security + JWT(JSON Web Token)实现认证与接口鉴权
- API文档:Knife4j(集成Swagger,生成在线API文档)
- 消息中间件:RabbitMQ(用于异步处理计划发布、状态变更通知等场景)
- 任务调度:Quartz / Spring Scheduler(用于定时执行MRP运算、数据同步等任务)
- 构建与依赖管理:Maven
2.2 前端技术栈
- 框架:Vue 3 + Composition API
- 构建工具:Vite
- UI组件库:Element Plus
- 状态管理:Pinia
- 路由:Vue Router
- HTTP客户端:Axios
- 可视化图表:ECharts(用于生产甘特图、资源负荷图、数据看板)
2.3 部署与运维
- 容器化:Docker + Docker Compose
- 持续集成/持续部署(CI/CD):Jenkins / GitLab CI
- 版本控制:Git
三、 系统核心功能模块设计
系统采用模块化设计,核心功能模块如下:
3.1 基础数据管理
- 物料主数据:管理原材料、半成品、成品的编码、名称、规格、单位等。
- 物料清单(BOM)管理:定义产品结构,支持多级BOM、版本管理。
- 工作中心/资源管理:定义生产线、设备、班组等生产资源及其产能、日历。
- 工艺路线管理:定义产品加工的工序顺序、所需资源、标准工时。
3.2 销售与需求管理
- 销售订单管理:接收客户订单,明确产品、数量、交期。
- 需求预测:支持基于历史数据的预测模型,生成预测订单。
3.3 主生产计划(MPS)
- 计划编制:基于销售订单与需求预测,考虑现有库存、在制品,编制产品级的主生产计划。
- 粗能力计划(RCCP):校验关键资源(瓶颈设备)的负荷是否满足计划。
- 计划审批与发布:工作流驱动计划审批,发布后锁定。
3.4 物料需求计划(MRP)
- MRP运算:根据MPS、BOM、库存、在途量,自动计算各级物料(采购件、自制件)的净需求、建议计划订单(生产/采购)及时间。
- 计划订单管理:对MRP生成的建议订单进行审核、调整、下达。
3.5 车间作业计划(SFC)与派工
- 工序级排程:将生产订单分解到具体工作中心的工序任务。
- 派工单管理:生成并下发派工单到车间终端或移动设备。
- 生产执行反馈:接收工序报工(数量、工时、质量状态)。
3.6 生产进度跟踪与看板
- 甘特图:可视化展示订单、设备、工序的排程与进度。
- 生产实绩看板:实时展示产量、效率、不良率等关键指标。
- 异常报警:对计划延迟、质量异常等事件进行预警。
四、 数据库核心表结构设计
以下为部分核心业务表的设计:
-- 物料主数据表 CREATE TABLE `base_material` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', `material_code` varchar(64) NOT NULL COMMENT '物料编码', `material_name` varchar(255) NOT NULL COMMENT '物料名称', `specification` varchar(500) DEFAULT NULL COMMENT '规格型号', `unit` varchar(20) DEFAULT NULL COMMENT '单位', `material_type` tinyint DEFAULT NULL COMMENT '物料类型:1-原材料,2-半成品,3-成品', `safety_stock` decimal(18,6) DEFAULT NULL COMMENT '安全库存', `lead_time` int DEFAULT NULL COMMENT '提前期(天)', `status` tinyint DEFAULT '1' COMMENT '状态:0-禁用,1-启用', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_material_code` (`material_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物料主数据'; -- 物料清单(BOM)表 CREATE TABLE base_bom ( id bigint NOT NULL AUTO_INCREMENT, parent_material_id bigint NOT NULL COMMENT '父项物料ID', child_material_id bigint NOT NULL COMMENT '子项物料ID', usage_qty decimal(18,6) NOT NULL COMMENT '用量', loss_rate decimal(5,4) DEFAULT '0.0000' COMMENT '损耗率', bom_version varchar(20) DEFAULT NULL COMMENT 'BOM版本', effective_date date DEFAULT NULL COMMENT '生效日期', expiry_date date DEFAULT NULL COMMENT '失效日期', PRIMARY KEY (id), KEY idx_parent_material (parent_material_id), KEY idx_child_material (child_material_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物料清单'; -- 销售订单表 CREATE TABLE sales_order ( id bigint NOT NULL AUTO_INCREMENT, order_no varchar(64) NOT NULL COMMENT '订单号', customer_id bigint DEFAULT NULL COMMENT '客户ID', product_material_id bigint NOT NULL COMMENT '产品物料ID', order_qty decimal(18,6) NOT NULL COMMENT '订单数量', delivery_date date NOT NULL COMMENT '要求交期', order_status tinyint DEFAULT '1' COMMENT '状态:1-新建,2-已确认,3-已关闭', create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='销售订单'; -- 主生产计划(MPS)表 CREATE TABLE plan_mps ( id bigint NOT NULL AUTO_INCREMENT, plan_no varchar(64) NOT NULL COMMENT '计划编号', material_id bigint NOT NULL COMMENT '物料ID', plan_qty decimal(18,6) NOT NULL COMMENT '计划数量', plan_date date NOT NULL COMMENT '计划日期', plan_type tinyint DEFAULT NULL COMMENT '计划类型:1-订单,2-预测', source_order_id bigint DEFAULT NULL COMMENT '来源订单ID', status tinyint DEFAULT '1' COMMENT '状态:1-草案,2-已审核,3-已发布,4-已关闭', create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_plan_no (plan_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='主生产计划'; -- 生产订单表(由MRP运算生成或手动创建) CREATE TABLE production_order ( id bigint NOT NULL AUTO_INCREMENT, order_no varchar(64) NOT NULL COMMENT '生产订单号', material_id bigint NOT NULL COMMENT '物料ID', plan_qty decimal(18,6) NOT NULL COMMENT '计划数量', start_date date DEFAULT NULL COMMENT '计划开始日期', end_date date DEFAULT NULL COMMENT '计划完成日期', priority tinyint DEFAULT '5' COMMENT '优先级(1-10)', order_status tinyint DEFAULT '1' COMMENT '状态:1-新建,2-已下达,3-生产中,4-已完成,5-已关闭', create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生产订单';五、 核心业务逻辑与代码实现
5.1 主生产计划(MPS)编制服务
核心服务类,负责基于销售订单和预测生成主生产计划。
package com.example.ppms.service.plan; import com.example.ppms.domain.entity.SalesOrder; import com.example.ppms.domain.entity.PlanMps; import com.example.ppms.domain.entity.BaseMaterial; import com.example.ppms.mapper.PlanMpsMapper; import com.example.ppms.mapper.SalesOrderMapper; import com.example.ppms.mapper.BaseMaterialMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.util.List; import java.util.ArrayList; /** 主生产计划(MPS)服务 */ @Slf4j @Service @RequiredArgsConstructor public class MpsService { private final SalesOrderMapper salesOrderMapper; private final BaseMaterialMapper materialMapper; private final PlanMpsMapper planMpsMapper; /** 根据销售订单生成主生产计划草案 @param startDate 计划开始日期 @param endDate 计划结束日期 @return 生成的MPS记录列表 */ @Transactional(rollbackFor = Exception.class) public List<PlanMps> generateMpsFromSalesOrders(LocalDate startDate, LocalDate endDate) { // 1. 查询指定时间范围内已确认的销售订单 List<SalesOrder> confirmedOrders = salesOrderMapper.selectConfirmedOrdersByDeliveryDate(startDate, endDate); List<PlanMps> mpsList = new ArrayList<>(); for (SalesOrder order : confirmedOrders) { BaseMaterial product = materialMapper.selectById(order.getProductMaterialId()); if (product == null || !product.isFinishedGood()) { log.warn("销售订单 {} 对应的产品物料不存在或不是成品,跳过。", order.getOrderNo()); continue; } // 2. 构建MPS记录 PlanMps mps = new PlanMps(); mps.setPlanNo(generatePlanNo()); // 生成计划编号 mps.setMaterialId(order.getProductMaterialId()); mps.setPlanQty(order.getOrderQty()); mps.setPlanDate(order.getDeliveryDate()); // 计划日期默认为订单交期 mps.setPlanType(PlanMps.PlanType.ORDER); mps.setSourceOrderId(order.getId()); mps.setStatus(PlanMps.Status.DRAFT); // 3. 保存到数据库 planMpsMapper.insert(mps); mpsList.add(mps); log.info("已为销售订单 {} 生成MPS草案:{}", order.getOrderNo(), mps.getPlanNo()); } return mpsList; } private String generatePlanNo() { // 示例:MPS + 年月日 + 序列号 (实际应使用分布式ID或序列) String prefix = "MPS" + LocalDate.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd")); Long sequence = planMpsMapper.selectTodayCount(prefix) + 1; return prefix + String.format("%04d", sequence); } }5.2 物料需求计划(MRP)运算服务
核心算法服务,根据MPS、BOM和库存计算物料净需求。
package com.example.ppms.service.plan; import com.example.ppms.domain.entity.; import com.example.ppms.mapper.; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.util.*; /** 物料需求计划(MRP)运算服务 */ @Slf4j @Service @RequiredArgsConstructor public class MrpService { private final PlanMpsMapper planMpsMapper; private final BaseBomMapper bomMapper; private final InventoryMapper inventoryMapper; private final ProductionOrderMapper productionOrderMapper; private final PurchasePlanMapper purchasePlanMapper; /** 执行MRP运算 @param mpsIds 参与运算的MPS ID列表 @param planDate 计划基准日期 */ @Transactional(rollbackFor = Exception.class) public void runMrpCalculation(List<Long> mpsIds, LocalDate planDate) { // 1. 获取已发布的MPS List<PlanMps> mpsList = planMpsMapper.selectPublishedMpsByIds(mpsIds); if (mpsList.isEmpty()) { log.warn("未找到已发布的MPS记录,MRP运算终止。"); return; } // 2. 按时间桶汇总毛需求 (物料ID -> 日期 -> 数量) Map<Long, Map<LocalDate, Double>> grossRequirements = new HashMap<>(); for (PlanMps mps : mpsList) { addGrossRequirement(grossRequirements, mps.getMaterialId(), mps.getPlanDate(), mps.getPlanQty()); } // 3. 递归展开BOM,计算各级物料毛需求 expandBomRequirements(grossRequirements); // 4. 计算净需求并生成计划订单 for (Map.Entry<Long, Map<LocalDate, Double>> entry : grossRequirements.entrySet()) { Long materialId = entry.getKey(); BaseMaterial material = materialMapper.selectById(materialId); if (material == null) continue; Map<LocalDate, Double> dateQtyMap = entry.getValue(); List<LocalDate> sortedDates = new ArrayList<>(dateQtyMap.keySet()); Collections.sort(sortedDates); double projectedInventory = inventoryMapper.getOnHandQty(materialId); // 当前库存 for (L