feat(wms): 实现批量来料检验申请功能

- 将来料检验申请接口改为支持批量处理多个申请
- 添加QMS服务类用于推送检验申请到质量管理系统
- 在SRM检验内容DTO中新增采购组字段
- 在SRM检验行项目DTO中新增请求单号和二维码列表字段
- 修复了待办事项重复添加的问题
- 激活并实现之前被注释掉的数量一致性校验逻辑
- 修复了物料编号不存在时的错误提示信息
- 移除物料唯一编号字段的非空验证注解
This commit is contained in:
曹鹏飞 2026-06-05 11:48:22 +08:00
parent 512c3f97f9
commit c50972be50
10 changed files with 182 additions and 94 deletions

View File

@ -8,6 +8,8 @@ import com.nflg.wms.starter.BaseController;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@ -31,8 +33,8 @@ public class ExternalIncomingInspectionTaskController extends BaseController {
* - 要求完成时间 = 当前时间 + 检验标准中的检验周期
*/
@PostMapping("incoming-apply")
public ApiResult<Void> IncomingApply(@Valid @RequestBody ExternalIncomingInspectionApplyQO request) {
incomingInspectionTaskControllerService.IncomingApply(request);
public ApiResult<Void> IncomingApply(@Valid @RequestBody List<ExternalIncomingInspectionApplyQO> requests) {
incomingInspectionTaskControllerService.IncomingApply(requests);
return ApiResult.success();
}

View File

@ -127,7 +127,16 @@ public class IncomingInspectionTaskControllerService {
* - 若质检人员设置了转办人则使用转办人代替
*/
@Transactional
public void IncomingApply(ExternalIncomingInspectionApplyQO request) {
public void IncomingApply(List<ExternalIncomingInspectionApplyQO> requests) {
for (ExternalIncomingInspectionApplyQO request : requests) {
applySingle(request);
}
}
/**
* 单个来料检验申请处理
*/
private void applySingle(ExternalIncomingInspectionApplyQO request) {
// 1. 查询质检物料取该物料编号下 id 最大的记录最新版本
QmsQcMaterial material = qcMaterialService.lambdaQuery()
.eq(QmsQcMaterial::getMaterialNo, request.getMaterialNo())
@ -135,7 +144,7 @@ public class IncomingInspectionTaskControllerService {
.last("LIMIT 1")
.one();
VUtil.trueThrowBusinessError(Objects.isNull(material))
.throwMessage("物料编号不存在于质检物料表中");
.throwMessage("物料编号 [" + request.getMaterialNo() + "] 不存在于质检物料表中");
// 2. 查询该物料对应的已发布检验标准
QmsInspectionStandard standard = inspectionStandardService.lambdaQuery()
@ -146,7 +155,7 @@ public class IncomingInspectionTaskControllerService {
.last("LIMIT 1")
.one();
VUtil.trueThrowBusinessError(Objects.isNull(standard))
.throwMessage("物料不存在对应的检验标准");
.throwMessage("物料编号 [" + request.getMaterialNo() + "] 不存在对应的检验标准");
// 3. 查找负责该物料的质检人员IQEinspectionType=1
QmsQualityInspector inspector = resolveInspector(material);

View File

@ -357,6 +357,7 @@ public class NormalPGIController extends BaseController {
content.setSupplierNum(order.getSupplierNum());
content.setReceiveNum("");
content.setReceiveType("BY_DELIVERY");
content.setPurchaseGroup(order.getPurchaseGroup());
content.setLineVOList(new ArrayList<>());
srmDto.setContent(content);
@ -391,6 +392,7 @@ public class NormalPGIController extends BaseController {
srmItem.setSerialNum("");
srmItem.setLineNumber(Integer.valueOf(orderItem.getLineNumber()));
srmItem.setNoteNum(order.getNoteNum());
srmItem.setRequestNo(String.valueOf(qcItem.getId()));
srmItem.setReceivedDate(DateTimeUtil.format(LocalDate.now(), "yyyy-MM-dd"));
srmItem.setInspectionFlag("Y");
qcDto.getPushDto().getContent().getLineVOList().add(srmItem);
@ -1137,6 +1139,7 @@ public class NormalPGIController extends BaseController {
srmItem.setSerialNum(keys[1]);
srmItem.setLineNumber(Integer.valueOf(item.getLineNumber()));
srmItem.setNoteNum(item.getNoteNum());
srmItem.setRequestNo(IdUtil.getSnowflakeNextIdStr());
srmItem.setReceivedDate(DateTimeUtil.format(LocalDate.now(), "yyyy-MM-dd"));
srmItem.setInspectionFlag("Y");
pushDto.getContent().getLineVOList().add(srmItem);
@ -1153,6 +1156,7 @@ public class NormalPGIController extends BaseController {
srmItem.setSerialNum("");
srmItem.setLineNumber(Integer.valueOf(item.getLineNumber()));
srmItem.setNoteNum(item.getNoteNum());
srmItem.setRequestNo(IdUtil.getSnowflakeNextIdStr());
srmItem.setReceivedDate(DateTimeUtil.format(LocalDate.now(), "yyyy-MM-dd"));
srmItem.setInspectionFlag("Y");
pushDto.getContent().getLineVOList().add(srmItem);
@ -1202,6 +1206,7 @@ public class NormalPGIController extends BaseController {
content.setSupplierNum(order.getSupplierNum());
content.setReceiveNum("");
content.setReceiveType("BY_DELIVERY");
content.setPurchaseGroup(order.getPurchaseGroup());
content.setLineVOList(new ArrayList<>());
dto.setContent(content);
dto.setCode("WMS_RCV_RECEIVE_TO_SRM");

View File

@ -45,10 +45,10 @@ public class NoScanningBaseControllerService {
* @return 是否生成过二维码
*/
public boolean existsQrCode(String materialNo) {
// return qrCodeMasterService.lambdaQuery()
// .eq(WmsQrCodeMaster::getMaterialCode, materialNo)
// .exists();
return false;
return qrCodeMasterService.lambdaQuery()
.eq(WmsQrCodeMaster::getMaterialCode, materialNo)
.exists();
// return false;
}
public boolean cannotOutNoScanning(String materialNo, String factoryNo, String warehouseNo) {
@ -66,27 +66,27 @@ public class NoScanningBaseControllerService {
* @return 数量是否一致
*/
private boolean quantityConsistencyCheck(String materialNo, String factoryNo, String warehouseNo) {
// BigDecimal quantityQr = qrCodeMasterService.lambdaQuery()
// .select(WmsQrCodeMaster::getQuantity)
// .eq(WmsQrCodeMaster::getMaterialCode, materialNo)
// .eq(WmsQrCodeMaster::getFactoryCode, factoryNo)
// .eq(WmsQrCodeMaster::getStorageLocation, warehouseNo)
// .eq(WmsQrCodeMaster::getProcessStage, BarCodeProcessStage.InBound.getState())
// .list()
// .stream()
// .map(WmsQrCodeMaster::getQuantity)
// .reduce(BigDecimal.ZERO, BigDecimal::add);
// BigDecimal quantityInventory = inventoryService.lambdaQuery()
// .select(WmsInventory::getNum)
// .eq(WmsInventory::getMaterialNo, materialNo)
// .eq(WmsInventory::getFactoryNo, factoryNo)
// .eq(WmsInventory::getWarehouseNo, warehouseNo)
// .list()
// .stream()
// .map(WmsInventory::getNum)
// .reduce(BigDecimal.ZERO, BigDecimal::add);
// return quantityQr.compareTo(quantityInventory) == 0;
return true;
BigDecimal quantityQr = qrCodeMasterService.lambdaQuery()
.select(WmsQrCodeMaster::getQuantity)
.eq(WmsQrCodeMaster::getMaterialCode, materialNo)
.eq(WmsQrCodeMaster::getFactoryCode, factoryNo)
.eq(WmsQrCodeMaster::getStorageLocation, warehouseNo)
.eq(WmsQrCodeMaster::getProcessStage, BarCodeProcessStage.InBound.getState())
.list()
.stream()
.map(WmsQrCodeMaster::getQuantity)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal quantityInventory = inventoryService.lambdaQuery()
.select(WmsInventory::getNum)
.eq(WmsInventory::getMaterialNo, materialNo)
.eq(WmsInventory::getFactoryNo, factoryNo)
.eq(WmsInventory::getWarehouseNo, warehouseNo)
.list()
.stream()
.map(WmsInventory::getNum)
.reduce(BigDecimal.ZERO, BigDecimal::add);
return quantityQr.compareTo(quantityInventory) == 0;
// return true;
}
/**

View File

@ -13,6 +13,7 @@ import com.nflg.wms.common.constant.BarCodeProcessStage;
import com.nflg.wms.common.pojo.document.SrmMaterialReceiptNoScanCodes;
import com.nflg.wms.common.pojo.document.SrmMaterialReceiptScanCodes;
import com.nflg.wms.common.pojo.dto.*;
import com.nflg.wms.common.pojo.qo.ExternalIncomingInspectionApplyQO;
import com.nflg.wms.common.pojo.qo.PDAScanCodeQO;
import com.nflg.wms.common.pojo.qo.SrmMaterialReceiptQO;
import com.nflg.wms.common.pojo.vo.PDAOrderItemVO;
@ -112,6 +113,9 @@ public class NormalPGIControllerService {
@Resource
private IWmsPackageItemService wmsPackageItemService;
@Resource
private QmsService qmsService;
/**
* 根据订单编号获取订单详情信息包括订单头信息和订单行项目信息
* <p>
@ -581,73 +585,72 @@ public class NormalPGIControllerService {
* @param request SRM检验输入数据传输对象包含需要推送的检验物料信息
*/
private void pushInspectionMaterialsToSRM(SRMInspectionInputDTO request) {
// 获取SRM系统认证Token
String token = GetSRMToken();
VUtil.trueThrowBusinessError(token.trim().isEmpty()).throwMessage("获取SRM的TOKEN失败");
log.info("收货单推送SRM数据" + JSONUtil.toJsonStr(request));
// 构建HTTP请求头信息
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("AuthorizationCode", token);
headers.add("Gateway-Tag", gatewayTag);
HttpEntity<SRMInspectionInputDTO> requestEntity = new HttpEntity<>(request, headers);
// 调用SRM认证接口获取新的Token
ResponseEntity<SRMBaseDTO<Object>> response = restTemplate.exchange(
pushUrl,
HttpMethod.POST,
requestEntity,
new ParameterizedTypeReference<SRMBaseDTO<Object>>() {
}
);
// 记录推送结果日志并验证响应状态
log.info("收货单推送SRM的结果{},{}", response.getStatusCodeValue(), JSONUtil.toJsonStr(response.getBody()));
VUtil.trueThrowBusinessError(Objects.isNull(response.getBody()) || !response.getBody().getStatus().equals("S")).throwMessage("收货单推送SRM失败");
}
/**
* 获取SRM系统认证Token
* <p>
* 该方法首先尝试从Redis缓存中获取SRM Token如果缓存中不存在或为空
* 则通过OAuth认证接口重新获取Token并存入Redis缓存50分钟
*
* @return SRM系统认证Token字符串
*/
private String GetSRMToken() {
// 从Redis缓存中获取SRM Token
String srmToken = stringRedisTemplate.opsForValue().get("srm:token");
if (srmToken == null || srmToken.trim().isEmpty()) {
// 构造HTTP请求头和请求体
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<SRMTokenInputDTO> requestEntity = new HttpEntity<>(new SRMTokenInputDTO(oauthApp, oauthSecret), headers);
// 调用SRM认证接口获取新的Token
ResponseEntity<SRMBaseDTO<SRMTokenDTO>> response = restTemplate.exchange(
tokenUrl,
HttpMethod.POST,
requestEntity,
new ParameterizedTypeReference<SRMBaseDTO<SRMTokenDTO>>() {
}
);
// 提取返回的Token并存储到Redis缓存中有效期50分钟
String newToken = response.getBody() == null ? "" : response.getBody().getData().getAuthorizationCode();
stringRedisTemplate.opsForValue().set("srm:token", newToken, 50, TimeUnit.MINUTES);
log.info("PushTagValue结果{},{}", response.getStatusCodeValue(), JSONUtil.toJsonStr(response.getBody()));
SRMInspectionContentDTO content = request.getContent();
if (content == null || CollectionUtil.isEmpty(content.getLineVOList())) {
return;
}
// 重新从Redis获取Token并返回
srmToken = stringRedisTemplate.opsForValue().get("srm:token");
return srmToken;
List<ExternalIncomingInspectionApplyQO> qoList = new ArrayList<>();
for (SRMLineVOListItem item : content.getLineVOList()) {
ExternalIncomingInspectionApplyQO qo = new ExternalIncomingInspectionApplyQO();
qo.setRequestNo(item.getRequestNo());
qo.setMaterialNo(item.getItemCode());
qo.setInspectionType(0);
qo.setSupplierCode(content.getSupplierNum());
qo.setSupplierName(content.getSupplierName());
qo.setDeliveryOrderNo(content.getReceiveNum());
qo.setDeliveryOrderLine(String.valueOf(item.getLineNumber()));
qo.setPurchaseOrderNo(item.getNoteNum());
qo.setPurchaseOrderLine(String.valueOf(item.getLineNumber()));
qo.setFactory(item.getFactory());
qo.setInspectionQty(item.getReceivedQty() != null ? item.getReceivedQty().intValue() : 0);
qo.setPurchaseGroup(content.getPurchaseGroup());
qoList.add(qo);
}
qmsService.pushIQCIncoming(qoList);
}
// /**
// * 获取SRM系统认证Token
// * <p>
// * 该方法首先尝试从Redis缓存中获取SRM Token如果缓存中不存在或为空
// * 则通过OAuth认证接口重新获取Token并存入Redis缓存50分钟
// *
// * @return SRM系统认证Token字符串
// */
// private String GetSRMToken() {
//
// // 从Redis缓存中获取SRM Token
// String srmToken = stringRedisTemplate.opsForValue().get("srm:token");
// if (srmToken == null || srmToken.trim().isEmpty()) {
// // 构造HTTP请求头和请求体
// HttpHeaders headers = new HttpHeaders();
// headers.setContentType(MediaType.APPLICATION_JSON);
// HttpEntity<SRMTokenInputDTO> requestEntity = new HttpEntity<>(new SRMTokenInputDTO(oauthApp, oauthSecret), headers);
//
// // 调用SRM认证接口获取新的Token
// ResponseEntity<SRMBaseDTO<SRMTokenDTO>> response = restTemplate.exchange(
// tokenUrl,
// HttpMethod.POST,
// requestEntity,
// new ParameterizedTypeReference<SRMBaseDTO<SRMTokenDTO>>() {
//
// }
// );
//
// // 提取返回的Token并存储到Redis缓存中有效期50分钟
// String newToken = response.getBody() == null ? "" : response.getBody().getData().getAuthorizationCode();
// stringRedisTemplate.opsForValue().set("srm:token", newToken, 50, TimeUnit.MINUTES);
// log.info("PushTagValue结果{},{}", response.getStatusCodeValue(), JSONUtil.toJsonStr(response.getBody()));
// }
//
// // 重新从Redis获取Token并返回
// srmToken = stringRedisTemplate.opsForValue().get("srm:token");
// return srmToken;
// }
public List<PDAOrderItemVO> getPackageContents(@NotNull @NotBlank String packageCode) {
List<PackagePOItemDTO> packagePOItems = wmsPackageService.getPackagePOItems(packageCode);
VUtil.trueThrowBusinessError(CollectionUtil.isEmpty(packagePOItems)).throwMessage("此大包中的物料已完成收货");

View File

@ -0,0 +1,52 @@
package com.nflg.wms.admin.service;
import cn.hutool.json.JSONUtil;
import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.qo.ExternalIncomingInspectionApplyQO;
import com.nflg.wms.common.util.VUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Objects;
@Slf4j
@Component
public class QmsService {
@Resource
private RestTemplate restTemplate;
@Value("${qms.inspection.url:}")
private String qmsUrl;
public void pushIQCIncoming(List<ExternalIncomingInspectionApplyQO> qoList) {
log.info("推送来料检验申请到QMS{}", JSONUtil.toJsonStr(qoList));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<List<ExternalIncomingInspectionApplyQO>> requestEntity = new HttpEntity<>(qoList, headers);
ResponseEntity<ApiResult<Void>> response = restTemplate.exchange(
qmsUrl + "/external/incoming-inspection-task/incoming-apply",
HttpMethod.POST,
requestEntity,
new ParameterizedTypeReference<ApiResult<Void>>() {}
);
log.info("推送来料检验申请到QMS结果{},{}",
response.getStatusCode().value(), JSONUtil.toJsonStr(response.getBody()));
VUtil.trueThrowBusinessError(
Objects.isNull(response.getBody()) || response.getBody().getCode() != 200
).throwMessage("推送来料检验申请到QMS失败" + response.getBody().getMessage());
}
}

View File

@ -1,5 +1,6 @@
package com.nflg.wms.common.pojo.dto;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.util.List;
@ -11,4 +12,9 @@ public class SRMInspectionContentDTO {
private List<SRMLineVOListItem> lineVOList;
private String receiveNum;
private String receiveType;
/**
* 采购组
*/
private String purchaseGroup;
}

View File

@ -3,6 +3,7 @@ package com.nflg.wms.common.pojo.dto;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
public class SRMLineVOListItem {
@ -18,4 +19,14 @@ public class SRMLineVOListItem {
private String noteNum;
private String receivedDate;
private String inspectionFlag;
/**
* 请求单号第三方唯一编号
*/
private String requestNo;
/**
* 二维码列表
*/
private List<String> qrCodes;
}

View File

@ -23,7 +23,6 @@ public class QmsIncomingInspectionTaskTodoCheckSubmitQO {
/**
* 物料唯一编号
*/
@NotBlank(message = "物料唯一编号不能为空")
private String materialUniqueNo;
/**

View File

@ -38,6 +38,7 @@ public class QmsTodoItemServiceImpl extends ServiceImpl<QmsTodoItemMapper, QmsTo
@Override
public void add(QmsTodoItem todo) {
if (!lambdaQuery()
.eq(QmsTodoItem::getSourceTypeId, todo.getSourceTypeId())
.eq(QmsTodoItem::getSourceId, todo.getSourceId())
.eq(QmsTodoItem::getCreateUserId, todo.getCreateUserId())
.eq(QmsTodoItem::getIsRead, false)