# 生产订单、排产、报工优化方案 ## 一、现状分析 ### 1.1 数据模型关系 ``` 生产工单 (mes_pro_work_order) ├── 工单工序 (mes_pro_work_order_process) -- 工单级别的工序安排 ├── 工单BOM (mes_pro_work_order_bom) -- 工单级别的物料清单 └── 生产任务 (mes_pro_task) -- 排产后的生产任务 ├── 任务投料 (mes_pro_task_issue) -- 任务级别的投料记录 └── 报工单 (mes_pro_feedback) -- 任务级别的报工记录 ├── 物料消耗 (mes_wm_item_consume) -- 报工消耗的物料 └── 产出单 (mes_wm_product_produce) -- 报工产出的产品 ``` ### 1.2 当前状态流转 | 实体 | 状态流转 | 触发条件 | |------|---------|---------| | **生产工单** | 草稿 → 已确认 → 已完成/已取消 | 手动确认、手动完成/取消 | | **生产任务** | 草稿 → 已完成/已取消 | 工单完成/取消时级联更新 | | **报工单** | 草稿 → 审批中 → 待检验/已完成 → 已完成 | 手动提交、审批通过 | ### 1.3 存在的问题 #### 问题1:工单工序与任务关系断裂 - `MesProWorkOrderProcessDO` 记录工单的工序安排,但创建任务时没有校验工序是否在工单工序中 - 任务可以随意指定任意工序,导致工单工序配置无效 - 排产时需要手动逐个工序创建任务,效率低 #### 问题2:排产数量无约束 - `quantityScheduled`(已排产数量)字段存在但没有累加逻辑 - 可以超量排产,无校验 - 无法直观看到工单的排产进度 #### 问题3:任务缺少中间状态 - 任务只有三种状态:草稿、已完成、已取消 - 缺少「生产中」「部分完成」等中间状态 - 无法区分「未开始生产」和「正在生产」 #### 问题4:报工流程繁琐 - 报工需要从独立菜单进入,手动选择任务 - 无法从任务列表直接发起报工 - 每次报工需要经过:新增 → 保存 → 提交 → 审批 4步 #### 问题5:工单完成条件不明确 - 工单完成是手动操作,不判断实际进度 - 可能工单数量未达标就被手动完成 - 缺少「自动完成」机制 #### 问题6:前端操作割裂 - 工单、排产、报工是三个独立菜单 - 工单确认后需手动跳到「排产管理」创建任务 - 任务创建后需手动跳到「报工列表」创建报工 - 用户需要在多个页面来回切换 --- ## 二、优化方案 ### 2.1 数据模型优化 #### 2.1.1 任务状态扩展 **新增任务状态枚举值**: | 状态 | 值 | 说明 | 进入条件 | |------|---|------|---------| | PREPARE | 0 | 草稿 | 任务创建时 | | PRODUCING | 1 | 生产中 | 首次报工审批通过后 | | PARTIAL_FINISHED | 2 | 部分完成 | 已生产数量 > 0 且 < 排产数量 | | FINISHED | 3 | 已完成 | 已生产数量 >= 排产数量 或 工单完成 | | CANCELED | 4 | 已取消 | 工单取消时 | **修改文件**:`MesProTaskStatusEnum.java` ```java public enum MesProTaskStatusEnum implements ArrayValuable { PREPARE(0, "草稿"), PRODUCING(1, "生产中"), PARTIAL_FINISHED(2, "部分完成"), FINISHED(3, "已完成"), CANCELED(4, "已取消"); /** * 判断是否为终态 */ public static boolean isEndStatus(Integer status) { return ObjectUtils.equalsAny(status, FINISHED.status, CANCELED.status); } /** * 判断是否可以报工(草稿或非终态) */ public static boolean canFeedback(Integer status) { return !isEndStatus(status); } } ``` #### 2.1.2 工单增加排产相关字段 **新增字段**(已存在,补充逻辑): | 字段 | 类型 | 说明 | |------|------|------| | quantityScheduled | decimal(24,6) | 已排产数量(关键工序任务数量之和) | | quantityProduced | decimal(24,6) | 已生产数量(关键工序报工数量之和) | **新增字段**: | 字段 | 类型 | 说明 | |------|------|------| | route_id | bigint | 工艺路线编号(确认时从产品默认路线获取或手动选择) | | auto_finish_flag | tinyint(1) | 是否自动完成(默认 true,报工达标后自动完成工单) | #### 2.1.3 任务增加进度追踪字段 **新增字段**: | 字段 | 类型 | 说明 | |------|------|------| | first_feedback_time | datetime | 首次报工时间(状态变为生产中时记录) | | last_feedback_time | datetime | 最近报工时间 | **数据库脚本**: ```sql -- 任务表新增字段 ALTER TABLE mes_pro_task ADD COLUMN first_feedback_time datetime COMMENT '首次报工时间' AFTER cancel_date; ALTER TABLE mes_pro_task ADD COLUMN last_feedback_time datetime COMMENT '最近报工时间' AFTER first_feedback_time; -- 工单表新增字段 ALTER TABLE mes_pro_work_order ADD COLUMN route_id bigint COMMENT '工艺路线编号' AFTER batch_code; ALTER TABLE mes_pro_work_order ADD COLUMN auto_finish_flag tinyint(1) DEFAULT 1 COMMENT '是否自动完成' AFTER route_id; ``` ### 2.2 业务逻辑优化 #### 2.2.1 排产数量校验 **规则**: - 创建任务时校验:`工单.已排产数量 + 本次排产数量 <= 工单.生产数量` - 任务创建成功后累加工单的 `quantityScheduled` - 任务删除时扣减工单的 `quantityScheduled` **接口变更**:`MesProWorkOrderService` ```java /** * 累加工单的已排产数量 * * @param id 工单编号 * @param incrQuantityScheduled 本次排产数量增量 */ void updateScheduledQuantity(Long id, BigDecimal incrQuantityScheduled); ``` **Mapper 新增方法**:`MesProWorkOrderMapper` ```java @Update("UPDATE mes_pro_work_order SET quantity_scheduled = quantity_scheduled + #{incrQuantity} " + "WHERE id = #{id} AND deleted = 0") void updateScheduledQuantity(@Param("id") Long id, @Param("incrQuantity") BigDecimal incrQuantity); ``` #### 2.2.2 一键排产接口 **接口设计**: ```java /** * 一键排产:根据工艺路线自动生成所有工序任务 * * @param workOrderId 工单编号 * @param routeId 工艺路线编号(可选,不传则使用工单关联的工艺路线) * @return 生成的任务ID列表 */ List autoGenerateTasks(Long workOrderId, Long routeId); ``` **业务逻辑**: 1. 校验工单存在且状态为「已确认」 2. 获取工艺路线工序列表(按 sort 排序) 3. 计算每个工序的排产数量: - 首道工序:工单生产数量 - 后续工序:前道工序数量 × 产出比(默认 1:1) 4. 自动分配工作站: - 优先分配工艺路线工序配置的工作站 - 若未配置,则分配产品默认工作站 - 若仍无,则留空由用户手动调整 5. 批量创建任务,累加工单已排产数量 6. 自动创建工单工序记录(`mes_pro_work_order_process`) **Controller 接口**: ```java @PostMapping("/auto-generate") @Operation(summary = "一键排产") @PreAuthorize("@ss.hasPermission('mes:pro-task:create')") public CommonResult> autoGenerateTasks( @RequestParam("workOrderId") Long workOrderId, @RequestParam(value = "routeId", required = false) Long routeId) { return success(taskService.autoGenerateTasks(workOrderId, routeId)); } ``` #### 2.2.3 任务状态自动流转 **触发时机**:报工审批通过后 **状态流转规则**: ``` 当前状态 报工后状态判断 ───────────────────────────────────────────────── 草稿(0) → 生产中(1) 或 部分完成(2) 或 已完成(3) 生产中(1) → 生产中(1) 或 部分完成(2) 或 已完成(3) 部分完成(2)→ 部分完成(2) 或 已完成(3) ``` **判断逻辑**: ```java if (producedQuantity.compareTo(quantity) >= 0) { // 已生产 >= 计划 → 已完成 newStatus = MesProTaskStatusEnum.FINISHED; } else if (producedQuantity.compareTo(BigDecimal.ZERO) > 0) { // 0 < 已生产 < 计划 → 部分完成(或保持生产中) newStatus = MesProTaskStatusEnum.PARTIAL_FINISHED; } else { // 已生产 = 0 且有报工 → 生产中(首次报工) newStatus = MesProTaskStatusEnum.PRODUCING; } ``` #### 2.2.4 工单进度自动更新 **规则**: - 所有关键工序的报工审批通过后,累加工单的 `quantityProduced` - 非关键工序的报工不更新工单进度(只更新任务进度) **触发时机**:报工审批通过后 ```java // MesProFeedbackServiceImpl#approveFeedback if (keyFlag) { // 关键工序:更新工单已生产数量 workOrderService.updateProducedQuantity(feedback.getWorkOrderId(), feedback.getFeedbackQuantity()); // 检查是否自动完成 checkAndAutoFinishWorkOrder(feedback.getWorkOrderId()); } ``` #### 2.2.5 工单自动完成检查 **规则**: - 当工单的 `autoFinishFlag = true` 时,检查是否满足自动完成条件 - 条件:所有关键工序任务的已生产数量 >= 工单生产数量 - 或者:工单下所有任务状态都为「已完成」 **接口设计**: ```java /** * 检查并自动完成工单 * * @param workOrderId 工单编号 * @return true=已自动完成, false=未满足条件 */ boolean checkAndAutoFinishWorkOrder(Long workOrderId); ``` **业务逻辑**: ```java public boolean checkAndAutoFinishWorkOrder(Long workOrderId) { MesProWorkOrderDO workOrder = getWorkOrder(workOrderId); if (workOrder == null || !Boolean.TRUE.equals(workOrder.getAutoFinishFlag())) { return false; } if (workOrder.getStatus() != MesProWorkOrderStatusEnum.CONFIRMED.getStatus()) { return false; } // 检查:已生产数量 >= 生产数量 if (workOrder.getQuantityProduced().compareTo(workOrder.getQuantity()) >= 0) { finishWorkOrder(workOrderId); return true; } return false; } ``` #### 2.2.6 工序校验增强 **规则**: - 创建任务时,校验工序必须在工单工序列表中 - 如果工单没有工序记录,允许创建(兼容老数据) **修改**:`MesProTaskServiceImpl#createTask` ```java // 校验工序在工单工序中(如果工单有工序配置) List workOrderProcesses = workOrderProcessService.getListByWorkOrderId(createReqVO.getWorkOrderId()); if (CollUtil.isNotEmpty(workOrderProcesses)) { boolean processExists = workOrderProcesses.stream() .anyMatch(p -> ObjUtil.equal(p.getProcessId(), createReqVO.getProcessId())); if (!processExists) { throw exception(PRO_TASK_PROCESS_NOT_IN_WORK_ORDER); } } ``` ### 2.3 前端交互优化 #### 2.3.1 整合为「工单中心」 **设计思路**:将工单、排产、报工整合到一个页面,通过 Tab 切换不同视图 **页面结构**: ``` ┌─────────────────────────────────────────────────────────────────────┐ │ 工单中心 │ ├─────────────────────────────────────────────────────────────────────┤ │ 【搜索条件区】 │ │ 工单编码: [____] 产品: [____] 状态: [全部 ▼] 客户: [____] │ │ [查询] [重置] │ ├─────────────────────────────────────────────────────────────────────┤ │ 【工单列表区】 │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ 工单编码 │ 产品 │ 计划数量 │ 已排产 │ 已生产 │ 进度 │ 状态 │ 操作│ │ │ │ WO001 │ A │ 100 │ 100 │ 80 │ ████████░░ │ 生产中│ 详情│ │ │ WO002 │ B │ 50 │ 0 │ 0 │ ░░░░░░░░░░ │ 已确认│ 排产│ │ └─────────────────────────────────────────────────────────────────┘ │ ├─────────────────────────────────────────────────────────────────────┤ │ 【工单详情区】(点击工单编码展开) │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ 【工序进度条】 │ │ │ │ [下料] → [机加工] → [热处理] → [质检] → [包装] │ │ │ │ ✓ 生产中 待生产 待生产 待生产 │ │ │ │ 100% 60% 0% 0% 0% │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 【当前工序任务列表】(点击工序tab切换) │ │ │ │ [下料] [机加工] [热处理] [质检] [包装] │ │ │ │ ┌───────────────────────────────────────────────────────────────┐│ │ │ │ │ 任务编码 │ 工作站 │ 计划 │ 已报工 │ 进度 │ 状态 │ 操作 ││ │ │ │ │ T001 │ A-01 │ 60 │ 36 │ 60% │ 生产中│ [报工] ││ │ │ │ │ T002 │ A-02 │ 40 │ 0 │ 0% │ 草稿 │ [编辑] [删除]││ │ │ │ └───────────────────────────────────────────────────────────────┘│ │ │ │ [新增任务] [一键排产] │ │ │ └─────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ ``` #### 2.3.2 快速报工入口 **场景1:从任务列表报工** ``` 任务行操作列 → 点击「报工」按钮 → 弹出报工表单 ┌────────────────────────────────────┐ │ 快速报工 │ ├────────────────────────────────────┤ │ 任务编码: T001 (自动带入,不可改) │ │ 工作站: A-01 (自动带入) │ │ 计划数量: 60 (自动带入) │ │ 已报工: 36 (自动带入) │ │ ───────────────────────────── │ │ 本次报工数量: [____] * │ │ 合格品数量: [____] │ │ 不良品数量: [____] │ │ 备注: [________________] │ ├────────────────────────────────────┤ │ [取消] [保存并提交] │ └────────────────────────────────────┘ ``` **场景2:批量报工** ``` 选中多个任务 → 点击「批量报工」→ 填写相同数量批量提交 ┌────────────────────────────────────┐ │ 批量报工 │ ├────────────────────────────────────┤ │ 已选择 3 个任务: │ │ - T001 (机加工, 计划60, 已报36) │ │ - T003 (机加工, 计划50, 已报20) │ │ - T005 (机加工, 计划40, 已报0) │ │ ───────────────────────────── │ │ 本次报工数量: [____] (应用到所有) │ │ 备注: [________________] │ ├────────────────────────────────────┤ │ [取消] [保存并提交] │ └────────────────────────────────────┘ ``` #### 2.3.3 一键排产交互 ``` ┌────────────────────────────────────────┐ │ 一键排产 │ ├────────────────────────────────────────┤ │ 工单编号: WO002 │ │ 产品: 产品A │ │ 生产数量: 100 │ │ ───────────────────────────── │ │ 工艺路线: [产品A默认路线 ▼] │ │ │ │ 预生成任务预览: │ │ ┌────────────────────────────────────┐ │ │ │ 工序 │ 工作站 │ 数量 │ 时间 │ │ │ │ 下料 │ 自动 │ 100 │ 8:00 │ │ │ │ 机加工 │ 自动 │ 100 │ 10:00 │ │ │ │ 热处理 │ 自动 │ 100 │ 14:00 │ │ │ │ 质检 │ 自动 │ 100 │ 16:00 │ │ │ │ 包装 │ 自动 │ 100 │ 17:00 │ │ │ └────────────────────────────────────┘ │ │ ☑ 自动分配工作站 │ │ ☑ 自动计算生产时间 │ ├────────────────────────────────────────┤ │ [取消] [确认排产] │ └────────────────────────────────────────┘ ``` #### 2.3.4 进度可视化 **工单列表进度条**: ```html ``` **工序进度条**: ```html
{{ process.processName }}
{{ process.progress }}%
``` --- ## 三、接口变更清单 ### 3.1 新增接口 | 接口 | 方法 | 路径 | 说明 | |------|------|------|------| | 一键排产 | POST | /mes/pro-task/auto-generate | 根据工艺路线自动生成所有工序任务 | | 批量报工 | POST | /mes/pro-feedback/batch-create | 批量创建报工单 | | 工单进度 | GET | /mes/pro-work-order/progress/{id} | 获取工单进度详情(含各工序进度) | | 工单自动完成检查 | POST | /mes/pro-work-order/check-auto-finish/{id} | 检查并自动完成工单 | ### 3.2 修改接口 | 接口 | 方法 | 路径 | 变更说明 | |------|------|------|---------| | 创建任务 | POST | /mes/pro-task/create | 增加工序校验、排产数量校验、累加工单已排产数量 | | 删除任务 | DELETE | /mes/pro-task/delete | 扣减工单已排产数量 | | 审批报工 | POST | /mes/pro-feedback/approve | 增加任务状态流转逻辑、工单自动完成检查 | | 确认工单 | POST | /mes/pro-work-order/confirm | 可选择关联工艺路线 | | 工单详情 | GET | /mes/pro-work-order/get | 增加返回 `routeId`、`quantityScheduled`、`quantityProduced` | ### 3.3 新增 VO #### 工单进度 VO ```java @Data public class MesProWorkOrderProgressRespVO { /** 工单ID */ private Long workOrderId; /** 工单编码 */ private String workOrderCode; /** 生产数量 */ private BigDecimal quantity; /** 已排产数量 */ private BigDecimal quantityScheduled; /** 已生产数量 */ private BigDecimal quantityProduced; /** 总进度百分比 */ private BigDecimal progressPercent; /** 工序进度列表 */ private List processList; @Data public static class ProcessProgressVO { /** 工序ID */ private Long processId; /** 工序编码 */ private String processCode; /** 工序名称 */ private String processName; /** 是否关键工序 */ private Boolean keyFlag; /** 计划数量(关键工序) */ private BigDecimal plannedQuantity; /** 已生产数量 */ private BigDecimal producedQuantity; /** 进度百分比 */ private BigDecimal progressPercent; /** 任务列表 */ private List taskList; } @Data public static class TaskProgressVO { /** 任务ID */ private Long taskId; /** 任务编码 */ private String taskCode; /** 工作站名称 */ private String workstationName; /** 排产数量 */ private BigDecimal quantity; /** 已生产数量 */ private BigDecimal producedQuantity; /** 进度百分比 */ private BigDecimal progressPercent; /** 任务状态 */ private Integer status; } } ``` #### 一键排产请求 VO ```java @Data public class MesProTaskAutoGenerateReqVO { /** 工单ID */ @NotNull(message = "工单ID不能为空") private Long workOrderId; /** 工艺路线ID(可选,不传则使用工单关联的工艺路线) */ private Long routeId; /** 是否自动分配工作站 */ private Boolean autoAssignWorkstation = true; /** 是否自动计算生产时间 */ private Boolean autoCalculateTime = true; } ``` #### 一键排产预览 VO ```java @Data public class MesProTaskAutoGeneratePreviewRespVO { /** 工单ID */ private Long workOrderId; /** 工艺路线ID */ private Long routeId; /** 工艺路线名称 */ private String routeName; /** 预生成任务列表 */ private List taskList; @Data public static class TaskPreviewVO { /** 工序ID */ private Long processId; /** 工序名称 */ private String processName; /** 工序顺序 */ private Integer sort; /** 建议工作站ID */ private Long suggestWorkstationId; /** 建议工作站名称 */ private String suggestWorkstationName; /** 排产数量 */ private BigDecimal quantity; /** 预计开始时间 */ private LocalDateTime suggestStartTime; /** 预计结束时间 */ private LocalDateTime suggestEndTime; /** 是否关键工序 */ private Boolean keyFlag; } } ``` #### 批量报工请求 VO ```java @Data public class MesProFeedbackBatchCreateReqVO { /** 任务ID列表 */ @NotEmpty(message = "任务ID列表不能为空") private List taskIds; /** 本次报工数量(应用到所有任务) */ @NotNull(message = "报工数量不能为空") @DecimalMin(value = "0.01", message = "报工数量必须大于0") private BigDecimal feedbackQuantity; /** 合格品数量(应用到所有任务,非质检工序必填) */ private BigDecimal qualifiedQuantity; /** 不良品数量 */ private BigDecimal unqualifiedQuantity; /** 备注 */ private String remark; } ``` --- ## 四、数据库变更 ### 4.1 表结构变更 ```sql -- ============================================= -- 生产任务表新增字段 -- ============================================= ALTER TABLE mes_pro_task ADD COLUMN first_feedback_time datetime COMMENT '首次报工时间' AFTER cancel_date, ADD COLUMN last_feedback_time datetime COMMENT '最近报工时间' AFTER first_feedback_time; -- ============================================= -- 生产工单表新增字段 -- ============================================= ALTER TABLE mes_pro_work_order ADD COLUMN route_id bigint COMMENT '工艺路线编号' AFTER batch_code, ADD COLUMN auto_finish_flag tinyint(1) DEFAULT 1 COMMENT '是否自动完成(报工达标后自动完成工单)' AFTER route_id; -- ============================================= -- 补充索引 -- ============================================= ALTER TABLE mes_pro_task ADD INDEX idx_work_order_process (work_order_id, process_id); ``` ### 4.2 枚举值变更 ```sql -- 任务状态枚举扩展 -- 0=草稿, 1=生产中, 2=部分完成, 3=已完成, 4=已取消 -- 注意:需要更新字典数据 mes_pro_task_status INSERT INTO system_dict_data (sort, label, value, dict_type, status, color_type, css_class, remark) VALUES (1, '生产中', '1', 'mes_pro_task_status', 0, 'processing', '', '任务正在生产中'), (2, '部分完成', '2', 'mes_pro_task_status', 0, 'warning', '', '任务部分完成'); -- 更新原有枚举的 sort UPDATE system_dict_data SET sort = 0 WHERE dict_type = 'mes_pro_task_status' AND value = '0'; UPDATE system_dict_data SET sort = 3 WHERE dict_type = 'mes_pro_task_status' AND value = '3'; UPDATE system_dict_data SET sort = 4 WHERE dict_type = 'mes_pro_task_status' AND value = '4'; ``` --- ## 五、实施计划 ### 5.1 阶段一:后端核心逻辑(预计 3 天) | 任务 | 说明 | 优先级 | |------|------|--------| | 任务状态枚举扩展 | 新增 PRODUCING、PARTIAL_FINISHED 状态 | P0 | | 数据库变更执行 | 执行 SQL 脚本 | P0 | | 排产数量校验 | 创建/删除任务时校验和更新工单已排产数量 | P0 | | 任务状态自动流转 | 报工审批后自动更新任务状态 | P0 | | 工单进度自动更新 | 报工审批后更新工单已生产数量 | P0 | | 工单自动完成检查 | 报工达标后自动完成工单 | P1 | ### 5.2 阶段二:新接口开发(预计 2 天) | 任务 | 说明 | 优先级 | |------|------|--------| | 一键排产接口 | 根据工艺路线自动生成任务 | P0 | | 工单进度接口 | 返回工单各工序进度详情 | P0 | | 批量报工接口 | 批量创建报工单 | P1 | ### 5.3 阶段三:前端改造(预计 4 天) | 任务 | 说明 | 优先级 | |------|------|--------| | 工单中心页面 | 整合工单、排产、报工到一个页面 | P0 | | 工序进度条组件 | 可视化展示各工序进度 | P0 | | 快速报工入口 | 任务行直接报工 | P0 | | 一键排产弹窗 | 选择工艺路线后预览并确认排产 | P1 | | 批量报工功能 | 选中多任务批量报工 | P2 | ### 5.4 阶段四:测试与优化(预计 1 天) - 单元测试覆盖新增逻辑 - 集成测试验证完整流程 - 性能测试(一键排产、批量报工) --- ## 六、风险与注意事项 ### 6.1 兼容性 - **老数据处理**:历史任务的 `status` 需要按新枚举值迁移 - **API 兼容**:新接口不影响现有接口,前端可逐步迁移 ### 6.2 性能考虑 - 一键排产可能生成大量任务,建议限制单次排产数量(如最多 10 个工序) - 批量报工建议限制批量数量(如最多 50 个任务) ### 6.3 业务规则确认 - 工单自动完成是否默认开启?(建议默认开启) - 非关键工序报工是否更新工单进度?(建议不更新,工单进度只看关键工序) - 任务部分完成状态是否需要明确区分?(建议简化为「生产中」包含部分完成) --- ## 七、前端联调方案 详见:`docs/work_order_center_integration.md`(待创建)