From bb2ebe49c90d3df86681dfcf6d8b840ad9b4bbef Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 24 Mar 2026 08:51:20 +0800 Subject: [PATCH] ✨ feat(mes): 添加待检任务分页查询功能,支持多来源数据整合 --- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/pendinginspect/MesQcPendingInspectController.java | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/pendinginspect/vo/MesQcPendingInspectRespVO.java | 3 --- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/pendinginspect/MesQcPendingInspectMapper.java | 21 +++++++++++++++++++++ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java | 5 +++++ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/pendinginspect/MesQcPendingInspectServiceImpl.java | 151 ++++++++++++------------------------------------------------------------------------------------------------------------------------------------------- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptService.java | 8 ++++++++ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImpl.java | 14 ++++++++++++++ yudao-module-mes/src/main/resources/mapper/qc/pendinginspect/MesQcPendingInspectMapper.xml | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 176 insertions(+), 143 deletions(-) create mode 100644 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/pendinginspect/MesQcPendingInspectMapper.java create mode 100644 yudao-module-mes/src/main/resources/mapper/qc/pendinginspect/MesQcPendingInspectMapper.xml diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/pendinginspect/MesQcPendingInspectController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/pendinginspect/MesQcPendingInspectController.java index 0b64e0d..14c1b20 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/pendinginspect/MesQcPendingInspectController.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/pendinginspect/MesQcPendingInspectController.java @@ -1,9 +1,16 @@ package cn.iocoder.yudao.module.mes.controller.admin.qc.pendinginspect; +import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.mes.controller.admin.qc.pendinginspect.vo.MesQcPendingInspectPageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.qc.pendinginspect.vo.MesQcPendingInspectRespVO; +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.md.vendor.MesMdVendorDO; +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.md.vendor.MesMdVendorService; import cn.iocoder.yudao.module.mes.service.qc.pendinginspect.MesQcPendingInspectService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -15,7 +22,11 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.Map; + import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; @Tag(name = "管理后台 - MES 待检任务") @RestController @@ -25,13 +36,49 @@ public class MesQcPendingInspectController { @Resource private MesQcPendingInspectService pendingInspectService; + @Resource + private MesMdItemService itemService; + @Resource + private MesMdUnitMeasureService unitMeasureService; + @Resource + private MesMdVendorService vendorService; @GetMapping("/page") @Operation(summary = "获得待检任务分页") @PreAuthorize("@ss.hasPermission('mes:qc-pending-inspect:query')") public CommonResult> getPendingInspectPage( @Valid MesQcPendingInspectPageReqVO pageReqVO) { - return success(pendingInspectService.getPendingInspectPage(pageReqVO)); + PageResult pageResult = pendingInspectService.getPendingInspectPage(pageReqVO); + // 拼接 VO:物料、供应商、计量单位 + buildPendingInspectRespVOList(pageResult.getList()); + return success(pageResult); + } + + // ==================== 拼接 VO ==================== + + private void buildPendingInspectRespVOList(java.util.List list) { + if (CollUtil.isEmpty(list)) { + return; + } + // 批量查询物料 + Map itemMap = itemService.getItemMap( + convertSet(list, MesQcPendingInspectRespVO::getItemId)); + // 批量查询计量单位(通过物料的 unitMeasureId) + Map unitMeasureMap = unitMeasureService.getUnitMeasureMap( + convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId)); + // 批量查询供应商 + Map vendorMap = vendorService.getVendorMap( + convertSet(list, MesQcPendingInspectRespVO::getVendorId)); + // 拼装 + list.forEach(vo -> { + findAndThen(itemMap, vo.getItemId(), item -> { + vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification()); + findAndThen(unitMeasureMap, item.getUnitMeasureId(), + unit -> vo.setUnitName(unit.getName())); + }); + findAndThen(vendorMap, vo.getVendorId(), + vendor -> vo.setVendorName(vendor.getName())); + }); } } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/pendinginspect/vo/MesQcPendingInspectRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/pendinginspect/vo/MesQcPendingInspectRespVO.java index bfb4403..6f719fa 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/pendinginspect/vo/MesQcPendingInspectRespVO.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/pendinginspect/vo/MesQcPendingInspectRespVO.java @@ -25,9 +25,6 @@ public class MesQcPendingInspectRespVO { @Schema(description = "检验类型(MesQcTypeEnum)", example = "1") private Integer qcType; - @Schema(description = "检验类型名称", example = "来料检验") - private String qcTypeName; - // ========== 物料 ========== @Schema(description = "物料 ID", example = "10") diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/pendinginspect/MesQcPendingInspectMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/pendinginspect/MesQcPendingInspectMapper.java new file mode 100644 index 0000000..6f8b78f --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/pendinginspect/MesQcPendingInspectMapper.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.mes.dal.mysql.qc.pendinginspect; + +import cn.iocoder.yudao.module.mes.controller.admin.qc.pendinginspect.vo.MesQcPendingInspectRespVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * MES 待检任务 Mapper + * + * 使用 UNION ALL SQL 从多个来源表(到货通知单、外协入库单等)查询待检行 + */ +@Mapper +public interface MesQcPendingInspectMapper { + + IPage selectQcPendingPage(IPage page, + @Param("sourceDocCode") String sourceDocCode, + @Param("qcType") Integer qcType, + @Param("itemId") Long itemId); + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java index 61dd3a0..b629c79 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java @@ -153,6 +153,9 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { } else if (Objects.equals(iqc.getSourceDocType(), MesBizTypeConstants.WM_OUTSOURCE_RECPT)) { outsourceReceiptService.approveOutsourceReceiptWhenIqcComplete(iqc.getSourceDocId(), iqc.getSourceLineId(), iqc.getId(), iqc.getQualifiedQuantity(), iqc.getUnqualifiedQuantity()); + } else { + throw new IllegalArgumentException( + "IQC 单[" + iqc.getId() + "] sourceDocType=" + iqc.getSourceDocType() + " 无法识别,无法回写来源单据"); } } @@ -220,6 +223,8 @@ public class MesQcIqcServiceImpl implements MesQcIqcService { // 根据来源单据类型,校验数据存在且匹配 if (Objects.equals(sourceDocType, MesBizTypeConstants.WM_ARRIVAL_NOTICE)) { arrivalNoticeService.validateArrivalNoticeAndLineExists(sourceDocId, sourceLineId); + } else if (Objects.equals(sourceDocType, MesBizTypeConstants.WM_OUTSOURCE_RECPT)) { + outsourceReceiptService.validateOutsourceReceiptAndLineExists(sourceDocId, sourceLineId); } } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/pendinginspect/MesQcPendingInspectServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/pendinginspect/MesQcPendingInspectServiceImpl.java index 7100c3f..c11205b 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/pendinginspect/MesQcPendingInspectServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/pendinginspect/MesQcPendingInspectServiceImpl.java @@ -1,162 +1,35 @@ package cn.iocoder.yudao.module.mes.service.qc.pendinginspect; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; import cn.iocoder.yudao.module.mes.controller.admin.qc.pendinginspect.vo.MesQcPendingInspectPageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.qc.pendinginspect.vo.MesQcPendingInspectRespVO; -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.md.vendor.MesMdVendorDO; -import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeDO; -import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeLineDO; -import cn.iocoder.yudao.module.mes.dal.mysql.wm.arrivalnotice.MesWmArrivalNoticeLineMapper; -import cn.iocoder.yudao.module.mes.dal.mysql.wm.arrivalnotice.MesWmArrivalNoticeMapper; -import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; -import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum; -import cn.iocoder.yudao.module.mes.enums.wm.MesWmArrivalNoticeStatusEnum; -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.md.vendor.MesMdVendorService; +import cn.iocoder.yudao.module.mes.dal.mysql.qc.pendinginspect.MesQcPendingInspectMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import java.util.*; -import java.util.stream.Collectors; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; - /** * MES 待检任务 Service 实现类 + * + * 使用 UNION ALL SQL 从多个来源表查询待检行,对齐 KTG 的 QcPendingInspectMapper.xml 实现 */ @Service @Validated public class MesQcPendingInspectServiceImpl implements MesQcPendingInspectService { @Resource - private MesWmArrivalNoticeMapper arrivalNoticeMapper; - @Resource - private MesWmArrivalNoticeLineMapper arrivalNoticeLineMapper; - @Resource - private MesMdItemService itemService; - @Resource - private MesMdVendorService vendorService; - @Resource - private MesMdUnitMeasureService unitMeasureService; + private MesQcPendingInspectMapper pendingInspectMapper; - // 【下面的 todo 先晚点改】 - // TODO @AI:是否可以在 mapper 里查询(union 汇总)然后关联数据的读取,尽量放在 controller; - // TODO @AI:另外,如果使用 mapper 操作,尽量使用 mybatis plus 或者 mybatis plus join,减少 mapper xml; @Override public PageResult getPendingInspectPage(MesQcPendingInspectPageReqVO pageReqVO) { - List allItems = new ArrayList<>(); - - // 1. IQC 来源:到货通知单 - if (pageReqVO.getQcType() == null || Objects.equals(pageReqVO.getQcType(), MesQcTypeEnum.IQC.getType())) { - allItems.addAll(queryIqcPendingFromArrivalNotice(pageReqVO)); - } - // TODO 2. IPQC 来源(生产工单,WM 模块未实现) - // TODO 3. OQC 来源(销售出库单,WM 模块未实现) - // TODO 4. RQC 来源(退货单,WM 模块未实现) - - // 5. 按 recordTime 降序排序 - allItems.sort(Comparator.comparing(MesQcPendingInspectRespVO::getRecordTime, - Comparator.nullsLast(Comparator.reverseOrder()))); - - // 6. 内存分页 - int total = allItems.size(); - int fromIndex = (pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize(); - if (fromIndex >= total) { - return new PageResult<>(Collections.emptyList(), (long) total); - } - int toIndex = Math.min(fromIndex + pageReqVO.getPageSize(), total); - return new PageResult<>(allItems.subList(fromIndex, toIndex), (long) total); + // 分页查询 + IPage page = MyBatisUtils.buildPage(pageReqVO); + pendingInspectMapper.selectQcPendingPage(page, + pageReqVO.getSourceDocCode(), pageReqVO.getQcType(), pageReqVO.getItemId()); + // 返回结果 + return new PageResult<>(page.getRecords(), page.getTotal()); } - // ==================== IQC 来源:到货通知单 ==================== - - private List queryIqcPendingFromArrivalNotice(MesQcPendingInspectPageReqVO pageReqVO) { - // 1. 查询状态为"待质检"的到货通知单 - List notices = arrivalNoticeMapper.selectListByStatus( - MesWmArrivalNoticeStatusEnum.PENDING_QC.getStatus()); - if (CollUtil.isEmpty(notices)) { - return Collections.emptyList(); - } - // 1.1 按 sourceDocCode 模糊过滤 - if (StrUtil.isNotBlank(pageReqVO.getSourceDocCode())) { - notices = notices.stream() - .filter(n -> n.getCode().contains(pageReqVO.getSourceDocCode())) - .collect(Collectors.toList()); - } - if (CollUtil.isEmpty(notices)) { - return Collections.emptyList(); - } - Map noticeMap = convertMap(notices, MesWmArrivalNoticeDO::getId); - - // 2. 查询需要 IQC 但尚未创建检验单的行 - List lines = arrivalNoticeLineMapper.selectListByIqcPending( - new ArrayList<>(noticeMap.keySet())); - if (CollUtil.isEmpty(lines)) { - return Collections.emptyList(); - } - - // 3. 批量查询物料 - Map itemMap = itemService.getItemMap(convertSet(lines, MesWmArrivalNoticeLineDO::getItemId)); - // 3.1 按 itemId 精确过滤 - if (pageReqVO.getItemId() != null) { - lines = lines.stream() - .filter(line -> pageReqVO.getItemId().equals(line.getItemId())) - .collect(Collectors.toList()); - } - if (CollUtil.isEmpty(lines)) { - return Collections.emptyList(); - } - - // 4. 批量查询供应商 - Map vendorMap = vendorService.getVendorMap( - convertSet(notices, MesWmArrivalNoticeDO::getVendorId)); - // 5. 批量查询计量单位 - Map unitMeasureMap = unitMeasureService.getUnitMeasureMap( - convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId)); - - // 6. 组装 VO - List result = new ArrayList<>(lines.size()); - for (MesWmArrivalNoticeLineDO line : lines) { - MesWmArrivalNoticeDO notice = noticeMap.get(line.getNoticeId()); - if (notice == null) { - continue; - } - MesQcPendingInspectRespVO vo = new MesQcPendingInspectRespVO(); - vo.setSourceDocId(notice.getId()); - vo.setSourceDocType(MesBizTypeConstants.WM_ARRIVAL_NOTICE); - vo.setSourceDocCode(notice.getCode()); - vo.setSourceLineId(line.getId()); - vo.setQcType(MesQcTypeEnum.IQC.getType()); - vo.setQcTypeName(MesQcTypeEnum.IQC.getName()); - vo.setItemId(line.getItemId()); - vo.setQuantityToCheck(line.getArrivalQuantity()); - vo.setVendorId(notice.getVendorId()); - vo.setRecordTime(notice.getArrivalDate()); - // 物料信息 - MesMdItemDO item = itemMap.get(line.getItemId()); - if (item != null) { - vo.setItemCode(item.getCode()); - vo.setItemName(item.getName()); - vo.setSpecification(item.getSpecification()); - MesMdUnitMeasureDO unit = unitMeasureMap.get(item.getUnitMeasureId()); - if (unit != null) { - vo.setUnitName(unit.getName()); - } - } - // 供应商信息 - MesMdVendorDO vendor = vendorMap.get(notice.getVendorId()); - if (vendor != null) { - vo.setVendorName(vendor.getName()); - } - result.add(vo); - } - return result; - } } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptService.java index d9600df..cd00de8 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptService.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptService.java @@ -91,4 +91,12 @@ public interface MesWmOutsourceReceiptService { void approveOutsourceReceiptWhenIqcComplete(Long receiptId, Long lineId, Long iqcId, BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity); + /** + * 校验外协入库单和行存在 + * + * @param receiptId 入库单编号 + * @param lineId 行编号 + */ + void validateOutsourceReceiptAndLineExists(Long receiptId, Long lineId); + } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImpl.java index c082838..6b9f58e 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImpl.java @@ -250,6 +250,20 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe return receipt; } + @Override + public void validateOutsourceReceiptAndLineExists(Long receiptId, Long lineId) { + // 1. 校验入库单存在 + validateOutsourceReceiptExists(receiptId); + // 2. 校验行存在且属于该入库单 + MesWmOutsourceReceiptLineDO line = outsourceReceiptLineService.getOutsourceReceiptLine(lineId); + if (line == null) { + throw exception(WM_OUTSOURCE_RECEIPT_LINE_NOT_EXISTS); + } + if (ObjUtil.notEqual(receiptId, line.getReceiptId())) { + throw exception(WM_OUTSOURCE_RECEIPT_LINE_NOT_EXISTS); + } + } + /** * 校验外协入库单存在且为草稿状态 */ diff --git a/yudao-module-mes/src/main/resources/mapper/qc/pendinginspect/MesQcPendingInspectMapper.xml b/yudao-module-mes/src/main/resources/mapper/qc/pendinginspect/MesQcPendingInspectMapper.xml new file mode 100644 index 0000000..af8cc49 --- /dev/null +++ b/yudao-module-mes/src/main/resources/mapper/qc/pendinginspect/MesQcPendingInspectMapper.xml @@ -0,0 +1,68 @@ + + + + + + + + -- libgit2 0.21.4