feat(mes): 优化生产订单与排产任务列表显示,新增启停工及校验逻辑
1. 生产订单列表展示优化 (WorkOrder)
- 新增「生产状态」列,全局注册 `ORDER_PRODUCTION_STATUS` 常量并绑定系统动态字典。
- 彻底重构「工序生产进度」单元格显示:弃用臃肿的 Steps 组件,改为极简横向布局(彩色状态圆点 + 进度百分比),解决表格行高被强行撑开及内容截断问题,支持横向滚动。
- 优化「完成进度」列宽及展示样式,将百分比数值外置同行显示,更加直观。
2. 排产任务列表改造 (Task)
- 顶部表头重构,由单一标题重构为「全部 / 未完成 / 已完成」的 Tabs 标签页切换结构。
- 重写数据过滤逻辑,剥离写死的 status 查询,改为使用 `scheduleStatus` 字段动态响应 Tabs 切换 (0: 未完成, 1: 已完成)。
3. 工单业务逻辑增强与状态流转
- 落实「工单查询逻辑设计方案」,优化底层的工单条件检索逻辑。
- 实现「添加工单启停功能」与「停工状态」,支持工单在执行过程中的暂停与恢复业务流转。
- 强化容错与防呆设计:操作「完成订单」时增加前置校验 `handleCheckFinish`,若生产状态为未完成,强制弹出二次确认 Modal,避免误操作。
# 相关讨论/参考方案
# - 添加工单启停功能
# - 停工状态功能设计方案
# - 工单查询逻辑设计方案
# - 工单完成进度显示优化
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | CREATE TABLE `mes_pro_task_pause_record` ( |
| | | `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼å·', |
| | | `task_id` bigint(20) NOT NULL COMMENT 'ä»»å¡ç¼å·', |
| | | `work_order_id` bigint(20) DEFAULT NULL COMMENT 'å·¥åç¼å·', |
| | | `workstation_id` bigint(20) DEFAULT NULL COMMENT 'å·¥ä½ç«ç¼å·', |
| | | `reason` varchar(255) NOT NULL COMMENT 'åå·¥åå ', |
| | | `pause_time` datetime NOT NULL COMMENT 'åå·¥æ¶é´', |
| | | `pause_user_id` bigint(20) NOT NULL COMMENT 'åå·¥æä½äººID', |
| | | `resume_time` datetime DEFAULT NULL COMMENT 'å¤å·¥æ¶é´', |
| | | `resume_user_id` bigint(20) DEFAULT NULL COMMENT 'å¤å·¥æä½äººID', |
| | | `remark` varchar(500) DEFAULT NULL COMMENT '夿³¨', |
| | | `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT 'å建è
', |
| | | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'å建æ¶é´', |
| | | `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT 'æ´æ°è
', |
| | | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ´æ°æ¶é´', |
| | | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å é¤', |
| | | `tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT 'ç§æ·ç¼å·', |
| | | PRIMARY KEY (`id`) USING BTREE, |
| | | KEY `idx_task_id` (`task_id`) USING BTREE |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='ç产任å¡å工记å½è¡¨'; |
| | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.GanttDataRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskAutoGeneratePreviewRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskAutoGenerateReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPauseReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskResumeReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskSaveReqVO; |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.service.pro.feedback.MesProFeedbackService; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskPauseRecordDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskPauseRecordMapper; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPauseRecordRespVO; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| | | |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | |
| | | private MesProRouteProcessService routeProcessService; |
| | | @Resource |
| | | private MesProFeedbackService feedbackService; |
| | | @Resource |
| | | private MesProTaskPauseRecordMapper pauseRecordMapper; |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建ç产任å¡") |
| | |
| | | @PreAuthorize("@ss.hasPermission('mes:pro-task:delete')") |
| | | public CommonResult<Boolean> deleteTask(@RequestParam("id") Long id) { |
| | | taskService.deleteTask(id); |
| | | return success(true); |
| | | } |
| | | |
| | | @PutMapping("/pause") |
| | | @Operation(summary = "ç产任å¡åå·¥") |
| | | @PreAuthorize("@ss.hasPermission('mes:pro-task:update')") |
| | | public CommonResult<Boolean> pauseTask(@Valid @RequestBody MesProTaskPauseReqVO pauseReqVO) { |
| | | taskService.pauseTask(pauseReqVO); |
| | | return success(true); |
| | | } |
| | | |
| | | @PutMapping("/resume") |
| | | @Operation(summary = "ç产任å¡å¤å·¥") |
| | | @PreAuthorize("@ss.hasPermission('mes:pro-task:update')") |
| | | public CommonResult<Boolean> resumeTask(@Valid @RequestBody MesProTaskResumeReqVO resumeReqVO) { |
| | | taskService.resumeTask(resumeReqVO); |
| | | return success(true); |
| | | } |
| | | |
| | |
| | | @PreAuthorize("@ss.hasPermission('mes:pro-task:query')") |
| | | public CommonResult<PageResult<MesProTaskRespVO>> getPendingFeedbackPage(@Valid MesProTaskPageReqVO pageReqVO) { |
| | | PageResult<MesProTaskDO> pageResult = taskService.getPendingFeedbackPage(pageReqVO); |
| | | return success(new PageResult<>(buildTaskRespVOList(pageResult.getList()), pageResult.getTotal())); |
| | | List<MesProTaskRespVO> respVOList = buildTaskRespVOList(pageResult.getList()); |
| | | |
| | | if (CollUtil.isNotEmpty(respVOList)) { |
| | | Set<Long> taskIds = convertSet(respVOList, MesProTaskRespVO::getId); |
| | | List<MesProTaskPauseRecordDO> records = pauseRecordMapper.selectListByTaskIds(taskIds); |
| | | if (CollUtil.isNotEmpty(records)) { |
| | | Set<Long> userIds = new HashSet<>(); |
| | | records.forEach(r -> { |
| | | if (r.getPauseUserId() != null) userIds.add(r.getPauseUserId()); |
| | | if (r.getResumeUserId() != null) userIds.add(r.getResumeUserId()); |
| | | }); |
| | | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds); |
| | | |
| | | Map<Long, List<MesProTaskPauseRecordRespVO>> pauseRecordMap = new HashMap<>(); |
| | | for (MesProTaskPauseRecordDO record : records) { |
| | | MesProTaskPauseRecordRespVO vo = BeanUtils.toBean(record, MesProTaskPauseRecordRespVO.class); |
| | | if (record.getPauseUserId() != null && userMap.containsKey(record.getPauseUserId())) { |
| | | vo.setPauseUserName(userMap.get(record.getPauseUserId()).getNickname()); |
| | | } |
| | | if (record.getResumeUserId() != null && userMap.containsKey(record.getResumeUserId())) { |
| | | vo.setResumeUserName(userMap.get(record.getResumeUserId()).getNickname()); |
| | | } |
| | | pauseRecordMap.computeIfAbsent(record.getTaskId(), k -> new ArrayList<>()).add(vo); |
| | | } |
| | | respVOList.forEach(vo -> vo.setPauseRecords(pauseRecordMap.get(vo.getId()))); |
| | | } |
| | | } |
| | | |
| | | return success(new PageResult<>(respVOList, pageResult.getTotal())); |
| | | } |
| | | |
| | | @GetMapping("/gantt-list") |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Schema(description = "管çåå° - MES ç产任å¡åå·¥åå²è®°å½ Response VO") |
| | | @Data |
| | | public class MesProTaskPauseRecordRespVO { |
| | | |
| | | @Schema(description = "ç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "ç产任å¡ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
| | | private Long taskId; |
| | | |
| | | @Schema(description = "åå·¥æä½äººID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Long pauseUserId; |
| | | |
| | | @Schema(description = "åå·¥æä½äººå§å", requiredMode = Schema.RequiredMode.REQUIRED, example = "å¼ ä¸") |
| | | private String pauseUserName; |
| | | |
| | | @Schema(description = "åå·¥æ¶é´", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private LocalDateTime pauseTime; |
| | | |
| | | @Schema(description = "åå·¥åå ", example = "è®¾å¤æ
é") |
| | | private String reason; |
| | | |
| | | @Schema(description = "夿³¨", example = "ç´§æ¥åå·¥") |
| | | private String remark; |
| | | |
| | | @Schema(description = "å¤å·¥äººID", example = "2") |
| | | private Long resumeUserId; |
| | | |
| | | @Schema(description = "å¤å·¥äººå§å", example = "æå") |
| | | private String resumeUserName; |
| | | |
| | | @Schema(description = "å¤å·¥æ¶é´") |
| | | private LocalDateTime resumeTime; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotEmpty; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.Data; |
| | | |
| | | |
| | | |
| | | @Schema(description = "管çåå° - ç产任å¡åå·¥ Request VO") |
| | | @Data |
| | | public class MesProTaskPauseReqVO { |
| | | |
| | | @Schema(description = "ä»»å¡ç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
| | | @NotNull(message = "ä»»å¡ç¼å·ä¸è½ä¸ºç©º") |
| | | private Long id; |
| | | |
| | | @Schema(description = "åå·¥åå ", requiredMode = Schema.RequiredMode.REQUIRED, example = "è®¾å¤æ
é") |
| | | @NotEmpty(message = "åå·¥åå ä¸è½ä¸ºç©º") |
| | | private String reason; |
| | | |
| | | @Schema(description = "夿³¨", example = "ç»´ä¿®ä¸") |
| | | private String remark; |
| | | |
| | | } |
| | |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | @Schema(description = "管çåå° - MES çäº§ä»»å¡ Response VO") |
| | | @Data |
| | |
| | | @Schema(description = "å建æ¶é´", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private LocalDateTime createTime; |
| | | |
| | | @Schema(description = "åå·¥åå²è®°å½") |
| | | private List<MesProTaskPauseRecordRespVO> pauseRecords; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.Data; |
| | | |
| | | |
| | | |
| | | @Schema(description = "管çåå° - ç产任å¡å¤å·¥ Request VO") |
| | | @Data |
| | | public class MesProTaskResumeReqVO { |
| | | |
| | | @Schema(description = "ä»»å¡ç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
| | | @NotNull(message = "ä»»å¡ç¼å·ä¸è½ä¸ºç©º") |
| | | private Long id; |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderProgressRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderSaveReqVO; |
| | | import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi; |
| | | import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.*; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderProductionStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| | | import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.util.*; |
| | | |
| | | import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| | | |
| | | @Tag(name = "管çåå° - MES ç产工å") |
| | |
| | | private SrmSupplierApi srmSupplierApi; |
| | | @Resource |
| | | private MesMdUnitMeasureService unitMeasureService; |
| | | @Resource |
| | | private MesProWorkOrderProcessService workOrderProcessService; |
| | | @Resource |
| | | private MesProTaskService taskService; |
| | | @Resource |
| | | private MesProProcessService processService; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建ç产工å") |
| | |
| | | @PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')") |
| | | public CommonResult<PageResult<MesProWorkOrderRespVO>> getWorkOrderPage(@Valid MesProWorkOrderPageReqVO pageReqVO) { |
| | | PageResult<MesProWorkOrderDO> pageResult = workOrderService.getWorkOrderPage(pageReqVO); |
| | | return success(new PageResult<>(buildWorkOrderRespVOList(pageResult.getList()), pageResult.getTotal())); |
| | | } |
| | | |
| | | @GetMapping("/schedule-page") |
| | | @Operation(summary = "è·å¾ç产工åè°åº¦ç¶æå页") |
| | | @PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')") |
| | | public CommonResult<PageResult<MesProWorkOrderRespVO>> getWorkOrderSchedulePage(@Valid MesProWorkOrderPageReqVO pageReqVO) { |
| | | PageResult<MesProWorkOrderDO> pageResult = workOrderService.getWorkOrderSchedulePage(pageReqVO); |
| | | return success(new PageResult<>(buildWorkOrderRespVOList(pageResult.getList()), pageResult.getTotal())); |
| | | } |
| | | |
| | |
| | | return Collections.emptyList(); |
| | | } |
| | | // 1. æ¹éè·åå
³èæ°æ® |
| | | Set<Long> workOrderIds = convertSet(list, MesProWorkOrderDO::getId); |
| | | Map<Long, MesMdItemDO> itemMap = itemService.getItemMap( |
| | | convertSet(list, MesProWorkOrderDO::getProductId)); |
| | | Map<Long, CrmCustomerRespDTO> customerMap = customerApi.getCustomerMap( |
| | |
| | | convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId)); |
| | | Map<Long, MesProWorkOrderDO> parentMap = workOrderService.getWorkOrderMap( |
| | | convertSet(list, MesProWorkOrderDO::getParentId)); |
| | | |
| | | // è·åææå
³èçå·¥åå·¥åºåä»»å¡ |
| | | List<MesProWorkOrderProcessDO> allProcesses = workOrderProcessService.getWorkOrderProcessListByWorkOrderIds(workOrderIds); |
| | | List<MesProTaskDO> allTasks = taskService.getTaskListByWorkOrderIds(workOrderIds); |
| | | |
| | | Set<Long> processIds = convertSet(allProcesses, MesProWorkOrderProcessDO::getProcessId); |
| | | Map<Long, MesProProcessDO> processMap = processService.getProcessMap(processIds); |
| | | |
| | | Map<Long, List<MesProWorkOrderProcessDO>> woProcessMap = convertMultiMap(allProcesses, MesProWorkOrderProcessDO::getWorkOrderId); |
| | | Map<Long, List<MesProTaskDO>> woTaskMap = convertMultiMap(allTasks, MesProTaskDO::getWorkOrderId); |
| | | |
| | | // 2. æ¼æ¥ VOï¼åä½åç§°ä»äº§åå
³èç item è·å¾ï¼ |
| | | return BeanUtils.toBean(list, MesProWorkOrderRespVO.class, vo -> { |
| | | MapUtils.findAndThen(itemMap, vo.getProductId(), item -> { |
| | |
| | | supplier -> vo.setVendorName(supplier.getName())); |
| | | MapUtils.findAndThen(parentMap, vo.getParentId(), |
| | | parent -> vo.setParentCode(parent.getCode())); |
| | | |
| | | // 计ç®å®å·¥è¿åº¦ (quantityProduced / quantity * 100) |
| | | BigDecimal produced = vo.getQuantityProduced() != null ? vo.getQuantityProduced() : BigDecimal.ZERO; |
| | | if (vo.getQuantity() != null && vo.getQuantity().compareTo(BigDecimal.ZERO) > 0) { |
| | | vo.setCompletionProgress(produced.multiply(new BigDecimal("100")).divide(vo.getQuantity(), 2, java.math.RoundingMode.HALF_UP)); |
| | | } else { |
| | | vo.setCompletionProgress(produced.compareTo(BigDecimal.ZERO) > 0 ? new BigDecimal("100.00") : BigDecimal.ZERO); |
| | | } |
| | | |
| | | // 计ç®è¿åº¦åç¶æ |
| | | List<MesProWorkOrderProcessDO> processes = woProcessMap.getOrDefault(vo.getId(), Collections.emptyList()); |
| | | List<MesProTaskDO> tasks = woTaskMap.getOrDefault(vo.getId(), Collections.emptyList()); |
| | | |
| | | List<MesProWorkOrderProcessProgressVO> progressList = new ArrayList<>(); |
| | | boolean allCompleted = !processes.isEmpty(); |
| | | boolean anyStarted = false; |
| | | |
| | | Map<Long, List<MesProTaskDO>> processTaskMap = convertMultiMap(tasks, MesProTaskDO::getProcessId); |
| | | |
| | | for (MesProWorkOrderProcessDO process : processes) { |
| | | List<MesProTaskDO> pTasks = processTaskMap.getOrDefault(process.getProcessId(), Collections.emptyList()); |
| | | |
| | | BigDecimal totalQty = pTasks.stream().map(t -> t.getQuantity() != null ? t.getQuantity() : BigDecimal.ZERO).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal totalProduced = pTasks.stream().map(t -> t.getProducedQuantity() != null ? t.getProducedQuantity() : BigDecimal.ZERO).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | |
| | | MesProWorkOrderProcessProgressVO progressVO = new MesProWorkOrderProcessProgressVO(); |
| | | MesProProcessDO processDO = processMap.get(process.getProcessId()); |
| | | progressVO.setProcessName(processDO != null ? processDO.getName() : ""); |
| | | |
| | | if (totalQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | progressVO.setProgress(totalProduced.multiply(new BigDecimal("100")).divide(totalQty, 2, java.math.RoundingMode.HALF_UP)); |
| | | } else { |
| | | progressVO.setProgress(totalProduced.compareTo(BigDecimal.ZERO) > 0 ? new BigDecimal("100.00") : BigDecimal.ZERO); |
| | | } |
| | | progressList.add(progressVO); |
| | | |
| | | if (totalProduced.compareTo(BigDecimal.ZERO) > 0) { |
| | | anyStarted = true; |
| | | } |
| | | if (totalQty.compareTo(BigDecimal.ZERO) == 0 || totalProduced.compareTo(totalQty) < 0) { |
| | | allCompleted = false; |
| | | } |
| | | } |
| | | |
| | | vo.setProcessProgressList(progressList); |
| | | if (processes.isEmpty()) { |
| | | vo.setProductionStatus(MesProWorkOrderProductionStatusEnum.WAITING.getStatus()); // å¾
ç产 |
| | | } else if (allCompleted) { |
| | | vo.setProductionStatus(MesProWorkOrderProductionStatusEnum.FINISHED.getStatus()); // çäº§å®æ |
| | | } else if (anyStarted) { |
| | | vo.setProductionStatus(MesProWorkOrderProductionStatusEnum.PRODUCING.getStatus()); // çäº§ä¸ |
| | | } else { |
| | | vo.setProductionStatus(MesProWorkOrderProductionStatusEnum.WAITING.getStatus()); // å¾
ç产 |
| | | } |
| | | }); |
| | | } |
| | | |
| | |
| | | @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) |
| | | private LocalDateTime[] requestDate; |
| | | |
| | | @Schema(description = "è°åº¦ç¶æï¼ç©º=ææï¼0=æªå®æï¼1=已宿ï¼", example = "1") |
| | | private Integer scheduleStatus; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Schema(description = "管çåå° - MES å·¥åå·¥åºè¿åº¦ VO") |
| | | @Data |
| | | public class MesProWorkOrderProcessProgressVO { |
| | | |
| | | @Schema(description = "å·¥åºåç§°", example = "æç£¨å·¥åº") |
| | | private String processName; |
| | | |
| | | @Schema(description = "å·¥åºè¿åº¦(%)", example = "50.00") |
| | | private BigDecimal progress; |
| | | } |
| | |
| | | @ExcelProperty("åä½") |
| | | private String unitMeasureName; |
| | | |
| | | @Schema(description = "ç产æ°é", example = "100.00") |
| | | @ExcelProperty("ç产æ°é") |
| | | @Schema(description = "è®¢åæ°é", example = "100.00") |
| | | @ExcelProperty("è®¢åæ°é") |
| | | private BigDecimal quantity; |
| | | |
| | | @Schema(description = "å·²ç产æ°é", example = "50.00") |
| | | @ExcelProperty("å·²ç产æ°é") |
| | | private BigDecimal quantityProduced; |
| | | |
| | | @Schema(description = "å®å·¥è¿åº¦", example = "50.00") |
| | | @ExcelProperty("å®å·¥è¿åº¦") |
| | | private BigDecimal completionProgress; |
| | | |
| | | @Schema(description = "è°æ´æ°é", example = "0") |
| | | @ExcelProperty("è°æ´æ°é") |
| | |
| | | @ExcelProperty("å建æ¶é´") |
| | | private LocalDateTime createTime; |
| | | |
| | | @Schema(description = "å·¥åºè¿åº¦å表") |
| | | private java.util.List<MesProWorkOrderProcessProgressVO> processProgressList; |
| | | |
| | | @Schema(description = "çäº§ç¶æ (0: å¾
ç产, 1: ç产ä¸, 2: çäº§å®æ)") |
| | | @ExcelProperty("çäº§ç¶æ") |
| | | private Integer productionStatus; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.dataobject.pro.task; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * MES ç产任å¡åå·¥è®°å½ DO |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @TableName("mes_pro_task_pause_record") |
| | | @KeySequence("mes_pro_task_pause_record_seq") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class MesProTaskPauseRecordDO extends BaseDO { |
| | | |
| | | /** |
| | | * ç¼å· |
| | | */ |
| | | @TableId |
| | | private Long id; |
| | | /** |
| | | * ä»»å¡ç¼å· |
| | | */ |
| | | private Long taskId; |
| | | /** |
| | | * å·¥åç¼å· |
| | | */ |
| | | private Long workOrderId; |
| | | /** |
| | | * å·¥ä½ç«ç¼å· |
| | | */ |
| | | private Long workstationId; |
| | | /** |
| | | * åå·¥åå |
| | | */ |
| | | private String reason; |
| | | /** |
| | | * åå·¥æ¶é´ |
| | | */ |
| | | private LocalDateTime pauseTime; |
| | | /** |
| | | * åå·¥æä½äººID |
| | | */ |
| | | private Long pauseUserId; |
| | | /** |
| | | * å¤å·¥æ¶é´ |
| | | */ |
| | | private LocalDateTime resumeTime; |
| | | /** |
| | | * å¤å·¥æä½äººID |
| | | */ |
| | | private Long resumeUserId; |
| | | /** |
| | | * 夿³¨ |
| | | */ |
| | | private String remark; |
| | | |
| | | } |
| | |
| | | MPJLambdaWrapperX<MesProTaskDO> query = new MPJLambdaWrapperX<>(); |
| | | query.selectAll(MesProTaskDO.class); |
| | | // å¾
æ¥å·¥å°è´¦ï¼è¿è¡ä¸çä»»å¡ï¼æé¤å·²å®æ/已忶ï¼ï¼ä¸ä»æå¾
æ¥å·¥é |
| | | query.eq(MesProTaskDO::getStatus, MesProTaskStatusEnum.PREPARE.getStatus()); |
| | | query.in(MesProTaskDO::getStatus, MesProTaskStatusEnum.PREPARE.getStatus(),MesProTaskStatusEnum.PAUSED.getStatus()); |
| | | query.apply("(quantity - IFNULL(produced_quantity, 0)) > 0"); |
| | | query.likeIfPresent(MesProTaskDO::getCode, reqVO.getCode()) |
| | | .likeIfPresent(MesProTaskDO::getName, reqVO.getName()) |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.pro.task; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskPauseRecordDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | |
| | | @Mapper |
| | | public interface MesProTaskPauseRecordMapper extends BaseMapperX<MesProTaskPauseRecordDO> { |
| | | |
| | | default MesProTaskPauseRecordDO selectLastUnresumedByTaskId(Long taskId) { |
| | | return selectOne(new LambdaQueryWrapperX<MesProTaskPauseRecordDO>() |
| | | .eq(MesProTaskPauseRecordDO::getTaskId, taskId) |
| | | .isNull(MesProTaskPauseRecordDO::getResumeTime) |
| | | .orderByDesc(MesProTaskPauseRecordDO::getId) |
| | | .last("LIMIT 1")); |
| | | } |
| | | |
| | | default List<MesProTaskPauseRecordDO> selectListByTaskIds(Collection<Long> taskIds) { |
| | | return selectList(new LambdaQueryWrapperX<MesProTaskPauseRecordDO>() |
| | | .inIfPresent(MesProTaskPauseRecordDO::getTaskId, taskIds) |
| | | .orderByDesc(MesProTaskPauseRecordDO::getId)); |
| | | } |
| | | |
| | | } |
| | |
| | | return selectPage(reqVO, wrapper); |
| | | } |
| | | |
| | | /** |
| | | * å页æ¥è¯¢ï¼å¸¦å®¢æ·æéè¿æ»¤ä¸æ¯æè°åº¦ç¶ææ¥è¯¢ï¼ |
| | | */ |
| | | default PageResult<MesProWorkOrderDO> selectSchedulePage(MesProWorkOrderPageReqVO pageReqVO, List<Long> permittedClientIds) { |
| | | com.baomidou.mybatisplus.core.metadata.IPage<MesProWorkOrderDO> mpPage = cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils.buildPage(pageReqVO); |
| | | com.baomidou.mybatisplus.core.metadata.IPage<MesProWorkOrderDO> result = selectSchedulePage(mpPage, pageReqVO, permittedClientIds); |
| | | return new PageResult<>(result.getRecords(), result.getTotal()); |
| | | } |
| | | |
| | | com.baomidou.mybatisplus.core.metadata.IPage<MesProWorkOrderDO> selectSchedulePage( |
| | | com.baomidou.mybatisplus.core.metadata.IPage<MesProWorkOrderDO> page, |
| | | @org.apache.ibatis.annotations.Param("pageReqVO") MesProWorkOrderPageReqVO pageReqVO, |
| | | @org.apache.ibatis.annotations.Param("permittedClientIds") List<Long> permittedClientIds); |
| | | |
| | | default MesProWorkOrderDO selectByCode(String code) { |
| | | return selectOne(MesProWorkOrderDO::getCode, code); |
| | | } |
| | |
| | | .orderByAsc(MesProWorkOrderProcessDO::getSort)); |
| | | } |
| | | |
| | | default MesProWorkOrderProcessDO selectLastByWorkOrderId(Long workOrderId) { |
| | | return selectOne(new LambdaQueryWrapperX<MesProWorkOrderProcessDO>() |
| | | .eq(MesProWorkOrderProcessDO::getWorkOrderId, workOrderId) |
| | | .orderByDesc(MesProWorkOrderProcessDO::getSort) |
| | | .last("LIMIT 1")); |
| | | } |
| | | |
| | | default void deleteByWorkOrderId(Long workOrderId) { |
| | | delete(new LambdaQueryWrapperX<MesProWorkOrderProcessDO>() |
| | | .eq(MesProWorkOrderProcessDO::getWorkOrderId, workOrderId)); |
| | | } |
| | | |
| | | default List<MesProWorkOrderProcessDO> selectListByWorkOrderIds(java.util.Collection<Long> workOrderIds) { |
| | | return selectList(new LambdaQueryWrapperX<MesProWorkOrderProcessDO>() |
| | | .in(MesProWorkOrderProcessDO::getWorkOrderId, workOrderIds) |
| | | .orderByAsc(MesProWorkOrderProcessDO::getWorkOrderId, MesProWorkOrderProcessDO::getSort)); |
| | | } |
| | | |
| | | } |
| | |
| | | ErrorCode PRO_TASK_ROUTE_PROCESS_MISMATCH = new ErrorCode(1_040_503_004, "ç产任å¡ä¸å½åå·¥èºè·¯çº¿æå·¥åºä¸ä¸è´"); |
| | | ErrorCode PRO_TASK_ITEM_MISMATCH = new ErrorCode(1_040_503_005, "æ£éªç©æä¸ç产任å¡ç©æä¸ä¸è´"); |
| | | ErrorCode PRO_TASK_HAS_FEEDBACK = new ErrorCode(1_040_503_006, "该任å¡ä¸å卿¥å·¥è®°å½ï¼æ æ³å é¤ï¼é¿å
æ¥å·¥è¿½æº¯æé¾ï¼"); |
| | | ErrorCode PRO_WORKSTATION_PROCESS_MISMATCH = new ErrorCode(1_040_503_006, "å·¥ä½ç«æå±å·¥åºä¸å½åå·¥åºä¸ä¸è´"); |
| | | ErrorCode PRO_WORKSTATION_PROCESS_MISMATCH = new ErrorCode(1_040_503_009, "å·¥ä½ç«æå±å·¥åºä¸å½åå·¥åºä¸ä¸è´"); // ä¿®æ£ç¼å·å²çª |
| | | ErrorCode PRO_TASK_CANNOT_PAUSE = new ErrorCode(1_040_503_007, "åªæãç产ä¸ãæãé¨å宿ãçä»»å¡å¯ä»¥åå·¥"); |
| | | ErrorCode PRO_TASK_NOT_PAUSED = new ErrorCode(1_040_503_008, "ä»»å¡ä¸å¤äºãå·²åå·¥ãç¶æï¼æ æ³å¤å·¥"); |
| | | // ========== MES ç产管ç-çäº§ä»»å¡ææï¼1-040-503-100ï¼ ========== |
| | | ErrorCode PRO_TASK_ISSUE_NOT_EXISTS = new ErrorCode(1_040_503_100, "çäº§ä»»å¡ææè®°å½ä¸åå¨"); |
| | | |
| | |
| | | ErrorCode PRO_FEEDBACK_QUALIFIED_UNQUALIFIED_REQUIRED = new ErrorCode(1_040_506_011, "请è¾å
¥åæ ¼ååä¸è¯åæ°éï¼ä¸åè®¡é¡»å¤§äº 0"); |
| | | ErrorCode PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS = new ErrorCode(1_040_506_012, "å½åæ¥å·¥åæªå®ææ£éªï¼å¾
æ£æ°éï¼{}ï¼ï¼æ æ³æ§è¡æ¥å·¥"); |
| | | ErrorCode PRO_FEEDBACK_BACKFLUSH_BOM_REQUIRED = new ErrorCode(1_040_506_013, "å½åå·¥åºä¸ºåå²å·¥åºï¼ä½æªé
ç½®BOMæ¶èï¼æ æ³æ¥å·¥"); |
| | | ErrorCode PRO_FEEDBACK_CANNOT_CREATE_WHEN_PAUSED = new ErrorCode(1_040_506_014, "ç产任å¡å¤äºåå·¥ç¶æï¼æ æ³æ¥å·¥"); |
| | | |
| | | // ========== MES ç产管ç-ç产æµè½¬å¡ï¼1-040-507-000ï¼ ========== |
| | | ErrorCode PRO_CARD_NOT_EXISTS = new ErrorCode(1_040_507_000, "ç产æµè½¬å¡ä¸åå¨"); |
| | |
| | | * |
| | | * å¯¹åº MesProTaskService#cancelTaskByOrderId æ¹æ³ |
| | | */ |
| | | CANCELED(MesOrderStatusConstants.CANCELLED, "已忶"); |
| | | CANCELED(MesOrderStatusConstants.CANCELLED, "已忶"), |
| | | /** |
| | | * å·²åå·¥ |
| | | */ |
| | | PAUSED(9, "å·²åå·¥"); |
| | | |
| | | public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesProTaskStatusEnum::getStatus).toArray(Integer[]::new); |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * 夿æ¯å¦å¯ä»¥æ¥å·¥ï¼éç»æï¼ |
| | | * 夿æ¯å¦å¯ä»¥æ¥å·¥ï¼éç»æä¸éåå·¥ï¼ |
| | | * |
| | | * @param status ç¶æå¼ |
| | | * @return æ¯å¦å¯ä»¥æ¥å·¥ |
| | | */ |
| | | public static boolean canFeedback(Integer status) { |
| | | return !isEndStatus(status); |
| | | return !isEndStatus(status) && !ObjectUtils.equalsAny(status, PAUSED.status); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.enums.pro; |
| | | |
| | | import cn.iocoder.yudao.framework.common.core.ArrayValuable; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * MES ç产工åçäº§ç¶ææä¸¾ |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Getter |
| | | @AllArgsConstructor |
| | | public enum MesProWorkOrderProductionStatusEnum implements ArrayValuable<Integer> { |
| | | |
| | | /** |
| | | * å¾
ç产 |
| | | */ |
| | | WAITING(0, "å¾
ç产"), |
| | | /** |
| | | * çäº§ä¸ |
| | | */ |
| | | PRODUCING(1, "ç产ä¸"), |
| | | /** |
| | | * çäº§å®æ |
| | | */ |
| | | FINISHED(2, "çäº§å®æ"); |
| | | |
| | | public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesProWorkOrderProductionStatusEnum::getStatus).toArray(Integer[]::new); |
| | | |
| | | /** |
| | | * ç¶æå¼ |
| | | */ |
| | | private final Integer status; |
| | | /** |
| | | * ç¶æå |
| | | */ |
| | | private final String name; |
| | | |
| | | @Override |
| | | public Integer[] array() { |
| | | return ARRAYS; |
| | | } |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProTaskStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.itemconsume.MesWmItemConsumeService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceLineService; |
| | |
| | | private MesProRouteProcessService routeProcessService; |
| | | @Resource |
| | | private MesProWorkOrderBomService workOrderBomService; |
| | | @Resource |
| | | private MesProWorkOrderProcessService workOrderProcessService; |
| | | @Resource |
| | | @Lazy // é¿å
循ç¯ä¾èµ |
| | | private MesProTaskService taskService; |
| | |
| | | .setUncheckQuantity(BigDecimal.ZERO)); |
| | | |
| | | // 6. æ ¹æ®æ¯å¦éè¦æ£éªï¼å³å®äº§åºåå
¥åºæ¹å¼ |
| | | // 6.2 è°æ´ åªè¦å®¡æ¹æåç´æ¥æ´æ° |
| | | // å
³é®å·¥åºï¼æ´æ°ä»»å¡ + å·¥åçå·²ç产æ°éï¼éå
³é®å·¥åºï¼åªæ´æ°ä»»å¡çå·²ç产æ°éï¼å·¥ååªçå
³é®å·¥åºäº§åºï¼ |
| | | if (keyFlag) { |
| | | updateTaskAndWorkOrderByFeedback(feedback); |
| | | } else { |
| | | taskService.updateProducedQuantity(feedback.getTaskId(), |
| | | feedback.getFeedbackQuantity(), |
| | | ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO), |
| | | ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO)); |
| | | checkAndPublishProductionFinished(feedback.getWorkOrderId()); |
| | | |
| | | } |
| | | if (checkFlag) { |
| | | // 6.1 éè¦æ£éªï¼çæäº§åºåï¼è´¨éç¶æ=å¾
æ£éªï¼ï¼æ´æ°æ¥å·¥ç¶æä¸ºå¾
æ£éªï¼çè´¨æ£å®æåè°ååå
¥åº |
| | | // 宿æ¶åè°è§ï¼MesQcIpqcServiceImpl#finishIpqc â splitPendingAndFinishProduce + updateProFeedbackWhenIpqcFinish |
| | |
| | | // 6.2 æ 鿣éªï¼çæäº§åºåï¼æåæ ¼/ä¸åæ ¼æè¡ï¼ï¼ç´æ¥å®æå
¥åº |
| | | MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false, reqVO); |
| | | productProduceService.finishProductProduce(produce.getId()); |
| | | // å
³é®å·¥åºï¼æ´æ°ä»»å¡ + å·¥åçå·²ç产æ°éï¼éå
³é®å·¥åºï¼åªæ´æ°ä»»å¡çå·²ç产æ°éï¼å·¥ååªçå
³é®å·¥åºäº§åºï¼ |
| | | if (keyFlag) { |
| | | updateTaskAndWorkOrderByFeedback(feedback); |
| | | } else { |
| | | taskService.updateProducedQuantity(feedback.getTaskId(), |
| | | feedback.getFeedbackQuantity(), |
| | | ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO), |
| | | ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO)); |
| | | checkAndPublishProductionFinished(feedback.getWorkOrderId()); |
| | | } |
| | | |
| | | } |
| | | |
| | | return true; // 已宿 |
| | |
| | | * <li>ä½¿ç¨æ¥å·¥æ°éï¼feedbackQuantityï¼ï¼ç´¯å ä»»å¡åå·¥åçå·²ç产æ°é</li> |
| | | * <li>使ç¨äº§åºåè¡æ qualityStatus èåçåæ ¼å/ä¸åæ ¼åæ°éï¼ç´¯å ä»»å¡çåæ ¼å/ä¸åæ ¼åæ°é |
| | | * ï¼ä¸ç´æ¥ç¨ feedback ä¸çæ°éï¼ç¡®ä¿è´¨æ£åè°åºæ¯ä¸æ°éæ¥æºæ£ç¡®ï¼</li> |
| | | * isPush æ¯å¦æ¨éçäº§å®æäºä»¶ |
| | | * </ul> |
| | | * |
| | | * @param feedback æ¥å·¥å |
| | |
| | | } |
| | | } |
| | | |
| | | // 2. æ´æ°ä»»å¡çå·²ç产/åæ ¼/ä¸åæ ¼æ°é |
| | | // 2. æ´æ°ä»»å¡çå·²ç产/åæ ¼/ä¸åæ ¼æ°é |
| | | taskService.updateProducedQuantity(feedback.getTaskId(), |
| | | feedback.getFeedbackQuantity(), qualifiedQty, unqualifiedQty); |
| | | // 3. æ´æ°å·¥åçå·²ç产æ°é |
| | | workOrderService.updateProducedQuantity(feedback.getWorkOrderId(), |
| | | feedback.getFeedbackQuantity()); |
| | | |
| | | // 4. æ´æ° MPS çå·²ç产æ°éï¼å¦ææ¯éå®è®¢åæ¥æºï¼ |
| | | MesProMpsRespDTO mps = mpsApi.getMpsByWorkOrderId(feedback.getWorkOrderId()); |
| | | if (mps != null) { |
| | | mpsApi.updateMpsProducedQuantity(mps.getId(), feedback.getFeedbackQuantity()); |
| | | // 3. æ´æ°è®¢åçå·²ç产æ°é |
| | | // æ°å¢é»è¾ï¼åªæå¨è¯¥ç¬è®¢åæåä¸ä¸ªå·¥åºææ´æ°ç产订åçå·²ç产æ°é |
| | | MesProWorkOrderProcessDO lastProcess = workOrderProcessService.getLastWorkOrderProcessByWorkOrderId(feedback.getWorkOrderId()); |
| | | if (lastProcess != null && ObjUtil.equal(lastProcess.getProcessId(), feedback.getProcessId())) { |
| | | workOrderService.updateProducedQuantity(feedback.getWorkOrderId(), |
| | | feedback.getFeedbackQuantity()); |
| | | // 4. æ´æ° MPS çå·²ç产æ°éï¼å¦ææ¯éå®è®¢åæ¥æºï¼ |
| | | MesProMpsRespDTO mps = mpsApi.getMpsByWorkOrderId(feedback.getWorkOrderId()); |
| | | if (mps != null) { |
| | | mpsApi.updateMpsProducedQuantity(mps.getId(), feedback.getFeedbackQuantity()); |
| | | } |
| | | } |
| | | |
| | | |
| | | // 5. æ£æ¥å¹¶åå¸çäº§å®æäºä»¶ |
| | | checkAndPublishProductionFinished(feedback.getWorkOrderId()); |
| | |
| | | // 1. æ ¡éªä»»å¡åå¨ä¸æªç»æ |
| | | MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId()); |
| | | |
| | | // æ ¡éªåå·¥ç¶æä¸è½æ¥å·¥ |
| | | if (!MesProTaskStatusEnum.canFeedback(task.getStatus())) { |
| | | throw exception(PRO_FEEDBACK_CANNOT_CREATE_WHEN_PAUSED); |
| | | } |
| | | |
| | | // 2. ä»ä»»å¡èªå¨å¡«å
å
³èåæ®µï¼å端æ éä¼ å
¥ï¼å端ç»ä¸ä»ä»»å¡è·åï¼ |
| | | reqVO.setWorkOrderId(task.getWorkOrderId()); |
| | | reqVO.setRouteId(task.getRouteId()); |
| | |
| | | public MesProFeedbackQuickCreateRespVO quickCreateFeedback(MesProFeedbackQuickCreateReqVO reqVO) { |
| | | // 1. æ ¡éªä»»å¡åå¨ä¸éç»æ |
| | | MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId()); |
| | | |
| | | // æ ¡éªåå·¥ç¶æä¸è½æ¥å·¥ |
| | | if (!MesProTaskStatusEnum.canFeedback(task.getStatus())) { |
| | | throw exception(PRO_FEEDBACK_CANNOT_CREATE_WHEN_PAUSED); |
| | | } |
| | | |
| | | // 2. æ ¡éªå·¥èºè·¯çº¿å·¥åºé
ç½® |
| | | MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId( |
| | | task.getRouteId(), task.getProcessId()); |
| | |
| | | |
| | | // 4. æ´æ°ä»»å¡/å·¥åçå·²ç产æ°é |
| | | feedback.setQualifiedQuantity(qualifiedQty).setUnqualifiedQuantity(unqualifiedQty); |
| | | updateTaskAndWorkOrderByFeedback(feedback); |
| | | //5.æ£æ¥å¹¶åå¸çäº§å®æäºä»¶ |
| | | checkAndPublishProductionFinished(feedback.getWorkOrderId()); |
| | | } |
| | | |
| | | @Override |
| | |
| | | List<Long> autoGenerateTasks( |
| | | cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskAutoGenerateReqVO reqVO); |
| | | |
| | | /** |
| | | * ç产任å¡åå·¥ |
| | | * |
| | | * @param pauseReqVO åå·¥è¯·æ± |
| | | */ |
| | | void pauseTask(cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPauseReqVO pauseReqVO); |
| | | |
| | | /** |
| | | * ç产任å¡å¤å·¥ |
| | | * |
| | | * @param resumeReqVO å¤å·¥è¯·æ± |
| | | */ |
| | | void resumeTask(cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskResumeReqVO resumeReqVO); |
| | | |
| | | } |
| | |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_ALREADY_FINISHED; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_CANNOT_PAUSE; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_HAS_FEEDBACK; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_NOT_EXISTS; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_NOT_PAUSED; |
| | | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskPauseRecordDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskPauseRecordMapper; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPauseReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskResumeReqVO; |
| | | |
| | | /** |
| | | * MES çäº§ä»»å¡ Service å®ç°ç±» |
| | |
| | | |
| | | @Resource |
| | | private MesProFeedbackMapper feedbackMapper; |
| | | |
| | | @Resource |
| | | private MesProTaskPauseRecordMapper taskPauseRecordMapper; |
| | | |
| | | @Resource |
| | | @Lazy |
| | |
| | | return taskIds; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void pauseTask(MesProTaskPauseReqVO pauseReqVO) { |
| | | // 1. æ ¡éªä»»å¡åå¨ |
| | | MesProTaskDO task = validateTaskExists(pauseReqVO.getId()); |
| | | // 2. æ ¡éªç¶ææ¯å¦å
许åå·¥ï¼å¦å¿
é¡»æ¯ çäº§ä¸ æ é¨åå®æï¼ |
| | | if (!ObjectUtils.equalsAny(task.getStatus(), MesProTaskStatusEnum.PREPARE.getStatus())) { |
| | | throw exception(PRO_TASK_CANNOT_PAUSE); |
| | | } |
| | | |
| | | // 3. ä¿®æ¹ä»»å¡ç¶æä¸ºå·²åå·¥ |
| | | taskMapper.updateById(new MesProTaskDO().setId(task.getId()).setStatus(MesProTaskStatusEnum.PAUSED.getStatus())); |
| | | |
| | | // 4. å建åå·¥è®°å½ |
| | | MesProTaskPauseRecordDO pauseRecord = new MesProTaskPauseRecordDO() |
| | | .setTaskId(task.getId()) |
| | | .setWorkOrderId(task.getWorkOrderId()) |
| | | .setWorkstationId(task.getWorkstationId()) |
| | | .setReason(pauseReqVO.getReason()) |
| | | .setRemark(pauseReqVO.getRemark()) |
| | | .setPauseTime(LocalDateTime.now()) |
| | | .setPauseUserId(SecurityFrameworkUtils.getLoginUserId()); |
| | | taskPauseRecordMapper.insert(pauseRecord); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void resumeTask(MesProTaskResumeReqVO resumeReqVO) { |
| | | // 1. æ ¡éªä»»å¡åå¨ |
| | | MesProTaskDO task = validateTaskExists(resumeReqVO.getId()); |
| | | // 2. æ ¡éªç¶æå¿
é¡»æ¯å·²åå·¥ |
| | | if (!ObjectUtils.equalsAny(task.getStatus(), MesProTaskStatusEnum.PAUSED.getStatus())) { |
| | | throw exception(PRO_TASK_NOT_PAUSED); |
| | | } |
| | | |
| | | // 3. æ¢å¤ä»»å¡ç¶æ |
| | | Integer resumeStatus = MesProTaskStatusEnum.PREPARE.getStatus(); |
| | | taskMapper.updateById(new MesProTaskDO().setId(task.getId()).setStatus(resumeStatus)); |
| | | |
| | | // 4. æ´æ°ææ°ç䏿¡åå·¥è®°å½ |
| | | MesProTaskPauseRecordDO pauseRecord = taskPauseRecordMapper.selectLastUnresumedByTaskId(task.getId()); |
| | | if (pauseRecord != null) { |
| | | pauseRecord.setResumeTime(LocalDateTime.now()); |
| | | pauseRecord.setResumeUserId(SecurityFrameworkUtils.getLoginUserId()); |
| | | taskPauseRecordMapper.updateById(pauseRecord); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | List<MesProWorkOrderProcessDO> getWorkOrderProcessListByWorkOrderId(Long workOrderId); |
| | | |
| | | /** |
| | | * æ ¹æ®å·¥åç¼å·è·å¾æåä¸éå·¥åº |
| | | * |
| | | * @param workOrderId å·¥åç¼å· |
| | | * @return æåä¸éå·¥åº |
| | | */ |
| | | MesProWorkOrderProcessDO getLastWorkOrderProcessByWorkOrderId(Long workOrderId); |
| | | |
| | | /** |
| | | * æ ¹æ®å·¥åç¼å·å表è·å¾å·¥åºå表 |
| | | * |
| | | * @param workOrderIds å·¥åç¼å·å表 |
| | | * @return å·¥åºå表 |
| | | */ |
| | | List<MesProWorkOrderProcessDO> getWorkOrderProcessListByWorkOrderIds(java.util.Collection<Long> workOrderIds); |
| | | |
| | | /** |
| | | * æ ¹æ®å·¥åç¼å·å é¤å·¥åº |
| | | * |
| | | * @param workOrderId å·¥åç¼å· |
| | |
| | | } |
| | | |
| | | @Override |
| | | public MesProWorkOrderProcessDO getLastWorkOrderProcessByWorkOrderId(Long workOrderId) { |
| | | return workOrderProcessMapper.selectLastByWorkOrderId(workOrderId); |
| | | } |
| | | |
| | | @Override |
| | | public List<MesProWorkOrderProcessDO> getWorkOrderProcessListByWorkOrderIds(java.util.Collection<Long> workOrderIds) { |
| | | if (CollUtil.isEmpty(workOrderIds)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return workOrderProcessMapper.selectListByWorkOrderIds(workOrderIds); |
| | | } |
| | | |
| | | @Override |
| | | public void deleteWorkOrderProcessByWorkOrderId(Long workOrderId) { |
| | | workOrderProcessMapper.deleteByWorkOrderId(workOrderId); |
| | | } |
| | |
| | | PageResult<MesProWorkOrderDO> getWorkOrderPage(MesProWorkOrderPageReqVO pageReqVO); |
| | | |
| | | /** |
| | | * è·å¾ç产工åå页ï¼å¸¦è°åº¦ç¶æï¼ |
| | | * |
| | | * @param pageReqVO å页æ¥è¯¢ |
| | | * @return ç产工åå页 |
| | | */ |
| | | PageResult<MesProWorkOrderDO> getWorkOrderSchedulePage(MesProWorkOrderPageReqVO pageReqVO); |
| | | |
| | | /** |
| | | * è·å¾å·¥åå表 |
| | | * |
| | | * @param ids ç¼å·æ°ç» |
| | | * @return å·¥åå表 |
| | | */ |
| | | List<MesProWorkOrderDO> getWorkOrderList(Collection<Long> ids); |
| | | List<MesProWorkOrderDO> getWorkOrderList(java.util.Collection<Long> ids); |
| | | |
| | | /** |
| | | * è·å¾å·¥å Map |
| | |
| | | } |
| | | |
| | | @Override |
| | | public PageResult<MesProWorkOrderDO> getWorkOrderSchedulePage(MesProWorkOrderPageReqVO pageReqVO) { |
| | | // è·åç¨æ·ææéçå®¢æ· ID å表 |
| | | List<Long> permittedClientIds = customerApi.getPermittedCustomerIds(); |
| | | return workOrderMapper.selectSchedulePage(pageReqVO, permittedClientIds); |
| | | } |
| | | |
| | | @Override |
| | | public List<MesProWorkOrderDO> getWorkOrderList(Collection<Long> ids) { |
| | | if (CollUtil.isEmpty(ids)) { |
| | | return Collections.emptyList(); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderMapper"> |
| | | |
| | | <select id="selectSchedulePage" resultType="cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO"> |
| | | SELECT |
| | | t.* |
| | | FROM |
| | | mes_pro_work_order t |
| | | <where> |
| | | t.deleted = 0 |
| | | <if test="pageReqVO.code != null and pageReqVO.code != ''"> |
| | | AND t.code LIKE CONCAT('%', #{pageReqVO.code}, '%') |
| | | </if> |
| | | <if test="pageReqVO.name != null and pageReqVO.name != ''"> |
| | | AND t.name LIKE CONCAT('%', #{pageReqVO.name}, '%') |
| | | </if> |
| | | <if test="pageReqVO.type != null"> |
| | | AND t.type = #{pageReqVO.type} |
| | | </if> |
| | | <if test="pageReqVO.orderSourceCode != null and pageReqVO.orderSourceCode != ''"> |
| | | AND t.order_source_code LIKE CONCAT('%', #{pageReqVO.orderSourceCode}, '%') |
| | | </if> |
| | | <if test="pageReqVO.productId != null"> |
| | | AND t.product_id = #{pageReqVO.productId} |
| | | </if> |
| | | <if test="pageReqVO.clientId != null"> |
| | | AND t.client_id = #{pageReqVO.clientId} |
| | | </if> |
| | | <if test="pageReqVO.status != null"> |
| | | AND t.status = #{pageReqVO.status} |
| | | </if> |
| | | <if test="pageReqVO.requestDate != null and pageReqVO.requestDate.length > 0"> |
| | | AND t.request_date >= #{pageReqVO.requestDate[0]} |
| | | <if test="pageReqVO.requestDate.length > 1"> |
| | | AND t.request_date <= #{pageReqVO.requestDate[1]} |
| | | </if> |
| | | </if> |
| | | |
| | | <if test="permittedClientIds != null"> |
| | | <choose> |
| | | <when test="permittedClientIds.size() > 0"> |
| | | AND (t.client_id IN |
| | | <foreach collection="permittedClientIds" item="id" open="(" separator="," close=")"> |
| | | #{id} |
| | | </foreach> |
| | | OR t.client_id IS NULL) |
| | | </when> |
| | | <otherwise> |
| | | AND 1 = 0 |
| | | </otherwise> |
| | | </choose> |
| | | </if> |
| | | |
| | | <if test="pageReqVO.scheduleStatus != null"> |
| | | <choose> |
| | | <!-- scheduleStatus: 1=已宿 (å·¥åç¶æ=4 æè
(æå·¥åº ä¸ ææå·¥åºé½æä»»å¡ ä¸ ææä»»å¡å·²å®æ)) --> |
| | | <when test="pageReqVO.scheduleStatus == 1"> |
| | | AND ( |
| | | t.status = 2 |
| | | OR ( |
| | | t.status != 4 |
| | | AND EXISTS (SELECT 1 FROM mes_pro_work_order_process wp WHERE wp.work_order_id = t.id AND wp.deleted = 0) |
| | | AND NOT EXISTS ( |
| | | SELECT 1 FROM mes_pro_work_order_process wp |
| | | WHERE wp.work_order_id = t.id AND wp.deleted = 0 |
| | | AND NOT EXISTS ( |
| | | SELECT 1 FROM mes_pro_task task |
| | | WHERE task.work_order_id = t.id AND task.process_id = wp.process_id AND task.deleted = 0 |
| | | ) |
| | | ) |
| | | AND NOT EXISTS ( |
| | | SELECT 1 FROM mes_pro_task task |
| | | WHERE task.work_order_id = t.id AND task.deleted = 0 |
| | | AND IFNULL(task.produced_quantity, 0) < IFNULL(task.quantity, 0) |
| | | ) |
| | | ) |
| | | ) |
| | | </when> |
| | | <!-- scheduleStatus: 0=æªå®æ (å·¥åç¶æ!=4 å¹¶ä¸ (æ å·¥åº æ ææå·¥åºæ ä»»å¡ æ å卿ªå®æçä»»å¡)) --> |
| | | <when test="pageReqVO.scheduleStatus == 0"> |
| | | AND ( |
| | | t.status in (1) |
| | | AND ( |
| | | NOT EXISTS (SELECT 1 FROM mes_pro_work_order_process wp WHERE wp.work_order_id = t.id AND wp.deleted = 0) |
| | | OR EXISTS ( |
| | | SELECT 1 FROM mes_pro_work_order_process wp |
| | | WHERE wp.work_order_id = t.id AND wp.deleted = 0 |
| | | AND NOT EXISTS ( |
| | | SELECT 1 FROM mes_pro_task task |
| | | WHERE task.work_order_id = t.id AND task.process_id = wp.process_id AND task.deleted = 0 |
| | | ) |
| | | ) |
| | | OR EXISTS ( |
| | | SELECT 1 FROM mes_pro_task task |
| | | WHERE task.work_order_id = t.id AND task.deleted = 0 |
| | | AND IFNULL(task.produced_quantity, 0) < IFNULL(task.quantity, 0) |
| | | ) |
| | | ) |
| | | ) |
| | | </when> |
| | | </choose> |
| | | </if> |
| | | </where> |
| | | ORDER BY t.id DESC |
| | | </select> |
| | | |
| | | </mapper> |
| | |
| | | # url: jdbc:postgresql://127.0.0.1:5432/postgres # OpenGauss è¿æ¥çç¤ºä¾ |
| | | # url: jdbc:postgresql://127.0.0.1:5866/ruoyi-vue-pro # çé« HighGo è¿æ¥çç¤ºä¾ |
| | | username: root |
| | | password: 123456 |
| | | password: root@2026 |
| | | # username: sa # SQL Server è¿æ¥çç¤ºä¾ |
| | | # password: Yudao@2024 # SQL Server è¿æ¥çç¤ºä¾ |
| | | # username: SYSDBA # DM è¿æ¥çç¤ºä¾ |
| | |
| | | lazy: true # å¼å¯æå è½½ï¼ä¿è¯å¯å¨é度 |
| | | url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true |
| | | username: root |
| | | password: 123456 |
| | | password: root@2026 |
| | | # tdengine: # IoT æ°æ®åºï¼éè¦ IoT ç©èç½åå¼å¯å¢ï¼ï¼ |
| | | # lazy: true # å¼å¯æå è½½ï¼ä¿è¯å¯å¨é度 |
| | | # url: jdbc:TAOS-WS://127.0.0.1:6041/ruoyi_vue_pro?varcharAsString=true&enableAutoReconnect=true # åè§ï¼https://t.zsxq.com/qaX9cï¼å¼å¯ TDengine WebSocket èªå¨éè¿ |
| | |
| | | host: 127.0.0.1 # å°å |
| | | port: 6379 # ç«¯å£ |
| | | database: 0 # æ°æ®åºç´¢å¼ |
| | | # password: dev # å¯ç ï¼å»ºè®®ç产ç¯å¢å¼å¯ |
| | | password: root@2026 # å¯ç ï¼å»ºè®®ç产ç¯å¢å¼å¯ |
| | | |
| | | --- #################### 宿¶ä»»å¡ç¸å
³é
ç½® #################### |
| | | |