package com.vincent.rsf.server.api.service.impl;
|
|
import com.vincent.rsf.server.api.config.RemotesInfoProperties;
|
import com.vincent.rsf.server.api.controller.erp.params.DapIlcwmsCompletionLine;
|
import com.vincent.rsf.server.api.controller.erp.params.DapIlcwmsCompletionRequest;
|
import com.vincent.rsf.server.api.controller.erp.params.InOutResultReportParam;
|
import com.vincent.rsf.server.api.controller.erp.params.InventoryAdjustReportParam;
|
import com.vincent.rsf.server.api.feign.CloudWmsErpFeignClient;
|
import com.vincent.rsf.server.api.integration.dap.DapIlcwmsResponseNormalizer;
|
import com.vincent.rsf.server.api.service.CloudWmsReportService;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.lang3.StringUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
/**
|
* 立库侧请求云仓:ICusStockService 入库/出库/调拨完成反馈;9.2 增减调整仍为 /api/report/inventoryAdjust,报文同为 {data:[]}。
|
*/
|
@Slf4j
|
@Service
|
public class CloudWmsReportServiceImpl implements CloudWmsReportService {
|
|
/** 云仓回馈:通知单 orgNo 为空时的默认组织 */
|
private static final String DEFAULT_CLOUD_ORG_NO = "1";
|
/** 云仓回馈:通知单 inWarehouseNo / outWarehouseNo 为空时的默认仓编码 */
|
private static final String DEFAULT_CLOUD_WH_NO = "101";
|
|
@Autowired
|
private RemotesInfoProperties erpApi;
|
|
@Autowired
|
private CloudWmsErpFeignClient cloudWmsErpFeignClient;
|
@Autowired
|
private ObjectMapper objectMapper;
|
|
@Override
|
public Map<String, Object> syncMatnrsToCloud(Object body) {
|
if (!isCloudWmsConfigured()) {
|
log.warn("ErpApi(云仓WMS) 未配置 host/base-url,跳过物料基础信息同步");
|
return stubWithoutUpstream("云仓地址未配置,未实际同步");
|
}
|
return cloudWmsErpFeignClient.syncMatnrs(body != null ? body : new HashMap<>());
|
}
|
|
@Override
|
public Map<String, Object> reportInOutResult(InOutResultReportParam param) {
|
if (param == null) {
|
return resultMap(400, "参数不能为空", null);
|
}
|
if (!isCloudWmsConfigured()) {
|
log.warn("ErpApi(云仓WMS) 未配置 host/base-url,跳过 9.1 入/出库结果上报,订单:{}", param.getOrderNo());
|
return stubWithoutUpstream("云仓地址未配置,未实际上报");
|
}
|
boolean inbound = param.getInbound() == null || Boolean.TRUE.equals(param.getInbound());
|
DapIlcwmsCompletionRequest req = new DapIlcwmsCompletionRequest()
|
.setData(Collections.singletonList(buildInOutLine(param, inbound)));
|
logOutboundPayload("IN_OUT_RESULT", inbound ? "stockInCompleted" : "stockOutCompleted", req);
|
Map<String, Object> raw = inbound
|
? cloudWmsErpFeignClient.cusInventoryCompletionReport(req)
|
: cloudWmsErpFeignClient.cusOutboundCompletionReport(req);
|
return DapIlcwmsResponseNormalizer.toNotifyFormat(raw);
|
}
|
|
@Override
|
public Map<String, Object> reportInOutResults(List<InOutResultReportParam> lines) {
|
if (lines == null || lines.isEmpty()) {
|
return resultMap(400, "明细不能为空", null);
|
}
|
if (!isCloudWmsConfigured()) {
|
log.warn("ErpApi(云仓WMS) 未配置 host/base-url,跳过 9.1 入出库合并上报");
|
return stubWithoutUpstream("云仓地址未配置,未实际上报");
|
}
|
InOutResultReportParam first = lines.get(0);
|
boolean inbound = first.getInbound() == null || Boolean.TRUE.equals(first.getInbound());
|
for (InOutResultReportParam param : lines) {
|
boolean rowIn = param.getInbound() == null || Boolean.TRUE.equals(param.getInbound());
|
if (rowIn != inbound) {
|
return resultMap(400, "合并上报须同为入库或同为出库", null);
|
}
|
}
|
List<DapIlcwmsCompletionLine> data = new ArrayList<>(lines.size());
|
for (InOutResultReportParam param : lines) {
|
data.add(buildInOutLine(param, inbound));
|
}
|
DapIlcwmsCompletionRequest req = new DapIlcwmsCompletionRequest().setData(data);
|
logOutboundPayload("IN_OUT_RESULT_BATCH", inbound ? "stockInCompleted" : "stockOutCompleted", req);
|
Map<String, Object> raw = inbound
|
? cloudWmsErpFeignClient.cusInventoryCompletionReport(req)
|
: cloudWmsErpFeignClient.cusOutboundCompletionReport(req);
|
return DapIlcwmsResponseNormalizer.toNotifyFormat(raw);
|
}
|
|
@Override
|
public Map<String, Object> reportInventoryAdjust(InventoryAdjustReportParam param) {
|
if (param == null) {
|
return resultMap(400, "参数不能为空", null);
|
}
|
if (!isCloudWmsConfigured()) {
|
log.warn("ErpApi(云仓WMS) 未配置 host/base-url,跳过 9.2 库存调整上报,物料:{}", param.getMatNr());
|
return stubWithoutUpstream("云仓地址未配置,未实际上报");
|
}
|
Integer changeType = param.getChangeType();
|
if (changeType == null) {
|
return resultMap(400, "changeType 不能为空", null);
|
}
|
DapIlcwmsCompletionRequest req = new DapIlcwmsCompletionRequest();
|
if (changeType == 3) {
|
String baseSeq = StringUtils.isNotBlank(param.getDocSeqNo()) ? param.getDocSeqNo() : "1";
|
List<DapIlcwmsCompletionLine> lines = new ArrayList<>(2);
|
lines.add(buildAdjustLine(param, false, true, baseSeq + "-O"));
|
lines.add(buildAdjustLine(param, true, false, baseSeq + "-I"));
|
req.setData(lines);
|
} else if (changeType == 1) {
|
req.setData(Collections.singletonList(buildAdjustLine(param, true, false, null)));
|
} else if (changeType == 2) {
|
req.setData(Collections.singletonList(buildAdjustLine(param, false, true, null)));
|
} else {
|
return resultMap(400, "不支持的 changeType:" + changeType, null);
|
}
|
logOutboundPayload("INVENTORY_ADJUST", changeType == 3 ? "stockTransferCompleted" : "reportInventoryAdjust", req);
|
Map<String, Object> raw = changeType == 3
|
? cloudWmsErpFeignClient.stockTransferCompleted(req)
|
: cloudWmsErpFeignClient.reportInventoryAdjust(req);
|
return changeType == 3
|
? DapIlcwmsResponseNormalizer.toNotifyFormat(raw)
|
: DapIlcwmsResponseNormalizer.toNotifyFormatFlexible(raw);
|
}
|
|
private DapIlcwmsCompletionLine buildInOutLine(InOutResultReportParam param, boolean inbound) {
|
CloudMatnrParts matnrParts = parseCloudMatnr(param.getMatNr());
|
String docType = StringUtils.isNotBlank(param.getWkType())
|
? param.getWkType()
|
: (inbound ? "IN" : "OUT");
|
String unitNo = StringUtils.isNotBlank(param.getUnitNo()) ? param.getUnitNo().trim() : "PCS";
|
String orgNoLine = StringUtils.isNotBlank(param.getOrgNo()) ? param.getOrgNo().trim() : DEFAULT_CLOUD_ORG_NO;
|
String docWh = StringUtils.trimToNull(param.getDocWarehouseNo());
|
String inWhLine = StringUtils.isNotBlank(param.getInWarehouseNo()) ? param.getInWarehouseNo().trim() : DEFAULT_CLOUD_WH_NO;
|
String outWhLine = StringUtils.isNotBlank(param.getOutWarehouseNo()) ? param.getOutWarehouseNo().trim() : DEFAULT_CLOUD_WH_NO;
|
DapIlcwmsCompletionLine line = new DapIlcwmsCompletionLine()
|
.setOrgNo(orgNoLine)
|
.setDocWarehouseNo(docWh)
|
.setDocType(docType)
|
.setDocNo(param.getOrderNo())
|
.setDocSeqNo(StringUtils.isNotBlank(param.getLineId()) ? param.getLineId() : "1")
|
// 按云仓规则拆分物料编码
|
.setItemNo(matnrParts.getItemNo())
|
.setQty(parseQty(param.getQty()))
|
.setUnitNo(unitNo)
|
.setCombinationLotNo(matnrParts.getCombinationLotNo())
|
.setBarcode(matnrParts.getBarcode());
|
if (inbound) {
|
line.setInWarehouseNo(inWhLine).setInCellNo(inWhLine);
|
// line.setInWarehouseNo(inWhLine).setInCellNo(param.getLocId());
|
} else {
|
line.setOutWarehouseNo(outWhLine).setOutCellNo(outWhLine);
|
// line.setOutWarehouseNo(outWhLine).setOutCellNo(param.getLocId());
|
}
|
return line;
|
}
|
|
/**
|
* @param fillIn 是否填入库储位
|
* @param fillOut 是否填出库储位
|
* @param docSeqOverride 非空时用作项次(移库第二行等)
|
*/
|
private DapIlcwmsCompletionLine buildAdjustLine(InventoryAdjustReportParam param, boolean fillIn, boolean fillOut, String docSeqOverride) {
|
String docType = resolveAdjustDocType(param);
|
String docNo = StringUtils.isNotBlank(param.getDocNo()) ? param.getDocNo() : "ADJ";
|
String docSeq = docSeqOverride != null ? docSeqOverride
|
: (StringUtils.isNotBlank(param.getDocSeqNo()) ? param.getDocSeqNo() : "1");
|
String unit = StringUtils.isNotBlank(param.getUnitNo()) ? param.getUnitNo() : "PCS";
|
CloudMatnrParts matnrParts = parseCloudMatnr(param.getMatNr());
|
DapIlcwmsCompletionLine line = new DapIlcwmsCompletionLine()
|
.setOrgNo(DEFAULT_CLOUD_ORG_NO)
|
.setDocType(docType)
|
.setDocNo(docNo)
|
.setDocSeqNo(docSeq)
|
// 按云仓规则拆分物料编码
|
.setItemNo(matnrParts.getItemNo())
|
.setQty(parseQty(param.getQty()))
|
.setUnitNo(unit)
|
.setCombinationLotNo(matnrParts.getCombinationLotNo())
|
.setBarcode(matnrParts.getBarcode());
|
if (fillIn) {
|
line.setInWarehouseNo(param.getWareHouseId());
|
line.setInCellNo(StringUtils.isNotBlank(param.getTargetLocId()) ? param.getTargetLocId() : param.getSourceLocId());
|
}
|
if (fillOut) {
|
line.setOutWarehouseNo(param.getWareHouseId());
|
line.setOutCellNo(StringUtils.isNotBlank(param.getSourceLocId()) ? param.getSourceLocId() : param.getTargetLocId());
|
}
|
return line;
|
}
|
|
private static String resolveAdjustDocType(InventoryAdjustReportParam param) {
|
if (StringUtils.isNotBlank(param.getDocType())) {
|
return param.getDocType();
|
}
|
Integer ct = param.getChangeType();
|
if (ct != null && ct == 2) {
|
return "OUT";
|
}
|
if (ct != null && ct == 3) {
|
return "ADJ";
|
}
|
return "IN";
|
}
|
|
// private static String resolveBarcode(InOutResultReportParam param) {
|
// if (StringUtils.isNotBlank(param.getBarcode())) {
|
// return param.getBarcode();
|
// }
|
// if (StringUtils.isNotBlank(param.getPalletId())) {
|
// return param.getPalletId();
|
// }
|
// if (param.getMatNr() != null && param.getLocId() != null) {
|
// return param.getMatNr() + ":" + param.getLocId();
|
// }
|
// return param.getMatNr();
|
// }
|
//
|
// private static String resolveAdjustBarcode(InventoryAdjustReportParam param) {
|
// if (StringUtils.isNotBlank(param.getBarcode())) {
|
// return param.getBarcode();
|
// }
|
// if (StringUtils.isNotBlank(param.getPalletId())) {
|
// return param.getPalletId();
|
// }
|
// return param.getMatNr();
|
// }
|
|
/**
|
* 云仓回报字段映射:三段式取前两段,barcode 保留原串。
|
*/
|
private static CloudMatnrParts parseCloudMatnr(String matNr) {
|
if (StringUtils.isBlank(matNr)) {
|
return new CloudMatnrParts(null, null, null);
|
}
|
String[] arr = matNr.split("#", -1);
|
if (arr.length >= 3) {
|
return new CloudMatnrParts(arr[0], arr[1], matNr);
|
}
|
return new CloudMatnrParts(matNr, null, matNr);
|
}
|
|
private static class CloudMatnrParts {
|
private final String itemNo;
|
private final String combinationLotNo;
|
private final String barcode;
|
|
private CloudMatnrParts(String itemNo, String combinationLotNo, String barcode) {
|
this.itemNo = itemNo;
|
this.combinationLotNo = combinationLotNo;
|
this.barcode = barcode;
|
}
|
|
public String getItemNo() {
|
return itemNo;
|
}
|
|
public String getCombinationLotNo() {
|
return combinationLotNo;
|
}
|
|
public String getBarcode() {
|
return barcode;
|
}
|
}
|
|
private static Double parseQty(String q) {
|
if (StringUtils.isBlank(q)) {
|
return 0d;
|
}
|
try {
|
return Double.parseDouble(q.trim());
|
} catch (NumberFormatException e) {
|
return 0d;
|
}
|
}
|
|
private boolean isCloudWmsConfigured() {
|
String host = erpApi.getHost();
|
if (host != null && !host.trim().isEmpty()) {
|
return true;
|
}
|
String baseUrl = erpApi.getBaseUrl();
|
return baseUrl != null && !baseUrl.trim().isEmpty();
|
}
|
|
/** 未走云仓 HTTP,code 非 200,避免调度误判成功 */
|
private Map<String, Object> stubWithoutUpstream(String msg) {
|
Map<String, Object> data = new HashMap<>();
|
data.put("result", "SKIPPED");
|
return resultMap(503, msg, data);
|
}
|
|
private Map<String, Object> resultMap(int code, String msg, Map<String, Object> data) {
|
Map<String, Object> map = new HashMap<>();
|
map.put("code", code);
|
map.put("msg", msg);
|
map.put("data", data);
|
return map;
|
}
|
|
private void logOutboundPayload(String reportType, String endpoint, DapIlcwmsCompletionRequest req) {
|
try {
|
log.info("云仓真实请求报文,reportType={},endpoint={},payload={}",
|
reportType, endpoint, objectMapper.writeValueAsString(req));
|
} catch (Exception e) {
|
log.warn("云仓真实请求报文序列化失败,reportType={},endpoint={}:{}",
|
reportType, endpoint, e.getMessage());
|
}
|
}
|
}
|