diff --git a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/WmsApiService.java b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/WmsApiService.java index 8078843c..72d81f19 100644 --- a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/WmsApiService.java +++ b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/WmsApiService.java @@ -41,7 +41,7 @@ public class WmsApiService { * @param 请求体类型 * @return 响应结果 */ - public ApiResult post(String path, T body, String bizDesc) { + public ApiResult post(String path, T body, String bizDesc) { String fullUrl = host + path; HttpHeaders headers = new HttpHeaders(); @@ -50,7 +50,7 @@ public class WmsApiService { log.info("{},URL: {},数据: {}", bizDesc, fullUrl, JSONUtil.toJsonStr(body)); - ResponseEntity> response = restTemplate.exchange( + ResponseEntity> response = restTemplate.exchange( fullUrl, HttpMethod.POST, requestEntity, diff --git a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/WmsIncomingInspectionTaskCallbackService.java b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/WmsIncomingInspectionTaskCallbackService.java index 40248d55..817f26d6 100644 --- a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/WmsIncomingInspectionTaskCallbackService.java +++ b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/WmsIncomingInspectionTaskCallbackService.java @@ -2,6 +2,7 @@ package com.nflg.qms.admin.service; import com.nflg.wms.common.constant.STATE; import com.nflg.wms.common.exception.NflgException; +import com.nflg.wms.common.pojo.ApiResult; import com.nflg.wms.common.pojo.dto.MaterialQrCodeDTO; import com.nflg.wms.common.pojo.qo.WmsIncomingInspectionTaskCallbackQO; import com.nflg.wms.common.pojo.qo.WmsInventoryInspectionTaskCallbackQO; @@ -17,6 +18,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; +import software.amazon.awssdk.core.document.VoidDocumentVisitor; import java.util.List; import java.util.Objects; @@ -142,45 +144,39 @@ public class WmsIncomingInspectionTaskCallbackService { * 向WMS系统发送库存检验任务回调 */ private void inventory(QmsIncomingInspectionTaskVO taskVO, Short processingResult) { -// WmsInventoryInspectionTaskCallbackQO qo = new WmsInventoryInspectionTaskCallbackQO() -// .setTaskNo(taskVO.getTaskNo()) -// .setRequestNo(taskVO.getRequestNo()) -// .setMaterialNo(taskVO.getMaterialNo()) -// .setFactory(taskVO.getFactory()) -// .setWarehouse(taskVO.getWarehouse()) -// .setStorageLocation(taskVO.getStorageLocation()) -// .setStorageDays(taskVO.getStorageDays()) -// .setDetectionQty(taskVO.getDetectionQty()) -// .setQualifiedQty(taskVO.getQualifiedQty()) -// .setUnqualifiedQty(taskVO.getUnqualifiedQty()) -// .setInspectionResult(taskVO.getInspectionResult()) -// .setProcessingResult(processingResult); -// -// List records = incomingInspectionTaskRecordService.lambdaQuery() -// .select(QmsIncomingInspectionTaskRecord::getMaterialUniqueNo, QmsIncomingInspectionTaskRecord::getQualified) -// .eq(QmsIncomingInspectionTaskRecord::getTaskId, taskVO.getId()) -// .list(); -// qo.setQualifiedMaterialUniqueNo(records.stream() -// .filter(QmsIncomingInspectionTaskRecord::getQualified) -// .map(QmsIncomingInspectionTaskRecord::getMaterialUniqueNo) -// .toList() -// ); -// qo.setUnqualifiedMaterialUniqueNo(records.stream() -// .filter(record -> !record.getQualified()) -// .map(QmsIncomingInspectionTaskRecord::getMaterialUniqueNo) -// .toList() -// ); + WmsInventoryInspectionTaskCallbackQO qo = new WmsInventoryInspectionTaskCallbackQO() + .setTaskNo(taskVO.getTaskNo()) + .setRequestNo(taskVO.getRequestNo()) + .setMaterialNo(taskVO.getMaterialNo()) + .setFactory(taskVO.getFactory()) + .setWarehouse(taskVO.getWarehouse()) + .setStorageLocation(taskVO.getStorageLocation()) + .setStorageDays(taskVO.getStorageDays()) + .setDetectionQty(taskVO.getDetectionQty()) + .setQualifiedQty(taskVO.getQualifiedQty()) + .setUnqualifiedQty(taskVO.getUnqualifiedQty()) + .setInspectionResult(taskVO.getInspectionResult()) + .setProcessingResult(processingResult); + + List records = incomingInspectionTaskRecordService.lambdaQuery() + .select(QmsIncomingInspectionTaskRecord::getMaterialUniqueNo, QmsIncomingInspectionTaskRecord::getQualified) + .eq(QmsIncomingInspectionTaskRecord::getTaskId, taskVO.getId()) + .list(); + qo.setQualifiedMaterialUniqueNo(records.stream() + .filter(QmsIncomingInspectionTaskRecord::getQualified) + .map(QmsIncomingInspectionTaskRecord::getMaterialUniqueNo) + .toList() + ); + qo.setUnqualifiedMaterialUniqueNo(records.stream() + .filter(record -> !record.getQualified()) + .map(QmsIncomingInspectionTaskRecord::getMaterialUniqueNo) + .toList() + ); // 注意:本方法不可被 @Transactional 包裹,否则下面"标记失败"的更新会随异常一起回滚,导致失败状态丢失 try { -// wmsApiService.post(inventoryUrl, qo, "库存检验任务回调WMS"); - inventoryService.lambdaUpdate() - .set(WmsInventory::getDetectionStatus, (short) 2) - .set(WmsInventory::getDetectionResults, taskVO.getInspectionResult()) - .eq(WmsInventory::getId, Long.valueOf(taskVO.getRequestNo())) - .eq(WmsInventory::getDetectionStatus, (short) 1) - .update(); - updateCallbackResult(taskVO.getId(), true); + ApiResult result = wmsApiService.post(inventoryUrl, qo, "库存检验任务回调WMS"); + updateCallbackResult(taskVO.getId(), result.getCode() == 200); } catch (Exception e) { updateCallbackResult(taskVO.getId(), false); throw new NflgException(STATE.BusinessError, "调用WMS接口失败:" + e.getMessage()); diff --git a/nflg-wms-srm-receive/src/main/java/com/nflg/wms/srm/receive/controller/QmsController.java b/nflg-wms-srm-receive/src/main/java/com/nflg/wms/srm/receive/controller/QmsController.java index 2c3e8fb0..602caa9c 100644 --- a/nflg-wms-srm-receive/src/main/java/com/nflg/wms/srm/receive/controller/QmsController.java +++ b/nflg-wms-srm-receive/src/main/java/com/nflg/wms/srm/receive/controller/QmsController.java @@ -1,25 +1,35 @@ package com.nflg.wms.srm.receive.controller; import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Pair; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; +import com.nflg.wms.common.constant.STATE; +import com.nflg.wms.common.exception.NflgException; import com.nflg.wms.common.pojo.ApiResult; import com.nflg.wms.common.pojo.dto.InventoryInDTO; import com.nflg.wms.common.pojo.dto.MaterialQrCodeDTO; +import com.nflg.wms.common.pojo.dto.TransferOrderDTO; import com.nflg.wms.common.pojo.qo.WmsIncomingInspectionTaskCallbackQO; +import com.nflg.wms.common.pojo.qo.WmsInventoryInspectionTaskCallbackQO; +import com.nflg.wms.common.pojo.vo.InventoryLockVO; +import com.nflg.wms.common.util.UserUtil; import com.nflg.wms.common.util.VUtil; import com.nflg.wms.repository.entity.*; import com.nflg.wms.repository.service.*; import com.nflg.wms.srm.receive.pojo.dto.ZWM3A18DTO; import com.nflg.wms.srm.receive.pojo.dto.ZWM3A18Item1DTO; import com.nflg.wms.common.pojo.qo.PdiInspectionTaskCallbackQO; +import com.nflg.wms.srm.receive.service.BasdeSerialNumberControllerService; import com.nflg.wms.srm.receive.service.SapService; import com.nflg.wms.srm.receive.util.NoUtil; import com.nflg.wms.starter.BaseController; import jakarta.annotation.Resource; import jakarta.validation.constraints.NotNull; import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -31,6 +41,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.concurrent.TimeUnit; /** * 接收QMS推送数据 @@ -64,6 +75,18 @@ public class QmsController extends BaseController { @Resource private IWmsInProduceOrderItemService produceOrderItemService; + @Resource + private BasdeSerialNumberControllerService serialNumberControllerService; + + @Resource + private IWmsTransferFactoryService transferFactoryService; + + @Resource + private IWmsTransferFactoryItemService transferFactoryItemService; + + @Resource + private RedissonClient redissonClient; + /** * IQC来料检测回调 */ @@ -231,6 +254,61 @@ public class QmsController extends BaseController { return ApiResult.success(); } + /** + * IQC库存检测回调 + */ + @Transactional + @PostMapping("/iqc/inventory") + public ApiResult iqcIncoming(@RequestBody @NotNull WmsInventoryInspectionTaskCallbackQO qo) { + log.info("IQC检测回调, 请求单号: {}, 物料: {}, 结果: {}", qo.getRequestNo(), qo.getMaterialNo(), qo.getInspectionResult()); + inventoryService.lambdaUpdate() + .set(WmsInventory::getDetectionStatus, (short) 2) + .set(WmsInventory::getDetectionResults, qo.getInspectionResult()) + .eq(WmsInventory::getId, Long.valueOf(qo.getRequestNo())) + .eq(WmsInventory::getDetectionStatus, (short) 1) + .update(); + if (!qo.getInspectionResult()) { + TransferOrderDTO dto = new TransferOrderDTO() + .setMatnr(qo.getMaterialNo()) + .setWerks(qo.getFactory()) + .setLgort(qo.getWarehouse()) + .setLgpbe(qo.getStorageLocation()) + .setUmlgo("9011") + .setNum(BigDecimal.valueOf(qo.getUnqualifiedQty())); + WmsTransferFactory order = Convert.convert(WmsTransferFactory.class, dto); + order.setNo(serialNumberControllerService.generateSerialNumber(18)); + order.setCreateBy(UserUtil.getUserName()); + order.setCreateTime(LocalDateTime.now()); + transferFactoryService.save(order); + RLock lock = redissonClient.getLock(StrUtil.format("lock:inventory:{}:{}:{}", dto.getWerks(), dto.getLgort(), dto.getMatnr())); + try { + // 等待5秒获取锁,10秒后自动释放 + if (lock.tryLock(5, 10, TimeUnit.SECONDS)) { + if (inventoryService.getNumOne(dto.getWerks(), dto.getLgort(), dto.getMatnr()).compareTo(dto.getNum()) < 0) { + List itemLocks = inventoryService.getLockList(dto.getWerks(), dto.getLgort(), dto.getMatnr()); + VUtil.trueThrowBusinessError(CollectionUtil.isEmpty(itemLocks)).throwMessage("库存不足:" + dto.getMatnr()); + } else { + WmsTransferFactoryItem data = Convert.convert(WmsTransferFactoryItem.class, dto); + data.setId(IdUtil.getSnowflakeNextId()); + data.setOrderId(order.getId()); + data.setLeft(data.getNum()); + transferFactoryItemService.save(data); + } + } else { + throw new NflgException(STATE.BusinessError, "获取锁失败"); + } + } catch (Exception e) { + log.error("保存出错", e); + throw new NflgException(STATE.BusinessError, e.getMessage()); + } finally { + if (lock.isHeldByCurrentThread()) { + lock.unlock(); + } + } + } + return ApiResult.success(); + } + /** * PDI检测回调 */ diff --git a/nflg-wms-srm-receive/src/main/java/com/nflg/wms/srm/receive/service/BasdeSerialNumberControllerService.java b/nflg-wms-srm-receive/src/main/java/com/nflg/wms/srm/receive/service/BasdeSerialNumberControllerService.java new file mode 100644 index 00000000..6f53c620 --- /dev/null +++ b/nflg-wms-srm-receive/src/main/java/com/nflg/wms/srm/receive/service/BasdeSerialNumberControllerService.java @@ -0,0 +1,58 @@ +package com.nflg.wms.srm.receive.service; + +import cn.hutool.core.util.StrUtil; +import com.nflg.wms.repository.entity.BasdeSerialNumber; +import com.nflg.wms.repository.service.IBasdeSerialNumberService; +import jakarta.annotation.Resource; +import org.springframework.retry.annotation.Backoff; +import org.springframework.retry.annotation.Retryable; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Objects; + +@Component +public class BasdeSerialNumberControllerService { + + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); + private static final int MAX_RETRY = 5; // 最大重试次数 + + @Resource + private IBasdeSerialNumberService basdeSerialNumberService; + + @Retryable( + maxAttempts = 5, // 最大重试次数(包括第一次调用) + backoff = @Backoff(delay = 1000) // 重试间隔1秒 + ) + public String generateSerialNumber(Integer businessType, Integer suffixLen) { + String currentDate = LocalDate.now().format(DATE_FORMATTER); + + BasdeSerialNumber serialNumber = basdeSerialNumberService.lambdaQuery() + .eq(BasdeSerialNumber::getBusinessType, businessType) + .one(); + + if (Objects.isNull(serialNumber)) { + return null; + } + int nextSerial = 0; + if (StrUtil.equals(currentDate, serialNumber.getCurrentDateStr())) { + nextSerial = serialNumber.getMaxSerial(); + } + nextSerial = nextSerial + 1; + serialNumber.setMaxSerial(nextSerial); + serialNumber.setCurrentDateStr(currentDate); + basdeSerialNumberService.updateById(serialNumber); + // 格式化为4位数字,不足补零 + return serialNumber.getBusinessPrefixNumber() + currentDate + + StrUtil.padPre(String.valueOf(nextSerial), suffixLen, '0'); + } + + @Retryable( + maxAttempts = 5, // 最大重试次数(包括第一次调用) + backoff = @Backoff(delay = 1000) // 重试间隔1秒 + ) + public String generateSerialNumber(Integer businessType) { + return generateSerialNumber(businessType, 4); + } +}