| | |
| | | package cn.iocoder.yudao.module.mes.service.pro.scan; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo.MesProScanEventCreateReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo.MesProScanEventPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.pallet.MesProPalletDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.scan.MesProScanEventDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.batch.MesProBatchMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.pallet.MesProPalletMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.scan.MesProScanEventMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; |
| | | import cn.iocoder.yudao.module.mes.enums.MesProScanEventStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockStateEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; |
| | | import cn.iocoder.yudao.module.mes.framework.qrcode.MesTraceCodeUtils; |
| | | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProBagCodeStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProPalletStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProBatchStatusEnum; |
| | | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | |
| | | @Slf4j |
| | | @Service |
| | | @Validated |
| | | public class MesProScanEventServiceImpl implements MesProScanEventService { |
| | | @Resource private MesProScanEventMapper eventMapper; |
| | | @Resource private MesProBagCodeMapper bagCodeMapper; |
| | | @Resource private MesProBatchMapper batchMapper; |
| | | @Resource private MesProPalletMapper palletMapper; |
| | | @Resource private MesWmTransactionService transactionService; |
| | | @Resource private MesWmWarehouseService warehouseService; |
| | | @Resource private MesWmBatchService mesWmBatchService; |
| | | |
| | | /** 扫码类型:袋码(默认) */ |
| | | private static final String SCAN_TYPE_BAG = "BAG"; |
| | | /** 扫码类型:托盘码(按托盘已装袋数整托入库) */ |
| | | private static final String SCAN_TYPE_PALLET = "PALLET"; |
| | | /** 扫码类型:批次码(按批次袋数整批入库) */ |
| | | private static final String SCAN_TYPE_BATCH = "BATCH"; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public MesProScanEventDO createInbound(MesProScanEventCreateReqVO req) { |
| | | // 追溯二维码内容是「追溯网址?code=码值」,扫码枪也可能直传整段网址,先统一提取码值 |
| | | String scanCode = MesTraceCodeUtils.extractCode(req.getBagCode()); |
| | | String scanType = (req.getScanType() == null || req.getScanType().isBlank()) ? SCAN_TYPE_BAG : req.getScanType(); |
| | | if (!List.of(SCAN_TYPE_BAG, SCAN_TYPE_PALLET, SCAN_TYPE_BATCH).contains(scanType)) { |
| | | throw new ServiceException(400, "不支持的扫码类型"); |
| | | } |
| | | MesProScanEventDO existing = eventMapper.selectByEventIdForUpdate(req.getEventId()); |
| | | if (existing != null) { |
| | | if (!req.getBagCode().equals(existing.getBagCode()) || !req.getDeviceCode().equals(existing.getDeviceCode())) { |
| | | return failed(existing, "MES_SCAN_EVENT_ID_CONFLICT", "事件号已存在但内容不一致"); |
| | | boolean contentMatch = scanCode.equals(existing.getBagCode()) |
| | | && Objects.equals(req.getDeviceCode(), existing.getDeviceCode()) |
| | | && Objects.equals(req.getLineCode(), existing.getLineCode()) |
| | | && Objects.equals(scanType, existing.getScanType()) |
| | | && Objects.equals(req.getLocationId(), existing.getLocationId()) |
| | | && Objects.equals(req.getAreaId(), existing.getAreaId()) |
| | | && (!SCAN_TYPE_BAG.equals(scanType) || (existing.getQuantity() != null |
| | | && req.getQuantity().compareTo(existing.getQuantity()) == 0)) |
| | | && Objects.equals(String.valueOf(SecurityFrameworkUtils.getLoginUserId()), existing.getCreator()); |
| | | if (!contentMatch) { |
| | | throw new ServiceException(409, "事件号已存在但内容或操作人不一致,请核实原操作"); |
| | | } |
| | | return existing; |
| | | } |
| | | MesProScanEventDO event = new MesProScanEventDO(); |
| | | event.setEventId(req.getEventId()).setBagCode(req.getBagCode()).setQuantity(req.getQuantity()) |
| | | .setDeviceCode(req.getDeviceCode()).setLineCode(req.getLineCode()).setRemark(req.getRemark()) |
| | | event.setEventId(req.getEventId()).setBagCode(scanCode).setScanType(scanType) |
| | | .setQuantity(req.getQuantity()).setDeviceCode(req.getDeviceCode()).setLineCode(req.getLineCode()) |
| | | .setRemark(req.getRemark()).setLocationId(req.getLocationId()).setAreaId(req.getAreaId()) |
| | | .setStatus(MesProScanEventStatusEnum.FAILED).setReceivedTime(LocalDateTime.now()); |
| | | eventMapper.insert(event); |
| | | MesProBagCodeDO bag = bagCodeMapper.selectByCodeForUpdate(req.getBagCode()); |
| | | if (bag == null) return failed(event, "MES_SCAN_BAG_NOT_EXISTS", "袋码不存在"); |
| | | event.setBagCodeId(bag.getId()); |
| | | if (bag.getInboundEventId() != null) return failed(event, "MES_SCAN_BAG_ALREADY_INBOUND", "袋码已入库"); |
| | | MesProBatchDO batch = batchMapper.selectByIdForUpdate(bag.getBatchId()); |
| | | if (batch == null) return failed(event, "MES_SCAN_BATCH_NOT_EXISTS", "生产批次不存在"); |
| | | event.setBatchId(batch.getId()).setBatchCode(batch.getCode()).setItemId(batch.getItemId()); |
| | | // 1. 按扫码类型解析扫码目标与入库数量(托盘按已装袋数、批次按批次袋数整批/整托入库) |
| | | Long targetId; |
| | | Long batchId; |
| | | BigDecimal quantity; |
| | | MesProBatchDO batch; |
| | | // 单袋扫码入库时传递袋码,供库存袋码明细在入库第一时间建立;托盘/批次整批入库为批量,不逐袋建明细 |
| | | Long bagCodeId = null; |
| | | String bagCode = null; |
| | | if (SCAN_TYPE_PALLET.equals(scanType)) { |
| | | MesProPalletDO pallet = palletMapper.selectByScanCodeForUpdate(scanCode); |
| | | if (pallet == null) return failed(event, "MES_SCAN_PALLET_NOT_EXISTS", "托盘码不存在"); |
| | | if (MesProPalletStatusEnum.CANCELED.getStatus().equals(pallet.getStatus())) |
| | | return failed(event, "MES_SCAN_PALLET_CANCELED", "托盘已作废,不可入库"); |
| | | if (pallet.getInboundEventId() != null) return failed(event, "MES_SCAN_PALLET_ALREADY_INBOUND", "托盘已入库"); |
| | | batch = batchMapper.selectByIdForUpdate(pallet.getBatchId()); |
| | | if (batch == null) return failed(event, "MES_SCAN_BATCH_NOT_EXISTS", "生产批次不存在"); |
| | | batchId = batch.getId(); |
| | | targetId = pallet.getId(); |
| | | event.setPalletId(pallet.getId()) |
| | | .setPalletCode(StrUtil.blankToDefault(pallet.getCode(), pallet.getTempCode())); |
| | | quantity = BigDecimal.valueOf(bagCodeMapper.selectPendingInboundCountByPalletId(pallet.getId())); |
| | | } else if (SCAN_TYPE_BATCH.equals(scanType)) { |
| | | batch = batchMapper.selectByScanCodeForUpdate(scanCode); |
| | | if (batch == null) return failed(event, "MES_SCAN_BATCH_NOT_EXISTS", "生产批次不存在"); |
| | | batchId = batch.getId(); |
| | | targetId = batch.getId(); |
| | | quantity = BigDecimal.valueOf(bagCodeMapper.selectPendingInboundCountByBatchId(batch.getId())); |
| | | } else { |
| | | MesProBagCodeDO bag = bagCodeMapper.selectByScanCodeForUpdate(scanCode); |
| | | if (bag == null) return failed(event, "MES_SCAN_BAG_NOT_EXISTS", "袋码不存在"); |
| | | if (MesProBagCodeStatusEnum.CANCELED.getStatus().equals(bag.getStatus())) |
| | | return failed(event, "MES_SCAN_BAG_CANCELED", "袋码已作废,不可入库"); |
| | | event.setBagCodeId(bag.getId()); |
| | | if (bag.getPalletId() != null) { |
| | | event.setPalletId(bag.getPalletId()).setPalletCode(bag.getPalletCode()); |
| | | } |
| | | bagCodeId = bag.getId(); |
| | | // 库存流水保持原追溯码为规范码,业务码仅用于查找同一袋,避免双码拆成两份库存。 |
| | | bagCode = bag.getCode(); |
| | | if (bag.getInboundEventId() != null) return failed(event, "MES_SCAN_BAG_ALREADY_INBOUND", "袋码已入库"); |
| | | batch = batchMapper.selectByIdForUpdate(bag.getBatchId()); |
| | | if (batch == null) return failed(event, "MES_SCAN_BATCH_NOT_EXISTS", "生产批次不存在"); |
| | | batchId = batch.getId(); |
| | | targetId = bag.getId(); |
| | | quantity = req.getQuantity(); |
| | | } |
| | | if (MesProBatchStatusEnum.CANCELED.getStatus().equals(batch.getStatus())) |
| | | return failed(event, "MES_SCAN_BATCH_CANCELED", "生产批次已作废,不可入库"); |
| | | if (quantity.signum() <= 0) |
| | | return failed(event, "MES_SCAN_NO_PENDING_BAGS", "没有待入库的有效袋码,请勿重复入库"); |
| | | // 批次未补录正式批次号时回退临时批次码,保证任何扫码类型的事件都带出批次码 |
| | | event.setBatchId(batchId).setBatchCode(StrUtil.blankToDefault(batch.getCode(), batch.getTempCode())) |
| | | .setItemId(batch.getItemId()).setQuantity(quantity); |
| | | MesWmWarehouseDO warehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE); |
| | | if (warehouse == null) return failed(event, "MES_SCAN_WIP_WAREHOUSE_NOT_EXISTS", "WIP虚拟仓不存在"); |
| | | event.setWarehouseId(warehouse.getId()).setLocationId(req.getLocationId()).setAreaId(req.getAreaId()); |
| | | try { |
| | | // 生产批次桥接仓库批次:库存/流水统一挂在 mes_wm_batch 上,扫码入暂存库待质检 |
| | | MesWmBatchDO wmBatch = mesWmBatchService.getOrCreateByProBatch(batch); |
| | | if (wmBatch == null) { |
| | | return failed(event, "MES_SCAN_WM_BATCH_NOT_EXISTS", "仓库批次桥接失败"); |
| | | } |
| | | Long txId = transactionService.createTransaction(new MesWmTransactionSaveReqDTO() |
| | | .setType(MesWmTransactionTypeEnum.IN.getType()).setBizType(MesBizTypeConstants.WM_PRODUCT_PRODUCE) |
| | | .setBizId(batch.getId()).setBizCode(batch.getCode()).setBizLineId(bag.getId()) |
| | | .setItemId(batch.getItemId()).setQuantity(req.getQuantity()).setBatchCode(batch.getCode()) |
| | | .setBizId(batchId).setBizCode(batch.getCode()).setBizLineId(targetId) |
| | | .setItemId(batch.getItemId()).setQuantity(quantity) |
| | | .setBatchId(wmBatch.getId()).setBatchCode(batch.getCode()) |
| | | .setBagCodeId(bagCodeId).setBagCode(bagCode) |
| | | .setWarehouseId(warehouse.getId()).setLocationId(req.getLocationId()).setAreaId(req.getAreaId()) |
| | | .setReceiptTime(LocalDateTime.now()).setCheckFlag(true)); |
| | | .setReceiptTime(LocalDateTime.now()).setCheckFlag(true) |
| | | .setStockState(MesWmStockStateEnum.TEMP.getState())); |
| | | event.setTransactionId(txId).setStatus(MesProScanEventStatusEnum.SUCCESS).setEventTime(LocalDateTime.now()); |
| | | eventMapper.updateById(event); |
| | | bagCodeMapper.markInbound(bag.getId(), event.getId(), req.getDeviceCode(), req.getLineCode()); |
| | | batchMapper.markInbound(batch.getId(), event.getId(), req.getDeviceCode(), req.getLineCode()); |
| | | // 2. 回写入库标记(托盘/批次整托/整批入库时同步标记其下未入库袋码,保持状态一致) |
| | | if (SCAN_TYPE_PALLET.equals(scanType)) { |
| | | palletMapper.markInbound(targetId, event.getId()); |
| | | bagCodeMapper.markInboundByPalletId(targetId, event.getId(), req.getLineCode()); |
| | | } else if (SCAN_TYPE_BATCH.equals(scanType)) { |
| | | bagCodeMapper.markInboundByBatchId(targetId, event.getId(), req.getLineCode()); |
| | | } else { |
| | | bagCodeMapper.markInbound(targetId, event.getId(), req.getDeviceCode(), req.getLineCode()); |
| | | } |
| | | palletMapper.markCompletedInboundByBatchId(batchId, event.getId()); |
| | | batchMapper.refreshInboundProgress(batchId, event.getId(), req.getDeviceCode(), req.getLineCode()); |
| | | return event; |
| | | } catch (Exception ex) { |
| | | return failed(event, "MES_SCAN_TRANSACTION_FAILED", "库存事务处理失败"); |
| | | log.error("[createInbound] 库存事务处理失败,eventId={}, bagCode={}, batchId={}, itemId={}", |
| | | req.getEventId(), scanCode, batchId, batch.getItemId(), ex); |
| | | throw ex; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 为扫码事件补充托盘码:托盘码事件按扫描内容反查托盘,袋码事件取所属托盘; |
| | | * 批次事件整批可能跨多个托盘,不归属单一托盘,保持为空。 |
| | | * <p> |
| | | * 托盘码不落库,读取时实时反查,保证历史事件也能展示。 |
| | | */ |
| | | private void fillPalletInfo(List<MesProScanEventDO> events) { |
| | | if (CollUtil.isEmpty(events)) { |
| | | return; |
| | | } |
| | | // 1. 托盘码事件:扫描内容可能是业务码、正式码或临时码,统一反查 |
| | | List<String> palletScanCodes = events.stream() |
| | | .filter(event -> SCAN_TYPE_PALLET.equals(event.getScanType()) && StrUtil.isNotBlank(event.getBagCode())) |
| | | .map(MesProScanEventDO::getBagCode).distinct().toList(); |
| | | Map<String, MesProPalletDO> palletByScanCode = new HashMap<>(); |
| | | if (CollUtil.isNotEmpty(palletScanCodes)) { |
| | | for (MesProPalletDO pallet : palletMapper.selectListByScanCodes(palletScanCodes)) { |
| | | if (StrUtil.isNotBlank(pallet.getBusinessCode())) { |
| | | palletByScanCode.put(pallet.getBusinessCode(), pallet); |
| | | } |
| | | if (StrUtil.isNotBlank(pallet.getCode())) { |
| | | palletByScanCode.put(pallet.getCode(), pallet); |
| | | } |
| | | if (StrUtil.isNotBlank(pallet.getTempCode())) { |
| | | palletByScanCode.put(pallet.getTempCode(), pallet); |
| | | } |
| | | } |
| | | } |
| | | // 2. 袋码事件:按袋码编号或码值反查袋码,再取所属托盘 |
| | | List<Long> bagIds = events.stream() |
| | | .filter(event -> SCAN_TYPE_BAG.equals(event.getScanType())) |
| | | .map(MesProScanEventDO::getBagCodeId).filter(Objects::nonNull).distinct().toList(); |
| | | List<String> bagScanCodes = events.stream() |
| | | .filter(event -> SCAN_TYPE_BAG.equals(event.getScanType()) && StrUtil.isNotBlank(event.getBagCode())) |
| | | .map(MesProScanEventDO::getBagCode).distinct().toList(); |
| | | Map<Long, MesProBagCodeDO> bagById = new HashMap<>(); |
| | | Map<String, MesProBagCodeDO> bagByScanCode = new HashMap<>(); |
| | | if (CollUtil.isNotEmpty(bagIds)) { |
| | | for (MesProBagCodeDO bag : bagCodeMapper.selectBatchIds(bagIds)) { |
| | | bagById.put(bag.getId(), bag); |
| | | } |
| | | } |
| | | if (CollUtil.isNotEmpty(bagScanCodes)) { |
| | | for (MesProBagCodeDO bag : bagCodeMapper.selectListByScanCodes(bagScanCodes)) { |
| | | bagByScanCode.putIfAbsent(bag.getCode(), bag); |
| | | if (StrUtil.isNotBlank(bag.getBusinessCode())) { |
| | | bagByScanCode.putIfAbsent(bag.getBusinessCode(), bag); |
| | | } |
| | | } |
| | | } |
| | | // 3. 袋码冗余托盘码为空时,回查托盘取正式码/临时码 |
| | | Set<Long> missingPalletIds = new HashSet<>(); |
| | | for (MesProBagCodeDO bag : bagById.values()) { |
| | | if (bag.getPalletId() != null && StrUtil.isBlank(bag.getPalletCode())) { |
| | | missingPalletIds.add(bag.getPalletId()); |
| | | } |
| | | } |
| | | for (MesProBagCodeDO bag : bagByScanCode.values()) { |
| | | if (bag.getPalletId() != null && StrUtil.isBlank(bag.getPalletCode())) { |
| | | missingPalletIds.add(bag.getPalletId()); |
| | | } |
| | | } |
| | | Map<Long, MesProPalletDO> palletById = new HashMap<>(); |
| | | if (CollUtil.isNotEmpty(missingPalletIds)) { |
| | | for (MesProPalletDO pallet : palletMapper.selectBatchIds(missingPalletIds)) { |
| | | palletById.put(pallet.getId(), pallet); |
| | | } |
| | | } |
| | | for (MesProScanEventDO event : events) { |
| | | if (StrUtil.isNotBlank(event.getPalletCode())) { |
| | | continue; |
| | | } |
| | | if (SCAN_TYPE_PALLET.equals(event.getScanType())) { |
| | | MesProPalletDO pallet = palletByScanCode.get(event.getBagCode()); |
| | | if (pallet != null) { |
| | | event.setPalletId(pallet.getId()) |
| | | .setPalletCode(StrUtil.blankToDefault(pallet.getCode(), pallet.getTempCode())); |
| | | } |
| | | } else if (SCAN_TYPE_BAG.equals(event.getScanType())) { |
| | | MesProBagCodeDO bag = event.getBagCodeId() != null |
| | | ? bagById.get(event.getBagCodeId()) : bagByScanCode.get(event.getBagCode()); |
| | | if (bag == null || bag.getPalletId() == null) { |
| | | continue; |
| | | } |
| | | event.setPalletId(bag.getPalletId()); |
| | | if (StrUtil.isNotBlank(bag.getPalletCode())) { |
| | | event.setPalletCode(bag.getPalletCode()); |
| | | } else { |
| | | MesProPalletDO pallet = palletById.get(bag.getPalletId()); |
| | | if (pallet != null) { |
| | | event.setPalletCode(StrUtil.blankToDefault(pallet.getCode(), pallet.getTempCode())); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void fillPalletInfo(MesProScanEventDO event) { |
| | | if (event != null) { |
| | | fillPalletInfo(List.of(event)); |
| | | } |
| | | } |
| | | |
| | |
| | | return event; |
| | | } |
| | | |
| | | @Override public MesProScanEventDO getByEventId(String eventId) { return eventMapper.selectOne(MesProScanEventDO::getEventId, eventId); } |
| | | @Override public PageResult<MesProScanEventDO> getPage(MesProScanEventPageReqVO reqVO) { return eventMapper.selectPage(reqVO); } |
| | | @Override public MesProScanEventDO getLatest(String deviceCode) { return eventMapper.selectLatest(deviceCode); } |
| | | @Override public MesProScanEventDO getByEventId(String eventId) { |
| | | MesProScanEventDO event = eventMapper.selectOne(MesProScanEventDO::getEventId, eventId); |
| | | fillPalletInfo(event); |
| | | return event; |
| | | } |
| | | @Override public PageResult<MesProScanEventDO> getPage(MesProScanEventPageReqVO reqVO) { |
| | | PageResult<MesProScanEventDO> page = eventMapper.selectPage(reqVO); |
| | | fillPalletInfo(page.getList()); |
| | | return page; |
| | | } |
| | | @Override public MesProScanEventDO getLatest(String deviceCode) { |
| | | MesProScanEventDO event = eventMapper.selectLatest(deviceCode); |
| | | fillPalletInfo(event); |
| | | return event; |
| | | } |
| | | @Override public List<MesProScanEventDO> getByBatchId(Long batchId) { |
| | | List<MesProScanEventDO> events = eventMapper.selectListByBatchId(batchId); |
| | | fillPalletInfo(events); |
| | | return events; |
| | | } |
| | | @Override public Map<String, Object> getDashboard() { |
| | | Map<String, Object> result = new HashMap<>(); |
| | | result.put("latest", eventMapper.selectLatest(null)); |
| | | MesProScanEventDO latest = eventMapper.selectLatest(null); |
| | | fillPalletInfo(latest); |
| | | result.put("latest", latest); |
| | | result.put("total", eventMapper.selectCount(null)); |
| | | result.put("success", eventMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MesProScanEventDO>().eq(MesProScanEventDO::getStatus, MesProScanEventStatusEnum.SUCCESS))); |
| | | result.put("failed", eventMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MesProScanEventDO>().eq(MesProScanEventDO::getStatus, MesProScanEventStatusEnum.FAILED))); |