diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java index 43c16b4..bde8bcd 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java @@ -39,12 +39,13 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; +import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; + import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -59,24 +60,20 @@ public class MesProTaskController { @Resource private MesProTaskService taskService; - @Resource private MesProWorkOrderService workOrderService; - @Resource private MesMdWorkstationService workstationService; - @Resource private MesProProcessService processService; - @Resource private MesMdItemService itemService; - @Resource private MesMdClientService clientService; - @Resource private MesMdUnitMeasureService unitMeasureService; + @Resource + private MesProRouteProcessService routeProcessService; @PostMapping("/create") @Operation(summary = "创建生产任务") @@ -217,11 +214,22 @@ public class MesProTaskController { Map workstationMap = workstationService.getWorkstationMap( convertSet(list, MesProTaskDO::getWorkstationId)); Map processMap = processService.getProcessMap( - new java.util.ArrayList<>(convertSet(list, MesProTaskDO::getProcessId))); + new ArrayList<>(convertSet(list, MesProTaskDO::getProcessId))); Map itemMap = itemService.getItemMap( convertSet(list, MesProTaskDO::getItemId)); Map clientMap = clientService.getClientMap( convertSet(list, MesProTaskDO::getClientId)); + // 工序的 checkFlag:批量查询后构建 routeId -> processId -> checkFlag 的双层 Map + Set routeIds = convertSet(list, MesProTaskDO::getRouteId); + Map> routeProcessCheckFlagMap = new HashMap<>(); + if (CollUtil.isNotEmpty(routeIds)) { + List allRouteProcesses = routeProcessService.getRouteProcessListByRouteIds(routeIds); + for (MesProRouteProcessDO rp : allRouteProcesses) { + routeProcessCheckFlagMap + .computeIfAbsent(rp.getRouteId(), k -> new HashMap<>()) + .put(rp.getProcessId(), Boolean.TRUE.equals(rp.getCheckFlag())); + } + } // 拼接 VO return convertList(list, task -> { MesProTaskRespVO vo = BeanUtils.toBean(task, MesProTaskRespVO.class); @@ -235,6 +243,8 @@ public class MesProTaskController { vo.setItemCode(item.getCode()).setItemName(item.getName()).setItemSpec(item.getSpecification())); findAndThen(clientMap, task.getClientId(), c -> vo.setClientName(c.getName())); + findAndThen(routeProcessCheckFlagMap, task.getRouteId(), processCheckMap -> + findAndThen(processCheckMap, task.getProcessId(), vo::setCheckFlag)); return vo; }); } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskPageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskPageReqVO.java index f855e42..28b4c06 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskPageReqVO.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskPageReqVO.java @@ -42,6 +42,9 @@ public class MesProTaskPageReqVO extends PageParam { @Schema(description = "任务状态列表(IN 查询)", example = "[0, 1, 2]") private List statuses; + @Schema(description = "是否质检(关联工艺路线工序)", example = "true") + private Boolean checkFlag; + @Schema(description = "创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java index 743ece8..95f9126 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java @@ -103,6 +103,9 @@ public class MesProTaskRespVO { @Schema(description = "任务状态", example = "0") private Integer status; + @Schema(description = "是否质检(派生自工艺路线工序 checkFlag)", example = "true") + private Boolean checkFlag; + @Schema(description = "备注", example = "备注") private String remark; diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskMapper.java index 56c0cf2..21b9f44 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskMapper.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskMapper.java @@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.mes.dal.mysql.pro.task; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPageReqVO; +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; @@ -20,8 +22,16 @@ import java.util.List; public interface MesProTaskMapper extends BaseMapperX { default PageResult selectPage(MesProTaskPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(MesProTaskDO::getCode, reqVO.getCode()) + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + query.selectAll(MesProTaskDO.class); + // 当需要按 checkFlag 过滤时,LEFT JOIN pro_route_process + if (reqVO.getCheckFlag() != null) { + query.leftJoin(MesProRouteProcessDO.class, on -> on + .eq(MesProRouteProcessDO::getRouteId, MesProTaskDO::getRouteId) + .eq(MesProRouteProcessDO::getProcessId, MesProTaskDO::getProcessId)); + query.eq(MesProRouteProcessDO::getCheckFlag, reqVO.getCheckFlag()); + } + query.likeIfPresent(MesProTaskDO::getCode, reqVO.getCode()) .likeIfPresent(MesProTaskDO::getName, reqVO.getName()) .eqIfPresent(MesProTaskDO::getWorkOrderId, reqVO.getWorkOrderId()) .eqIfPresent(MesProTaskDO::getRouteId, reqVO.getRouteId()) @@ -30,7 +40,8 @@ public interface MesProTaskMapper extends BaseMapperX { .eqIfPresent(MesProTaskDO::getStatus, reqVO.getStatus()) .inIfPresent(MesProTaskDO::getStatus, reqVO.getStatuses()) .betweenIfPresent(MesProTaskDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(MesProTaskDO::getId)); + .orderByDesc(MesProTaskDO::getId); + return selectPage(reqVO, query); } default List selectListByWorkOrderId(Long workOrderId) { diff --git a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskMapperTest.java b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskMapperTest.java new file mode 100644 index 0000000..4846286 --- /dev/null +++ b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskMapperTest.java @@ -0,0 +1,279 @@ +package cn.iocoder.yudao.module.mes.dal.mysql.pro.task; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPageReqVO; +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; +import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProcessMapper; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.List; +import java.util.function.Consumer; + +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link MesProTaskMapper#selectPage(MesProTaskPageReqVO)} 的单元测试 + * + * @author 芋道源码 + */ +public class MesProTaskMapperTest extends BaseDbUnitTest { + + @Resource + private MesProTaskMapper taskMapper; + + @Resource + private MesProRouteProcessMapper routeProcessMapper; + + /** + * 创建 Task 时统一设置 BigDecimal 字段为 2 位小数,避免 H2 decimal(14,2) 精度不匹配 + */ + private MesProTaskDO createTaskPojo(Consumer consumer) { + return randomPojo(MesProTaskDO.class, o -> { + o.setQuantity(new BigDecimal("10.00")); + o.setProducedQuantity(new BigDecimal("5.00")); + o.setQualifyQuantity(new BigDecimal("4.00")); + o.setUnqualifyQuantity(new BigDecimal("1.00")); + o.setChangedQuantity(new BigDecimal("0.00")); + consumer.accept(o); + }); + } + + // ==================== selectPage 基础过滤 ==================== + + @Test + public void testSelectPage_byCode() { + MesProTaskDO matchTask = createTaskPojo(o -> { + o.setCode("PT202501010001"); + o.setStatus(0); + }); + taskMapper.insert(matchTask); + taskMapper.insert(cloneIgnoreId(matchTask, o -> o.setCode("OTHER_CODE"))); + + MesProTaskPageReqVO reqVO = new MesProTaskPageReqVO(); + reqVO.setCode("PT2025"); + PageResult result = taskMapper.selectPage(reqVO); + + assertEquals(1, result.getTotal()); + assertPojoEquals(matchTask, result.getList().get(0)); + } + + @Test + public void testSelectPage_byName() { + MesProTaskDO matchTask = createTaskPojo(o -> { + o.setName("注塑任务-A"); + o.setStatus(0); + }); + taskMapper.insert(matchTask); + taskMapper.insert(cloneIgnoreId(matchTask, o -> o.setName("OTHER_NAME"))); + + MesProTaskPageReqVO reqVO = new MesProTaskPageReqVO(); + reqVO.setName("注塑"); + PageResult result = taskMapper.selectPage(reqVO); + + assertEquals(1, result.getTotal()); + assertPojoEquals(matchTask, result.getList().get(0)); + } + + @Test + public void testSelectPage_byWorkOrderId() { + MesProTaskDO matchTask = createTaskPojo(o -> { + o.setWorkOrderId(100L); + o.setStatus(0); + }); + taskMapper.insert(matchTask); + taskMapper.insert(cloneIgnoreId(matchTask, o -> o.setWorkOrderId(999L))); + + MesProTaskPageReqVO reqVO = new MesProTaskPageReqVO(); + reqVO.setWorkOrderId(100L); + PageResult result = taskMapper.selectPage(reqVO); + + assertEquals(1, result.getTotal()); + assertPojoEquals(matchTask, result.getList().get(0)); + } + + @Test + public void testSelectPage_byStatus() { + MesProTaskDO matchTask = createTaskPojo(o -> o.setStatus(1)); + taskMapper.insert(matchTask); + taskMapper.insert(cloneIgnoreId(matchTask, o -> o.setStatus(2))); + + MesProTaskPageReqVO reqVO = new MesProTaskPageReqVO(); + reqVO.setStatus(1); + PageResult result = taskMapper.selectPage(reqVO); + + assertEquals(1, result.getTotal()); + assertPojoEquals(matchTask, result.getList().get(0)); + } + + @Test + public void testSelectPage_byStatuses() { + MesProTaskDO task1 = createTaskPojo(o -> o.setStatus(1)); + MesProTaskDO task2 = createTaskPojo(o -> o.setStatus(2)); + MesProTaskDO task3 = createTaskPojo(o -> o.setStatus(3)); + taskMapper.insert(task1); + taskMapper.insert(task2); + taskMapper.insert(task3); + + MesProTaskPageReqVO reqVO = new MesProTaskPageReqVO(); + reqVO.setStatuses(List.of(1, 2)); + PageResult result = taskMapper.selectPage(reqVO); + + assertEquals(2, result.getTotal()); + } + + @Test + public void testSelectPage_noFilter() { + MesProTaskDO task1 = createTaskPojo(o -> o.setStatus(0)); + MesProTaskDO task2 = createTaskPojo(o -> o.setStatus(1)); + taskMapper.insert(task1); + taskMapper.insert(task2); + + MesProTaskPageReqVO reqVO = new MesProTaskPageReqVO(); + PageResult result = taskMapper.selectPage(reqVO); + + assertEquals(2, result.getTotal()); + } + + // ==================== selectPage + checkFlag (MPJ LEFT JOIN) ==================== + + @Test + public void testSelectPage_checkFlagTrue() { + Long routeId = 10L; + Long processId1 = 100L; + Long processId2 = 200L; + + MesProTaskDO task1 = createTaskPojo(o -> { + o.setRouteId(routeId); + o.setProcessId(processId1); + o.setStatus(0); + }); + MesProTaskDO task2 = createTaskPojo(o -> { + o.setRouteId(routeId); + o.setProcessId(processId2); + o.setStatus(0); + }); + taskMapper.insert(task1); + taskMapper.insert(task2); + + routeProcessMapper.insert(randomPojo(MesProRouteProcessDO.class, o -> { + o.setRouteId(routeId); + o.setProcessId(processId1); + o.setCheckFlag(true); + })); + routeProcessMapper.insert(randomPojo(MesProRouteProcessDO.class, o -> { + o.setRouteId(routeId); + o.setProcessId(processId2); + o.setCheckFlag(false); + })); + + // 查询:checkFlag = true,只返回 task1 + MesProTaskPageReqVO reqVO = new MesProTaskPageReqVO(); + reqVO.setCheckFlag(true); + PageResult result = taskMapper.selectPage(reqVO); + + assertEquals(1, result.getTotal()); + assertPojoEquals(task1, result.getList().get(0)); + } + + @Test + public void testSelectPage_checkFlagFalse() { + Long routeId = 20L; + Long processId1 = 300L; + Long processId2 = 400L; + + MesProTaskDO task1 = createTaskPojo(o -> { + o.setRouteId(routeId); + o.setProcessId(processId1); + o.setStatus(0); + }); + MesProTaskDO task2 = createTaskPojo(o -> { + o.setRouteId(routeId); + o.setProcessId(processId2); + o.setStatus(0); + }); + taskMapper.insert(task1); + taskMapper.insert(task2); + + routeProcessMapper.insert(randomPojo(MesProRouteProcessDO.class, o -> { + o.setRouteId(routeId); + o.setProcessId(processId1); + o.setCheckFlag(true); + })); + routeProcessMapper.insert(randomPojo(MesProRouteProcessDO.class, o -> { + o.setRouteId(routeId); + o.setProcessId(processId2); + o.setCheckFlag(false); + })); + + // 查询:checkFlag = false,只返回 task2 + MesProTaskPageReqVO reqVO = new MesProTaskPageReqVO(); + reqVO.setCheckFlag(false); + PageResult result = taskMapper.selectPage(reqVO); + + assertEquals(1, result.getTotal()); + assertPojoEquals(task2, result.getList().get(0)); + } + + @Test + public void testSelectPage_checkFlagWithOtherFilter() { + Long routeId = 30L; + Long processId = 500L; + + MesProTaskDO taskMatch = createTaskPojo(o -> { + o.setRouteId(routeId); + o.setProcessId(processId); + o.setCode("PT-MATCH-001"); + o.setStatus(0); + }); + MesProTaskDO taskNoMatch = createTaskPojo(o -> { + o.setRouteId(routeId); + o.setProcessId(processId); + o.setCode("OTHER-CODE"); + o.setStatus(0); + }); + taskMapper.insert(taskMatch); + taskMapper.insert(taskNoMatch); + + routeProcessMapper.insert(randomPojo(MesProRouteProcessDO.class, o -> { + o.setRouteId(routeId); + o.setProcessId(processId); + o.setCheckFlag(true); + })); + + // 查询:checkFlag=true 且 code like 'PT-MATCH' + MesProTaskPageReqVO reqVO = new MesProTaskPageReqVO(); + reqVO.setCheckFlag(true); + reqVO.setCode("PT-MATCH"); + PageResult result = taskMapper.selectPage(reqVO); + + assertEquals(1, result.getTotal()); + assertPojoEquals(taskMatch, result.getList().get(0)); + } + + @Test + public void testSelectPage_checkFlagNull_noJoin() { + Long routeId = 40L; + Long processId = 600L; + + MesProTaskDO task = createTaskPojo(o -> { + o.setRouteId(routeId); + o.setProcessId(processId); + o.setStatus(0); + }); + taskMapper.insert(task); + + MesProTaskPageReqVO reqVO = new MesProTaskPageReqVO(); + PageResult result = taskMapper.selectPage(reqVO); + + assertEquals(1, result.getTotal()); + assertPojoEquals(task, result.getList().get(0)); + } + +} diff --git a/yudao-module-mes/src/test/resources/sql/clean.sql b/yudao-module-mes/src/test/resources/sql/clean.sql index eda0d8c..4884447 100644 --- a/yudao-module-mes/src/test/resources/sql/clean.sql +++ b/yudao-module-mes/src/test/resources/sql/clean.sql @@ -13,3 +13,5 @@ DELETE FROM "mes_wm_item_receipt"; DELETE FROM "mes_wm_item_receipt_line"; DELETE FROM "mes_wm_item_receipt_detail"; DELETE FROM "mes_wm_batch"; +DELETE FROM "mes_pro_task"; +DELETE FROM "mes_pro_route_process"; diff --git a/yudao-module-mes/src/test/resources/sql/create_tables.sql b/yudao-module-mes/src/test/resources/sql/create_tables.sql index a37c9e9..1f3674c 100644 --- a/yudao-module-mes/src/test/resources/sql/create_tables.sql +++ b/yudao-module-mes/src/test/resources/sql/create_tables.sql @@ -618,3 +618,63 @@ CREATE TABLE IF NOT EXISTS "mes_wm_batch" ( "tenant_id" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("id") ); + +-- ---------------------------- +-- MES 生产任务 +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "mes_pro_task" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "code" varchar(64) DEFAULT NULL, + "name" varchar(255) DEFAULT NULL, + "work_order_id" bigint DEFAULT NULL, + "workstation_id" bigint DEFAULT NULL, + "route_id" bigint DEFAULT NULL, + "process_id" bigint DEFAULT NULL, + "item_id" bigint DEFAULT NULL, + "quantity" decimal(14,2) DEFAULT NULL, + "produced_quantity" decimal(14,2) DEFAULT NULL, + "qualify_quantity" decimal(14,2) DEFAULT NULL, + "unqualify_quantity" decimal(14,2) DEFAULT NULL, + "changed_quantity" decimal(14,2) DEFAULT NULL, + "client_id" bigint DEFAULT NULL, + "start_time" timestamp DEFAULT NULL, + "duration" int DEFAULT NULL, + "end_time" timestamp DEFAULT NULL, + "color_code" varchar(20) DEFAULT NULL, + "finish_date" timestamp DEFAULT NULL, + "cancel_date" timestamp DEFAULT NULL, + "status" int DEFAULT NULL, + "remark" varchar(500) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT 0, + PRIMARY KEY ("id") +); + +-- ---------------------------- +-- MES 工艺路线工序 +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "mes_pro_route_process" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "route_id" bigint DEFAULT NULL, + "process_id" bigint DEFAULT NULL, + "sort" int DEFAULT NULL, + "next_process_id" bigint DEFAULT NULL, + "link_type" int DEFAULT NULL, + "prepare_time" int DEFAULT NULL, + "wait_time" int DEFAULT NULL, + "color_code" varchar(20) DEFAULT NULL, + "key_flag" bit DEFAULT FALSE, + "check_flag" bit DEFAULT FALSE, + "remark" varchar(500) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT 0, + PRIMARY KEY ("id") +);