|
|
|
@ -167,9 +167,23 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 第二轮处理:处理元素组合引用 |
|
|
|
// 用原始模板定位元素,用第一轮的结果提供值;加工后内容不能再作为引用标识。 |
|
|
|
Map<String, ReportLabelList> referenceElements = new LinkedHashMap<>(); |
|
|
|
Map<ReportLabelList, ReportLabelList> resolvedElements = new IdentityHashMap<>(); |
|
|
|
Set<ReportLabelList> resolvingElements = Collections.newSetFromMap(new IdentityHashMap<>()); |
|
|
|
for (int i = 0; i < elements.size(); i++) { |
|
|
|
ReportLabelList original = elements.get(i); |
|
|
|
ReportLabelList processed = firstPassElements.get(i); |
|
|
|
referenceElements.put(getElementDisplayName(original), processed); |
|
|
|
if (!containsElementReferences(original.getData())) { |
|
|
|
resolvedElements.put(processed, processed); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 第二轮处理:组合只读取已经加工的值,不再次截取、替换或生成流水号。 |
|
|
|
for (ReportLabelList element : firstPassElements) { |
|
|
|
ReportLabelList finalElement = processElementCombinations(element, enhancedDataMap, firstPassElements, printFlag); |
|
|
|
ReportLabelList finalElement = processElementCombinations(element, enhancedDataMap, |
|
|
|
firstPassElements, referenceElements, resolvedElements, resolvingElements, printFlag); |
|
|
|
|
|
|
|
// 如果元素被设置为不显示,则跳过该元素 |
|
|
|
if (("onecode".equals(element.getType()) || "qrcode".equals(element.getType()) |
|
|
|
@ -195,6 +209,12 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService |
|
|
|
|
|
|
|
log.debug("处理条码元素: type={}, originalData={}", element.getType(), originalData); |
|
|
|
|
|
|
|
// 保留组合引用中的原始字段模板,待所有来源元素完成加工后再解析。 |
|
|
|
// 否则 {文本(#{FIELD})} 会提前变成 {文本(原始值)},无法匹配加工后的元素。 |
|
|
|
if (containsElementReferences(originalData)) { |
|
|
|
return originalData; |
|
|
|
} |
|
|
|
|
|
|
|
// 检查是否包含流水号引用(各种可能的格式) |
|
|
|
if (containsSerialNumberReference(originalData)) { |
|
|
|
log.debug("检测到流水号引用,开始处理"); |
|
|
|
@ -316,35 +336,45 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService |
|
|
|
* 处理元素组合引用 |
|
|
|
*/ |
|
|
|
private ReportLabelList processElementCombinations(ReportLabelList element, Map<String, Object> dataMap, |
|
|
|
List<ReportLabelList> allElements,Boolean printFlag) { |
|
|
|
List<ReportLabelList> allElements, |
|
|
|
Map<String, ReportLabelList> referenceElements, |
|
|
|
Map<ReportLabelList, ReportLabelList> resolvedElements, |
|
|
|
Set<ReportLabelList> resolvingElements, Boolean printFlag) { |
|
|
|
if (resolvedElements.containsKey(element)) { |
|
|
|
return resolvedElements.get(element); |
|
|
|
} |
|
|
|
if (element == null || element.getData() == null || element.getData().isEmpty()) { |
|
|
|
return element; |
|
|
|
} |
|
|
|
if (!resolvingElements.add(element)) { |
|
|
|
log.warn("检测到循环元素组合引用: type={}, data={}", element.getType(), element.getData()); |
|
|
|
return element; |
|
|
|
} |
|
|
|
|
|
|
|
String originalData = element.getData(); |
|
|
|
|
|
|
|
// 检查是否包含元素引用 |
|
|
|
if (containsElementReferences(originalData)) { |
|
|
|
try { |
|
|
|
ReportLabelList processedElement = copyElement(element); |
|
|
|
String combinedData = processElementCombination(originalData, dataMap, allElements, element, printFlag); |
|
|
|
String combinedData = processElementCombination(element.getData(), dataMap, allElements, |
|
|
|
referenceElements, resolvedElements, resolvingElements, printFlag); |
|
|
|
// 文本自身的加工规则在组合完成后执行一次;引用值不再做字段替换。 |
|
|
|
if ("text".equals(element.getType())) { |
|
|
|
combinedData = formatTextValue(combinedData, element, printFlag); |
|
|
|
} |
|
|
|
processedElement.setData(combinedData); |
|
|
|
log.debug("处理元素组合: 原始数据={}, 组合后数据={}", originalData, combinedData); |
|
|
|
resolvedElements.put(element, processedElement); |
|
|
|
return processedElement; |
|
|
|
} finally { |
|
|
|
resolvingElements.remove(element); |
|
|
|
} |
|
|
|
|
|
|
|
return element; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 检查数据中是否包含元素引用 |
|
|
|
* 只识别元素引用,不能把普通的 #{字段名} 当成元素组合。 |
|
|
|
*/ |
|
|
|
private boolean containsElementReferences(String data) { |
|
|
|
if (data == null || data.isEmpty()) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
// 检查是否包含 {元素类型} 格式的引用(排除 #{字段名} 格式) |
|
|
|
Pattern pattern = Pattern.compile("\\{(?!#)[^}]+\\}"); |
|
|
|
Pattern pattern = Pattern.compile("(?<!#)\\{(?:文本|一维码|二维码|图片|流水号)(?:\\(|\\})"); |
|
|
|
return pattern.matcher(data).find() || data.startsWith("CUSTOM:"); |
|
|
|
} |
|
|
|
|
|
|
|
@ -376,7 +406,10 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService |
|
|
|
// 处理不同类型的元素 |
|
|
|
switch (element.getType()) { |
|
|
|
case "text": |
|
|
|
processedElement.setData(processTextElement(originalData, dataMap, element, printFlag)); |
|
|
|
// 和条码一样,保留组合模板,避免提前替换字段或截取引用表达式。 |
|
|
|
if (!containsElementReferences(originalData)) { |
|
|
|
processedElement.setData(processTextElement(originalData, dataMap, element, printFlag)); |
|
|
|
} |
|
|
|
break; |
|
|
|
case "onecode": |
|
|
|
case "qrcode": |
|
|
|
@ -445,79 +478,63 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService |
|
|
|
* 支持将多个元素的内容按特定格式组合 |
|
|
|
*/ |
|
|
|
private String processElementCombination(String originalData, Map<String, Object> dataMap, |
|
|
|
List<ReportLabelList> allElements, ReportLabelList currentElement,Boolean printFlag) { |
|
|
|
if (originalData == null || originalData.isEmpty()) { |
|
|
|
return originalData; |
|
|
|
} |
|
|
|
|
|
|
|
// 检查是否是自定义表达式模式 |
|
|
|
List<ReportLabelList> allElements, |
|
|
|
Map<String, ReportLabelList> referenceElements, |
|
|
|
Map<ReportLabelList, ReportLabelList> resolvedElements, |
|
|
|
Set<ReportLabelList> resolvingElements, Boolean printFlag) { |
|
|
|
if (originalData.startsWith("CUSTOM:")) { |
|
|
|
return processCustomCombination(originalData.substring(7), dataMap, allElements); |
|
|
|
} |
|
|
|
|
|
|
|
// 处理模板模式的元素引用 {元素类型} 或 {元素类型(内容)} |
|
|
|
String processedData = originalData; |
|
|
|
|
|
|
|
// 创建元素映射,便于查找 |
|
|
|
Map<String, ReportLabelList> elementMap = new HashMap<>(); |
|
|
|
for (ReportLabelList element : allElements) { |
|
|
|
if (element != currentElement) { // 排除自身 |
|
|
|
String elementKey = getElementDisplayName(element); |
|
|
|
elementMap.put(elementKey, element); |
|
|
|
StringBuilder result = new StringBuilder(); |
|
|
|
int literalStart = 0; |
|
|
|
for (int i = 0; i < originalData.length(); i++) { |
|
|
|
if (originalData.charAt(i) != '{') { |
|
|
|
continue; |
|
|
|
} |
|
|
|
int end = findReferenceEnd(originalData, i); |
|
|
|
if (end < 0) { |
|
|
|
break; |
|
|
|
} |
|
|
|
if (i > 0 && originalData.charAt(i - 1) == '#') { |
|
|
|
// 普通字段由后续的片段替换处理,不进入元素匹配。 |
|
|
|
i = end; |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 替换元素引用 |
|
|
|
Pattern elementPattern = Pattern.compile("\\{([^}]+)\\}"); |
|
|
|
Matcher matcher = elementPattern.matcher(processedData); |
|
|
|
StringBuffer result = new StringBuffer(); |
|
|
|
|
|
|
|
while (matcher.find()) { |
|
|
|
String reference = matcher.group(1); |
|
|
|
String replacement = ""; |
|
|
|
|
|
|
|
// 检查是否是数据源字段引用 #{fieldName} |
|
|
|
if (reference.startsWith("#{") && reference.endsWith("}")) { |
|
|
|
String fieldName = reference.substring(2, reference.length() - 1); |
|
|
|
|
|
|
|
// 特殊处理流水号引用 |
|
|
|
if ("SERIAL_NUMBER".equals(fieldName)) { |
|
|
|
Object serialValue = getMapValueIgnoreCase(dataMap, "SERIAL_NUMBER"); |
|
|
|
replacement = serialValue != null ? serialValue.toString() : ""; |
|
|
|
log.debug("元素组合中使用流水号引用: SERIAL_NUMBER={}", replacement); |
|
|
|
} |
|
|
|
// 首先检查是否是系统变量 |
|
|
|
else if (isSystemVariable(fieldName)) { |
|
|
|
replacement = getSystemVariableValue(fieldName); |
|
|
|
log.debug("元素组合中使用系统变量: {}={}", fieldName, replacement); |
|
|
|
} else { |
|
|
|
// 普通数据源字段 |
|
|
|
Object value = getMapValueIgnoreCase(dataMap, fieldName); |
|
|
|
replacement = value != null ? value.toString() : ""; |
|
|
|
log.debug("元素组合中使用数据源字段: {}={}", fieldName, replacement); |
|
|
|
} |
|
|
|
result.append(replaceDataSourceFields(originalData.substring(literalStart, i), dataMap)); |
|
|
|
String reference = originalData.substring(i + 1, end); |
|
|
|
ReportLabelList referencedElement = findElementByReference(reference, referenceElements); |
|
|
|
if (referencedElement == null) { |
|
|
|
result.append(replaceDataSourceFields(originalData.substring(i, end + 1), dataMap)); |
|
|
|
} else { |
|
|
|
// 查找匹配的元素 |
|
|
|
ReportLabelList referencedElement = findElementByReference(reference, elementMap); |
|
|
|
if (referencedElement != null) { |
|
|
|
// 递归处理引用的元素数据 |
|
|
|
replacement = processElementData(referencedElement, dataMap,false, printFlag).getData(); |
|
|
|
if (replacement == null) { |
|
|
|
replacement = ""; |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 如果找不到匹配的元素,保持原样 |
|
|
|
replacement = "{" + reference + "}"; |
|
|
|
} |
|
|
|
ReportLabelList resolved = processElementCombinations(referencedElement, dataMap, allElements, |
|
|
|
referenceElements, resolvedElements, resolvingElements, printFlag); |
|
|
|
// 直接使用已加工的值,避免二次加工或把结果中的花括号再解释为模板。 |
|
|
|
result.append(resolved.getData() != null ? resolved.getData() : ""); |
|
|
|
} |
|
|
|
|
|
|
|
matcher.appendReplacement(result, Matcher.quoteReplacement(replacement)); |
|
|
|
i = end; |
|
|
|
literalStart = end + 1; |
|
|
|
} |
|
|
|
matcher.appendTail(result); |
|
|
|
|
|
|
|
result.append(replaceDataSourceFields(originalData.substring(literalStart), dataMap)); |
|
|
|
return result.toString(); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 引用内容可能包含 #{字段} 或嵌套的条码组合,必须按配对花括号读取完整引用。 |
|
|
|
*/ |
|
|
|
private int findReferenceEnd(String data, int start) { |
|
|
|
int depth = 0; |
|
|
|
for (int i = start; i < data.length(); i++) { |
|
|
|
if (data.charAt(i) == '{') { |
|
|
|
depth++; |
|
|
|
} else if (data.charAt(i) == '}' && --depth == 0) { |
|
|
|
return i; |
|
|
|
} |
|
|
|
} |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 处理自定义组合表达式 |
|
|
|
*/ |
|
|
|
@ -678,9 +695,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService |
|
|
|
|
|
|
|
String typeName = typeNames.getOrDefault(element.getType(), element.getType()); |
|
|
|
String content = element.getData() != null && !element.getData().isEmpty() |
|
|
|
? "(" + (element.getData().length() > 10 |
|
|
|
? element.getData().substring(0, 10) + "..." |
|
|
|
: element.getData()) + ")" |
|
|
|
? "(" + element.getData() + ")" |
|
|
|
: ""; |
|
|
|
|
|
|
|
return typeName + content; |
|
|
|
@ -692,7 +707,18 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService |
|
|
|
private String processTextElement(String originalData, Map<String, Object> dataMap, ReportLabelList element, Boolean printFlag) { |
|
|
|
// 首先替换数据源字段 |
|
|
|
String replacedData = replaceDataSourceFields(originalData, dataMap); |
|
|
|
if ("date".equals(element.getDataType())) { |
|
|
|
// 普通日期模板保留原有的占位符及固定前后缀处理。 |
|
|
|
return appendDateSerialSuffixIfEnabled( |
|
|
|
processDateText(originalData, replacedData, dataMap, element), element, printFlag); |
|
|
|
} |
|
|
|
return formatTextValue(replacedData, element, printFlag); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 格式化已解析的文本值,不再解释其中的数据源占位符或元素引用。 |
|
|
|
*/ |
|
|
|
private String formatTextValue(String replacedData, ReportLabelList element, Boolean printFlag) { |
|
|
|
// 根据数据类型进行不同的处理 |
|
|
|
String dataType = element.getDataType(); |
|
|
|
if (dataType == null) { |
|
|
|
@ -707,7 +733,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService |
|
|
|
break; |
|
|
|
case "date": |
|
|
|
return appendDateSerialSuffixIfEnabled( |
|
|
|
processDateText(originalData, replacedData, dataMap, element), |
|
|
|
processDateElement(replacedData, element), |
|
|
|
element, |
|
|
|
printFlag |
|
|
|
); |
|
|
|
@ -1632,6 +1658,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService |
|
|
|
copy.setBarcodeType(original.getBarcodeType()); |
|
|
|
copy.setShowContent(original.getShowContent()); |
|
|
|
copy.setShowElement(original.getShowElement()); |
|
|
|
copy.setShowSerialNumber(original.getShowSerialNumber()); |
|
|
|
copy.setShowMainSeq(original.getShowMainSeq()); |
|
|
|
copy.setSeqName(original.getSeqName()); |
|
|
|
copy.setIsChecked(original.getIsChecked()); |
|
|
|
|