feat(srm): 新增序列号生成服务并完善IQC库存检测回调功能
- 新增BasdeSerialNumberControllerService提供带重试机制的序列号生成功能 - 在QmsController中实现IQC库存检测回调接口处理不合格品转移逻辑 - 集成Redisson分布式锁确保库存操作的并发安全 - 更新WmsApiService支持泛型返回类型提高接口调用灵活性 - 完善WMS库存检验任务回调服务增强异常处理和结果更新机制
This commit is contained in:
parent
828d33a724
commit
07720b4acf
|
|
@ -41,7 +41,7 @@ public class WmsApiService {
|
|||
* @param <T> 请求体类型
|
||||
* @return 响应结果
|
||||
*/
|
||||
public <T> ApiResult<Object> post(String path, T body, String bizDesc) {
|
||||
public <T,R> ApiResult<R> 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<ApiResult<Object>> response = restTemplate.exchange(
|
||||
ResponseEntity<ApiResult<R>> response = restTemplate.exchange(
|
||||
fullUrl,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
|
|
|
|||
|
|
@ -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<QmsIncomingInspectionTaskRecord> 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<QmsIncomingInspectionTaskRecord> 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<Void> 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());
|
||||
|
|
|
|||
|
|
@ -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<Void> 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<InventoryLockVO> 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检测回调
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue