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

View File

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

View File

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

View File

@ -45,10 +45,10 @@ public class NoScanningBaseControllerService {
* @return 是否生成过二维码 * @return 是否生成过二维码
*/ */
public boolean existsQrCode(String materialNo) { public boolean existsQrCode(String materialNo) {
// return qrCodeMasterService.lambdaQuery() return qrCodeMasterService.lambdaQuery()
// .eq(WmsQrCodeMaster::getMaterialCode, materialNo) .eq(WmsQrCodeMaster::getMaterialCode, materialNo)
// .exists(); .exists();
return false; // return false;
} }
public boolean cannotOutNoScanning(String materialNo, String factoryNo, String warehouseNo) { public boolean cannotOutNoScanning(String materialNo, String factoryNo, String warehouseNo) {
@ -66,27 +66,27 @@ public class NoScanningBaseControllerService {
* @return 数量是否一致 * @return 数量是否一致
*/ */
private boolean quantityConsistencyCheck(String materialNo, String factoryNo, String warehouseNo) { private boolean quantityConsistencyCheck(String materialNo, String factoryNo, String warehouseNo) {
// BigDecimal quantityQr = qrCodeMasterService.lambdaQuery() BigDecimal quantityQr = qrCodeMasterService.lambdaQuery()
// .select(WmsQrCodeMaster::getQuantity) .select(WmsQrCodeMaster::getQuantity)
// .eq(WmsQrCodeMaster::getMaterialCode, materialNo) .eq(WmsQrCodeMaster::getMaterialCode, materialNo)
// .eq(WmsQrCodeMaster::getFactoryCode, factoryNo) .eq(WmsQrCodeMaster::getFactoryCode, factoryNo)
// .eq(WmsQrCodeMaster::getStorageLocation, warehouseNo) .eq(WmsQrCodeMaster::getStorageLocation, warehouseNo)
// .eq(WmsQrCodeMaster::getProcessStage, BarCodeProcessStage.InBound.getState()) .eq(WmsQrCodeMaster::getProcessStage, BarCodeProcessStage.InBound.getState())
// .list() .list()
// .stream() .stream()
// .map(WmsQrCodeMaster::getQuantity) .map(WmsQrCodeMaster::getQuantity)
// .reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
// BigDecimal quantityInventory = inventoryService.lambdaQuery() BigDecimal quantityInventory = inventoryService.lambdaQuery()
// .select(WmsInventory::getNum) .select(WmsInventory::getNum)
// .eq(WmsInventory::getMaterialNo, materialNo) .eq(WmsInventory::getMaterialNo, materialNo)
// .eq(WmsInventory::getFactoryNo, factoryNo) .eq(WmsInventory::getFactoryNo, factoryNo)
// .eq(WmsInventory::getWarehouseNo, warehouseNo) .eq(WmsInventory::getWarehouseNo, warehouseNo)
// .list() .list()
// .stream() .stream()
// .map(WmsInventory::getNum) .map(WmsInventory::getNum)
// .reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
// return quantityQr.compareTo(quantityInventory) == 0; return quantityQr.compareTo(quantityInventory) == 0;
return true; // 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.SrmMaterialReceiptNoScanCodes;
import com.nflg.wms.common.pojo.document.SrmMaterialReceiptScanCodes; import com.nflg.wms.common.pojo.document.SrmMaterialReceiptScanCodes;
import com.nflg.wms.common.pojo.dto.*; 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.PDAScanCodeQO;
import com.nflg.wms.common.pojo.qo.SrmMaterialReceiptQO; import com.nflg.wms.common.pojo.qo.SrmMaterialReceiptQO;
import com.nflg.wms.common.pojo.vo.PDAOrderItemVO; import com.nflg.wms.common.pojo.vo.PDAOrderItemVO;
@ -112,6 +113,9 @@ public class NormalPGIControllerService {
@Resource @Resource
private IWmsPackageItemService wmsPackageItemService; private IWmsPackageItemService wmsPackageItemService;
@Resource
private QmsService qmsService;
/** /**
* 根据订单编号获取订单详情信息包括订单头信息和订单行项目信息 * 根据订单编号获取订单详情信息包括订单头信息和订单行项目信息
* <p> * <p>
@ -581,73 +585,72 @@ public class NormalPGIControllerService {
* @param request SRM检验输入数据传输对象包含需要推送的检验物料信息 * @param request SRM检验输入数据传输对象包含需要推送的检验物料信息
*/ */
private void pushInspectionMaterialsToSRM(SRMInspectionInputDTO request) { private void pushInspectionMaterialsToSRM(SRMInspectionInputDTO request) {
// 获取SRM系统认证Token SRMInspectionContentDTO content = request.getContent();
String token = GetSRMToken(); if (content == null || CollectionUtil.isEmpty(content.getLineVOList())) {
VUtil.trueThrowBusinessError(token.trim().isEmpty()).throwMessage("获取SRM的TOKEN失败"); return;
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()));
} }
// 重新从Redis获取Token并返回 List<ExternalIncomingInspectionApplyQO> qoList = new ArrayList<>();
srmToken = stringRedisTemplate.opsForValue().get("srm:token"); for (SRMLineVOListItem item : content.getLineVOList()) {
return srmToken; 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) { public List<PDAOrderItemVO> getPackageContents(@NotNull @NotBlank String packageCode) {
List<PackagePOItemDTO> packagePOItems = wmsPackageService.getPackagePOItems(packageCode); List<PackagePOItemDTO> packagePOItems = wmsPackageService.getPackagePOItems(packageCode);
VUtil.trueThrowBusinessError(CollectionUtil.isEmpty(packagePOItems)).throwMessage("此大包中的物料已完成收货"); 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; package com.nflg.wms.common.pojo.dto;
import jakarta.validation.constraints.NotBlank;
import lombok.Data; import lombok.Data;
import java.util.List; import java.util.List;
@ -11,4 +12,9 @@ public class SRMInspectionContentDTO {
private List<SRMLineVOListItem> lineVOList; private List<SRMLineVOListItem> lineVOList;
private String receiveNum; private String receiveNum;
private String receiveType; private String receiveType;
/**
* 采购组
*/
private String purchaseGroup;
} }

View File

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

View File

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

View File

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