Browse Source

外箱标签可连带打印内箱标签

master
han\hanst 3 days ago
parent
commit
185de1fc8e
  1. 1
      src/main/java/com/gaotao/modules/base/entity/PrintLabelRequest.java
  2. 44
      src/main/java/com/gaotao/modules/base/service/Impl/LabelDataProcessorServiceImpl.java
  3. 334
      src/main/java/com/gaotao/modules/base/service/Impl/ReportLabelListServiceImpl.java

1
src/main/java/com/gaotao/modules/base/entity/PrintLabelRequest.java

@ -29,5 +29,6 @@ public class PrintLabelRequest {
private String lineNo; // 行号 private String lineNo; // 行号
private String relNo; // 关联号 private String relNo; // 关联号
private Integer lineItemNo; // 行项目号 private Integer lineItemNo; // 行项目号
private List<String> innerBox; // 内箱标签数量列表每个元素对应一份内箱标签的qty
private Map<String, Object> customFields; // 前端补充字段视图无字段时用于标签替换 private Map<String, Object> customFields; // 前端补充字段视图无字段时用于标签替换
} }

44
src/main/java/com/gaotao/modules/base/service/Impl/LabelDataProcessorServiceImpl.java

@ -42,6 +42,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService
private static final Pattern DATA_SOURCE_PATTERN = Pattern.compile("#\\{([^}]+)\\}"); private static final Pattern DATA_SOURCE_PATTERN = Pattern.compile("#\\{([^}]+)\\}");
private static final String CUSTOM_FIELDS_CONTEXT_KEY = "__CUSTOM_FIELDS_CONTEXT__"; private static final String CUSTOM_FIELDS_CONTEXT_KEY = "__CUSTOM_FIELDS_CONTEXT__";
private static final String RESOLVED_LABEL_DATA_LIST_KEY = "__RESOLVED_LABEL_DATA_LIST__"; private static final String RESOLVED_LABEL_DATA_LIST_KEY = "__RESOLVED_LABEL_DATA_LIST__";
private static final String CACHED_IFS_VIEW_DATA_LIST_KEY = "__CACHED_IFS_VIEW_DATA_LIST__";
/** /**
* 从数据映射中取值保持原有精确匹配逻辑未命中时再从前端补充字段中按大小写不敏感匹配 * 从数据映射中取值保持原有精确匹配逻辑未命中时再从前端补充字段中按大小写不敏感匹配
@ -1962,11 +1963,19 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService
log.info("获取到主视图数据记录数: {}", mainViewDataList.size()); log.info("获取到主视图数据记录数: {}", mainViewDataList.size());
} }
// 3.2 查询IFS视图数据从IFS API获取
// 3.2 查询IFS视图数据优先复用缓存避免同一次请求重复调用IFS API
List<java.util.Map<String, Object>> ifsViewDataList = new ArrayList<>(); List<java.util.Map<String, Object>> ifsViewDataList = new ArrayList<>();
if (ifsViewSql != null && !ifsViewSql.trim().isEmpty()) { if (ifsViewSql != null && !ifsViewSql.trim().isEmpty()) {
ifsViewDataList = getIfsDataFromApi(queryParams);
log.info("从IFS API获取到数据记录数: {}", ifsViewDataList.size());
ifsViewDataList = getCachedIfsDataFromQueryParams(queryParams);
if (ifsViewDataList.isEmpty()) {
ifsViewDataList = getIfsDataFromApi(queryParams);
if (queryParams != null) {
queryParams.put(CACHED_IFS_VIEW_DATA_LIST_KEY, ifsViewDataList);
}
log.info("从IFS API获取到数据记录数: {}", ifsViewDataList.size());
} else {
log.info("复用缓存IFS数据记录数: {}", ifsViewDataList.size());
}
if (ifsViewDataList.isEmpty()) { if (ifsViewDataList.isEmpty()) {
throw new RuntimeException("ifs视图数据获取失败"); throw new RuntimeException("ifs视图数据获取失败");
} }
@ -2077,6 +2086,35 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService
return Collections.emptyMap(); return Collections.emptyMap();
} }
/**
* 从queryParams提取缓存的IFS数据
*/
private List<Map<String, Object>> getCachedIfsDataFromQueryParams(Map<String, Object> queryParams) {
if (queryParams == null) {
return Collections.emptyList();
}
Object cachedIfsDataObj = queryParams.get(CACHED_IFS_VIEW_DATA_LIST_KEY);
if (!(cachedIfsDataObj instanceof List<?> cachedIfsDataList) || cachedIfsDataList.isEmpty()) {
return Collections.emptyList();
}
List<Map<String, Object>> normalizedResult = new ArrayList<>();
for (Object rowObj : cachedIfsDataList) {
if (!(rowObj instanceof Map<?, ?> rowMap)) {
continue;
}
Map<String, Object> normalizedRow = new HashMap<>();
for (Map.Entry<?, ?> entry : rowMap.entrySet()) {
if (entry.getKey() != null) {
normalizedRow.put(entry.getKey().toString(), entry.getValue());
}
}
if (!normalizedRow.isEmpty()) {
normalizedResult.add(normalizedRow);
}
}
return normalizedResult;
}
/** /**
* 判断是否为系统变量 * 判断是否为系统变量
* *

334
src/main/java/com/gaotao/modules/base/service/Impl/ReportLabelListServiceImpl.java

@ -692,32 +692,13 @@ public class ReportLabelListServiceImpl extends ServiceImpl<ReportLabelListMappe
*/ */
@Override @Override
public void printLabelCommon(PrintLabelRequest printRequest) { public void printLabelCommon(PrintLabelRequest printRequest) {
String rfidFlag;
// 当前端传了customerId时,根据客户编码解析标签编号 // 当前端传了customerId时,根据客户编码解析标签编号
String resolvedReportId = resolveReportIdByCustomer(printRequest); String resolvedReportId = resolveReportIdByCustomer(printRequest);
LabelSettingData labelSettingData = new LabelSettingData();
labelSettingData.setLabelNo(resolvedReportId);
labelSettingData.setLabelType(printRequest.getLabelType());
List<LabelSettingData> labelSettingDataList = baseService.getLabelSettingList(labelSettingData);
LabelSettingData labelSetting;
if (labelSettingDataList.isEmpty()) {
throw new RuntimeException("未找到对应的标签");
} else {
labelSetting = labelSettingDataList.getFirst();
rfidFlag = labelSetting.getRfidFlag();
}
LabelSettingData outerLabelSetting = getRequiredLabelSetting(resolvedReportId, printRequest.getLabelType());
String outerRfidFlag = outerLabelSetting.getRfidFlag();
try { try {
// 1. 构建查询参数Map // 1. 构建查询参数Map
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("site", printRequest.getSite());
List<String> unitIds = printRequest.getUnitIds();
queryParams.put("unitIds", unitIds);
// ifs外箱标签视图
queryParams.put("consignmentId", printRequest.getConsignmentId());
queryParams.put("jobNo", printRequest.getJobNo());
queryParams.put("lineNo", printRequest.getLineNo());
queryParams.put("relNo", printRequest.getRelNo());
queryParams.put("lineItemNo", printRequest.getLineItemNo());
Map<String, Object> queryParams = buildCommonQueryParams(printRequest);
/* 添加自定义字段 前端补充字段视图无字段时用于标签替换比如标签有一个元素是#{qty}是视图里不存在的前端传入 /* 添加自定义字段 前端补充字段视图无字段时用于标签替换比如标签有一个元素是#{qty}是视图里不存在的前端传入
{ {
"reportId": "xxx", "reportId": "xxx",
@ -725,6 +706,7 @@ public class ReportLabelListServiceImpl extends ServiceImpl<ReportLabelListMappe
"site": "55", "site": "55",
"userId": "admin", "userId": "admin",
"unitIds": ["A5500000001"], "unitIds": ["A5500000001"],
"innerBox": ["20","20","20","20","20"],// 内箱列表系统会根据外箱标签找到对应的内箱子标签这里的例子20就是每份数量指的就是内箱标签customFields里的qty
"consignmentId": "1773845", "consignmentId": "1773845",
"jobNo": "71330430", "jobNo": "71330430",
"lineNo": "19", "lineNo": "19",
@ -744,68 +726,288 @@ public class ReportLabelListServiceImpl extends ServiceImpl<ReportLabelListMappe
queryParams.put("customFields", customFields); queryParams.put("customFields", customFields);
} }
// 可以根据需要添加更多参数 // 可以根据需要添加更多参数
// 生成带真实数据的ZPL代码和对应数据
List<String> zplCodeList = previewLabelWithRealData(labelSetting.getLabelNo(), true, queryParams);
// 2. 生成外箱标签带真实数据的ZPL代码和对应数据
List<String> zplCodeList = previewLabelWithRealData(outerLabelSetting.getLabelNo(), true, queryParams);
// 同时获取原始数据用于保存WMS标签记录 // 同时获取原始数据用于保存WMS标签记录
List<Map<String, Object>> labelDataList = getLabelDataForPrint(labelSetting.getLabelNo(), queryParams);
// 2. 验证ZPL代码
List<Map<String, Object>> labelDataList = getLabelDataForPrint(outerLabelSetting.getLabelNo(), queryParams);
// 3. 验证ZPL代码
if (zplCodeList == null || zplCodeList.isEmpty()) { if (zplCodeList == null || zplCodeList.isEmpty()) {
throw new RuntimeException("ZPL代码获取失败"); throw new RuntimeException("ZPL代码获取失败");
} }
log.info("生成了 {} 个ZPL代码,准备打印", zplCodeList.size());
// 3. 获取用户的打印机配置
UserLabelPrinterData printerQuery = new UserLabelPrinterData();
printerQuery.setUserId(printRequest.getUserId());
printerQuery.setLabelNo(labelSetting.getLabelNo());
List<UserLabelPrinterData> printers = baseService.getUserLabelPrinters(printerQuery);
if (printers.isEmpty()) {
throw new RuntimeException("未找到指定的打印机配置");
}
UserLabelPrinterData printer = printers.getFirst();
String printerIP = printer.getIpAddress();
if (printerIP == null || printerIP.trim().isEmpty()) {
throw new RuntimeException("打印机IP地址未配置");
}
// 4. 处理打印份数
int requestedCopies = 1;
log.info("生成了 {} 个外箱标签ZPL代码,准备打印", zplCodeList.size());
// 5. 将所有打印任务添加到队列中不直接打印
// 4. 获取外箱标签打印机配置
UserLabelPrinterData outerPrinter = getRequiredLabelPrinter(printRequest.getUserId(), outerLabelSetting.getLabelNo());
// 5. 将外箱打印任务加入队列不直接打印
int totalLabels = zplCodeList.size(); int totalLabels = zplCodeList.size();
List<Long> taskIds = new ArrayList<>(); List<Long> taskIds = new ArrayList<>();
for (int i = 0; i < totalLabels; i++) { for (int i = 0; i < totalLabels; i++) {
String zplCode = zplCodeList.get(i); String zplCode = zplCodeList.get(i);
Map<String, Object> labelData = i < labelDataList.size() ? labelDataList.get(i) : new HashMap<>(); Map<String, Object> labelData = i < labelDataList.size() ? labelDataList.get(i) : new HashMap<>();
// 如果是RFID标签在这里组装完整的RFID ZPL包含写EPC指令
if ("Y".equals(rfidFlag)) {
zplCode = buildRfidZpl(zplCode, labelData);
log.info("已组装RFID ZPL(通用),unit_id={}", labelData.get("unit_id"));
}
// 添加到打印任务队列
Long taskId = printTaskService.addPrintTask(
printerIP,
zplCode,
rfidFlag,
requestedCopies,
labelData,
printRequest.getSite() != null ? printRequest.getSite() : "DEFAULT",
printRequest.getUserId() != null ? printRequest.getUserId().toString() : "SYSTEM"
);
Long taskId = enqueueSingleTask(outerPrinter.getIpAddress(), zplCode, outerRfidFlag, labelData, printRequest);
taskIds.add(taskId); taskIds.add(taskId);
log.info("打印任务已加入队列 {} / {}, taskId={}", i + 1, totalLabels, taskId); log.info("打印任务已加入队列 {} / {}, taskId={}", i + 1, totalLabels, taskId);
} }
// 6. 记录日志
log.info("✓ 所有打印任务已加入队列,共 {} 个任务,任务ID: {}", totalLabels, taskIds);
// 6. customerId + innerBox 场景在外箱任务后自动补打对应内箱子标签
enqueueInnerBoxTasksIfNeeded(printRequest, resolvedReportId, queryParams, outerPrinter, taskIds);
// 7. 记录日志
log.info("✓ 所有打印任务已加入队列,共 {} 个任务,任务ID: {}", taskIds.size(), taskIds);
} catch (Exception e) { } catch (Exception e) {
System.err.println("RFID标签打印失败: " + e.getMessage()); System.err.println("RFID标签打印失败: " + e.getMessage());
throw new RuntimeException("RFID标签打印失败: " + e.getMessage()); throw new RuntimeException("RFID标签打印失败: " + e.getMessage());
} }
} }
/**
* 构建通用打印查询参数
*/
private Map<String, Object> buildCommonQueryParams(PrintLabelRequest printRequest) {
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("site", printRequest.getSite());
queryParams.put("unitIds", printRequest.getUnitIds());
// ifs外箱标签视图
queryParams.put("consignmentId", printRequest.getConsignmentId());
queryParams.put("jobNo", printRequest.getJobNo());
queryParams.put("lineNo", printRequest.getLineNo());
queryParams.put("relNo", printRequest.getRelNo());
queryParams.put("lineItemNo", printRequest.getLineItemNo());
return queryParams;
}
/**
* 获取标签设置labelType为空时仅按labelNo匹配
*/
private LabelSettingData getRequiredLabelSetting(String labelNo, String labelType) {
LabelSettingData query = new LabelSettingData();
query.setLabelNo(labelNo);
if (StringUtils.isNotBlank(labelType)) {
query.setLabelType(labelType);
}
List<LabelSettingData> labelSettingList = baseService.getLabelSettingList(query);
if (labelSettingList == null || labelSettingList.isEmpty()) {
throw new RuntimeException("未找到对应的标签: " + labelNo);
}
return labelSettingList.getFirst();
}
/**
* 获取用户对应标签的打印机配置
*/
private UserLabelPrinterData getRequiredLabelPrinter(String userId, String labelNo) {
UserLabelPrinterData printerQuery = new UserLabelPrinterData();
printerQuery.setUserId(userId);
printerQuery.setLabelNo(labelNo);
List<UserLabelPrinterData> printers = baseService.getUserLabelPrinters(printerQuery);
if (printers == null || printers.isEmpty()) {
throw new RuntimeException("未找到指定的打印机配置: labelNo=" + labelNo);
}
UserLabelPrinterData printer = printers.getFirst();
if (StringUtils.isBlank(printer.getIpAddress())) {
throw new RuntimeException("打印机IP地址未配置: labelNo=" + labelNo);
}
return printer;
}
/**
* 添加单个打印任务到队列内部统一处理RFID逻辑
*/
private Long enqueueSingleTask(String printerIP, String zplCode, String rfidFlag,
Map<String, Object> labelData, PrintLabelRequest printRequest) {
String resolvedZpl = zplCode;
if ("Y".equals(rfidFlag)) {
resolvedZpl = buildRfidZpl(zplCode, labelData);
log.info("已组装RFID ZPL(通用),unit_id={}", labelData.get("unit_id"));
}
return printTaskService.addPrintTask(
printerIP,
resolvedZpl,
rfidFlag,
1,
labelData,
printRequest.getSite() != null ? printRequest.getSite() : "DEFAULT",
printRequest.getUserId() != null ? printRequest.getUserId() : "SYSTEM"
);
}
/**
* customerId + innerBox 场景打印外箱标签对应的内箱子标签
*/
private void enqueueInnerBoxTasksIfNeeded(PrintLabelRequest printRequest,
String outerLabelNo,
Map<String, Object> baseQueryParams,
UserLabelPrinterData outerPrinter,
List<Long> taskIds) {
if (StringUtils.isBlank(printRequest.getCustomerId())) {
return;
}
List<String> innerBoxList = printRequest.getInnerBox();
if (innerBoxList == null || innerBoxList.isEmpty()) {
return;
}
String innerLabelNo = resolveInnerLabelByCustomer(printRequest, outerLabelNo);
if (StringUtils.isBlank(innerLabelNo)) {
throw new RuntimeException("未找到外箱标签对应的内箱标签配置,outerLabelNo=" + outerLabelNo);
}
LabelSettingData innerLabelSetting = getRequiredLabelSetting(innerLabelNo, null);
UserLabelPrinterData innerPrinter = resolveInnerPrinter(printRequest.getUserId(), innerLabelNo, outerPrinter);
Map<String, Object> innerQueryParams = new HashMap<>(baseQueryParams);
int innerIndex = 1;
for (String innerQty : innerBoxList) {
if (StringUtils.isBlank(innerQty)) {
continue;
}
Map<String, Object> innerCustomFields = buildInnerCustomFields(printRequest.getCustomFields(), innerQty.trim());
innerQueryParams.put("customFields", innerCustomFields);
// innerBox 的每个元素代表一份内箱标签qty由当前元素覆盖
List<String> innerZplList = previewLabelWithRealData(innerLabelNo, true, innerQueryParams);
if (innerZplList == null || innerZplList.isEmpty()) {
throw new RuntimeException("内箱标签ZPL代码获取失败,innerQty=" + innerQty);
}
List<Map<String, Object>> innerLabelDataList = getLabelDataForPrint(innerLabelNo, innerQueryParams);
Map<String, Object> innerLabelData = innerLabelDataList != null && !innerLabelDataList.isEmpty()
? new HashMap<>(innerLabelDataList.getFirst()) : new HashMap<>();
if (innerZplList.size() > 1) {
log.warn("内箱标签生成了多条数据,本次按第一条打印。innerQty={}, count={}", innerQty, innerZplList.size());
}
Long taskId = enqueueSingleTask(
innerPrinter.getIpAddress(),
innerZplList.getFirst(),
innerLabelSetting.getRfidFlag(),
innerLabelData,
printRequest
);
taskIds.add(taskId);
log.info("内箱标签任务已加入队列 {} / {}, qty={}, taskId={}",
innerIndex, innerBoxList.size(), innerQty, taskId);
innerIndex++;
}
}
/**
* 解析内箱标签打印机优先取内箱标签专属打印机未配置则沿用外箱打印机
*/
private UserLabelPrinterData resolveInnerPrinter(String userId, String innerLabelNo, UserLabelPrinterData outerPrinter) {
try {
return getRequiredLabelPrinter(userId, innerLabelNo);
} catch (Exception ex) {
log.warn("内箱标签未配置独立打印机,沿用外箱打印机。innerLabelNo={}, reason={}", innerLabelNo, ex.getMessage());
return outerPrinter;
}
}
/**
* 构建内箱标签 customFields保留原字段并覆盖 qty
*/
private Map<String, Object> buildInnerCustomFields(Map<String, Object> sourceCustomFields, String innerQty) {
Map<String, Object> innerCustomFields = new HashMap<>();
if (sourceCustomFields != null && !sourceCustomFields.isEmpty()) {
innerCustomFields.putAll(sourceCustomFields);
}
boolean qtyUpdated = false;
for (String key : new ArrayList<>(innerCustomFields.keySet())) {
if (StringUtils.equalsIgnoreCase(StringUtils.trimToEmpty(key), "qty")) {
innerCustomFields.put(key, innerQty);
qtyUpdated = true;
}
}
if (!qtyUpdated) {
innerCustomFields.put("qty", innerQty);
}
return innerCustomFields;
}
/**
* 根据外箱标签解析对应内箱标签
*/
private String resolveInnerLabelByCustomer(PrintLabelRequest printRequest, String outerLabelNo) {
String customerId = StringUtils.trimToNull(printRequest.getCustomerId());
if (StringUtils.isBlank(customerId) || StringUtils.isBlank(outerLabelNo)) {
return null;
}
String addressNo = StringUtils.trimToNull(printRequest.getAddressNo());
try {
CustomerLabelSettingData query = new CustomerLabelSettingData();
query.setSearchFlag("Y");
query.setCustomerId(customerId.trim());
// 不在SQL层限定addressNo保留地址精确匹配 -> 客户通用地址回退的业务顺序
List<CustomerLabelSettingData> customerLabelList = baseService.getCustomerLabelSettingList(query);
if (customerLabelList == null || customerLabelList.isEmpty()) {
return null;
}
CustomerLabelSettingData exactInnerLabel = null;
CustomerLabelSettingData exactAnyLabel = null;
CustomerLabelSettingData fallbackInnerLabel = null;
CustomerLabelSettingData fallbackAnyLabel = null;
for (CustomerLabelSettingData item : customerLabelList) {
if (item == null || StringUtils.isBlank(item.getLabelNo())) {
continue;
}
if (StringUtils.isNotBlank(printRequest.getSite())
&& !StringUtils.equalsIgnoreCase(printRequest.getSite(), item.getSite())) {
continue;
}
if (!"Y".equalsIgnoreCase(StringUtils.trimToEmpty(item.getSubLabelFlag()))) {
continue;
}
if (!StringUtils.equalsIgnoreCase(StringUtils.trimToEmpty(item.getParentLabelNo()), outerLabelNo)) {
continue;
}
boolean isInnerLabelType = "内箱标签".equals(StringUtils.trimToEmpty(item.getLabelType()));
String itemAddressNo = StringUtils.trimToNull(item.getAddressNo());
if (StringUtils.isNotBlank(addressNo)) {
if (StringUtils.equalsIgnoreCase(addressNo, itemAddressNo)) {
if (isInnerLabelType) {
exactInnerLabel = item;
break;
}
if (exactAnyLabel == null) {
exactAnyLabel = item;
}
continue;
}
if (StringUtils.isBlank(itemAddressNo)) {
if (isInnerLabelType && fallbackInnerLabel == null) {
fallbackInnerLabel = item;
}
if (fallbackAnyLabel == null) {
fallbackAnyLabel = item;
}
}
continue;
}
if (isInnerLabelType) {
exactInnerLabel = item;
break;
}
if (exactAnyLabel == null) {
exactAnyLabel = item;
}
}
CustomerLabelSettingData targetLabel = exactInnerLabel != null ? exactInnerLabel
: (exactAnyLabel != null ? exactAnyLabel
: (fallbackInnerLabel != null ? fallbackInnerLabel : fallbackAnyLabel));
return targetLabel != null ? targetLabel.getLabelNo() : null;
} catch (Exception e) {
log.warn("根据外箱标签解析内箱标签失败,customerId={}, outerLabelNo={}, error={}",
customerId, outerLabelNo, e.getMessage());
return null;
}
}
/** /**
* 文本日期元素是否启用流水号后缀 * 文本日期元素是否启用流水号后缀
*/ */
@ -905,9 +1107,7 @@ public class ReportLabelListServiceImpl extends ServiceImpl<ReportLabelListMappe
query.setSearchFlag("Y"); query.setSearchFlag("Y");
query.setCustomerId(customerId.trim()); query.setCustomerId(customerId.trim());
query.setLabelType(printRequest.getLabelType()); query.setLabelType(printRequest.getLabelType());
if (StringUtils.isNotBlank(addressNo)) {
query.setAddressNo(addressNo);
}
// 不在SQL层限定addressNo保留地址精确匹配 -> 客户通用地址回退的业务顺序
List<CustomerLabelSettingData> customerLabelList = baseService.getCustomerLabelSettingList(query); List<CustomerLabelSettingData> customerLabelList = baseService.getCustomerLabelSettingList(query);
if (customerLabelList == null || customerLabelList.isEmpty()) { if (customerLabelList == null || customerLabelList.isEmpty()) {
log.warn("客户标签映射未找到,回退前端reportId。customerId={}, addressNo={}, labelType={}, reportId={}", log.warn("客户标签映射未找到,回退前端reportId。customerId={}, addressNo={}, labelType={}, reportId={}",

Loading…
Cancel
Save