|
|
@ -19,10 +19,12 @@ import java.time.format.DateTimeFormatter; |
|
|
import java.time.temporal.ChronoUnit; |
|
|
import java.time.temporal.ChronoUnit; |
|
|
import java.util.ArrayList; |
|
|
import java.util.ArrayList; |
|
|
import java.util.Date; |
|
|
import java.util.Date; |
|
|
|
|
|
import java.util.HashMap; |
|
|
import java.util.List; |
|
|
import java.util.List; |
|
|
import java.util.Map; |
|
|
import java.util.Map; |
|
|
import java.util.regex.Matcher; |
|
|
import java.util.regex.Matcher; |
|
|
import java.util.regex.Pattern; |
|
|
import java.util.regex.Pattern; |
|
|
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 标签数据处理服务实现类 |
|
|
* 标签数据处理服务实现类 |
|
|
@ -45,22 +47,68 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
List<ReportLabelList> processedElements = new ArrayList<>(); |
|
|
List<ReportLabelList> processedElements = new ArrayList<>(); |
|
|
|
|
|
|
|
|
|
|
|
// 第一轮处理:处理基本数据替换(不包含元素组合) |
|
|
|
|
|
List<ReportLabelList> firstPassElements = new ArrayList<>(); |
|
|
for (ReportLabelList element : elements) { |
|
|
for (ReportLabelList element : elements) { |
|
|
ReportLabelList processedElement = processElementData(element, dataMap); |
|
|
ReportLabelList processedElement = processElementData(element, dataMap); |
|
|
|
|
|
firstPassElements.add(processedElement); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 第二轮处理:处理元素组合引用 |
|
|
|
|
|
for (ReportLabelList element : firstPassElements) { |
|
|
|
|
|
ReportLabelList finalElement = processElementCombinations(element, dataMap, firstPassElements); |
|
|
|
|
|
|
|
|
// 如果元素被设置为不显示,则跳过该元素 |
|
|
// 如果元素被设置为不显示,则跳过该元素 |
|
|
if (("onecode".equals(element.getType()) || "qrcode".equals(element.getType()) |
|
|
if (("onecode".equals(element.getType()) || "qrcode".equals(element.getType()) |
|
|
|| "text".equals(element.getType()) || "pic".equals(element.getType())) |
|
|
|| "text".equals(element.getType()) || "pic".equals(element.getType())) |
|
|
&& processedElement.getData().isEmpty() |
|
|
|
|
|
&& BigDecimal.ZERO.equals(processedElement.getWidth())) { |
|
|
|
|
|
|
|
|
&& finalElement.getData().isEmpty() |
|
|
|
|
|
&& BigDecimal.ZERO.equals(finalElement.getWidth())) { |
|
|
log.debug("跳过不显示的元素: type={}, showElement={}", element.getType(), element.getShowElement()); |
|
|
log.debug("跳过不显示的元素: type={}, showElement={}", element.getType(), element.getShowElement()); |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
processedElements.add(processedElement); |
|
|
|
|
|
|
|
|
processedElements.add(finalElement); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return processedElements; |
|
|
return processedElements; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 处理元素组合引用 |
|
|
|
|
|
*/ |
|
|
|
|
|
private ReportLabelList processElementCombinations(ReportLabelList element, Map<String, Object> dataMap, |
|
|
|
|
|
List<ReportLabelList> allElements) { |
|
|
|
|
|
if (element == null || element.getData() == null || element.getData().isEmpty()) { |
|
|
|
|
|
return element; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
String originalData = element.getData(); |
|
|
|
|
|
|
|
|
|
|
|
// 检查是否包含元素引用 |
|
|
|
|
|
if (containsElementReferences(originalData)) { |
|
|
|
|
|
ReportLabelList processedElement = copyElement(element); |
|
|
|
|
|
String combinedData = processElementCombination(originalData, dataMap, allElements, element); |
|
|
|
|
|
processedElement.setData(combinedData); |
|
|
|
|
|
log.debug("处理元素组合: 原始数据={}, 组合后数据={}", originalData, combinedData); |
|
|
|
|
|
return processedElement; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return element; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 检查数据中是否包含元素引用 |
|
|
|
|
|
*/ |
|
|
|
|
|
private boolean containsElementReferences(String data) { |
|
|
|
|
|
if (data == null || data.isEmpty()) { |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 检查是否包含 {元素类型} 格式的引用(排除 #{字段名} 格式) |
|
|
|
|
|
Pattern pattern = Pattern.compile("\\{(?!#)[^}]+\\}"); |
|
|
|
|
|
return pattern.matcher(data).find() || data.startsWith("CUSTOM:"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
@Override |
|
|
@Override |
|
|
public ReportLabelList processElementData(ReportLabelList element, Map<String, Object> dataMap) { |
|
|
public ReportLabelList processElementData(ReportLabelList element, Map<String, Object> dataMap) { |
|
|
if (element == null || element.getData() == null || element.getData().isEmpty()) { |
|
|
if (element == null || element.getData() == null || element.getData().isEmpty()) { |
|
|
@ -93,7 +141,14 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService |
|
|
break; |
|
|
break; |
|
|
case "onecode": |
|
|
case "onecode": |
|
|
case "qrcode": |
|
|
case "qrcode": |
|
|
processedElement.setData(replaceDataSourceFields(originalData, dataMap)); |
|
|
|
|
|
|
|
|
// 检查是否包含元素组合引用 |
|
|
|
|
|
if (containsElementReferences(originalData)) { |
|
|
|
|
|
// 需要传入所有元素列表来处理组合 |
|
|
|
|
|
// 这里暂时使用基本的数据源替换,完整的组合处理在processLabelData中进行 |
|
|
|
|
|
processedElement.setData(replaceDataSourceFields(originalData, dataMap)); |
|
|
|
|
|
} else { |
|
|
|
|
|
processedElement.setData(replaceDataSourceFields(originalData, dataMap)); |
|
|
|
|
|
} |
|
|
break; |
|
|
break; |
|
|
case "serialNumber": |
|
|
case "serialNumber": |
|
|
// 处理流水号元素,生成真实的流水号 |
|
|
// 处理流水号元素,生成真实的流水号 |
|
|
@ -152,6 +207,237 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService |
|
|
return new CoordinateTransformer.CanvasSize(width, height, name, description); |
|
|
return new CoordinateTransformer.CanvasSize(width, height, name, description); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 处理元素组合数据 |
|
|
|
|
|
* 支持将多个元素的内容按特定格式组合 |
|
|
|
|
|
*/ |
|
|
|
|
|
private String processElementCombination(String originalData, Map<String, Object> dataMap, |
|
|
|
|
|
List<ReportLabelList> allElements, ReportLabelList currentElement) { |
|
|
|
|
|
if (originalData == null || originalData.isEmpty()) { |
|
|
|
|
|
return originalData; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 检查是否是自定义表达式模式 |
|
|
|
|
|
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); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 替换元素引用 |
|
|
|
|
|
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); |
|
|
|
|
|
Object value = dataMap.get(fieldName); |
|
|
|
|
|
replacement = value != null ? value.toString() : ""; |
|
|
|
|
|
} else { |
|
|
|
|
|
// 查找匹配的元素 |
|
|
|
|
|
ReportLabelList referencedElement = findElementByReference(reference, elementMap); |
|
|
|
|
|
if (referencedElement != null) { |
|
|
|
|
|
// 递归处理引用的元素数据 |
|
|
|
|
|
replacement = processElementData(referencedElement, dataMap).getData(); |
|
|
|
|
|
if (replacement == null) { |
|
|
|
|
|
replacement = ""; |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
// 如果找不到匹配的元素,保持原样 |
|
|
|
|
|
replacement = "{" + reference + "}"; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
matcher.appendReplacement(result, Matcher.quoteReplacement(replacement)); |
|
|
|
|
|
} |
|
|
|
|
|
matcher.appendTail(result); |
|
|
|
|
|
|
|
|
|
|
|
return result.toString(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 处理自定义组合表达式 |
|
|
|
|
|
*/ |
|
|
|
|
|
private String processCustomCombination(String expression, Map<String, Object> dataMap, |
|
|
|
|
|
List<ReportLabelList> allElements) { |
|
|
|
|
|
try { |
|
|
|
|
|
// 这里可以实现简单的表达式处理 |
|
|
|
|
|
// 为了安全起见,只支持有限的操作 |
|
|
|
|
|
|
|
|
|
|
|
// 示例:支持简单的字符串拼接和条件判断 |
|
|
|
|
|
if (expression.contains("elements.filter")) { |
|
|
|
|
|
return processElementsFilter(expression, allElements, dataMap); |
|
|
|
|
|
} else if (expression.contains("dataMap.")) { |
|
|
|
|
|
return processDataMapAccess(expression, dataMap); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 默认返回表达式本身 |
|
|
|
|
|
return expression; |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.error("处理自定义组合表达式失败: {}", expression, e); |
|
|
|
|
|
return "ERROR: " + e.getMessage(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 处理元素过滤表达式 |
|
|
|
|
|
*/ |
|
|
|
|
|
private String processElementsFilter(String expression, List<ReportLabelList> allElements, |
|
|
|
|
|
Map<String, Object> dataMap) { |
|
|
|
|
|
// 简单实现:elements.filter(e => e.type === 'text').map(e => e.data).join('-') |
|
|
|
|
|
if (expression.contains("e.type === 'text'")) { |
|
|
|
|
|
List<String> textData = allElements.stream() |
|
|
|
|
|
.filter(e -> "text".equals(e.getType())) |
|
|
|
|
|
.map(e -> e.getData() != null ? e.getData() : "") |
|
|
|
|
|
.collect(Collectors.toList()); |
|
|
|
|
|
|
|
|
|
|
|
String separator = "-"; |
|
|
|
|
|
if (expression.contains(".join('")) { |
|
|
|
|
|
int start = expression.indexOf(".join('") + 7; |
|
|
|
|
|
int end = expression.indexOf("')", start); |
|
|
|
|
|
if (end > start) { |
|
|
|
|
|
separator = expression.substring(start, end); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return String.join(separator, textData); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return expression; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 处理数据映射访问表达式 |
|
|
|
|
|
*/ |
|
|
|
|
|
private String processDataMapAccess(String expression, Map<String, Object> dataMap) { |
|
|
|
|
|
// 简单实现:dataMap.fieldName |
|
|
|
|
|
Pattern pattern = Pattern.compile("dataMap\\.([a-zA-Z_][a-zA-Z0-9_]*)"); |
|
|
|
|
|
Matcher matcher = pattern.matcher(expression); |
|
|
|
|
|
StringBuffer result = new StringBuffer(); |
|
|
|
|
|
|
|
|
|
|
|
while (matcher.find()) { |
|
|
|
|
|
String fieldName = matcher.group(1); |
|
|
|
|
|
Object value = dataMap.get(fieldName); |
|
|
|
|
|
String replacement = value != null ? value.toString() : ""; |
|
|
|
|
|
matcher.appendReplacement(result, Matcher.quoteReplacement(replacement)); |
|
|
|
|
|
} |
|
|
|
|
|
matcher.appendTail(result); |
|
|
|
|
|
|
|
|
|
|
|
return result.toString(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 根据引用查找元素 |
|
|
|
|
|
*/ |
|
|
|
|
|
private ReportLabelList findElementByReference(String reference, Map<String, ReportLabelList> elementMap) { |
|
|
|
|
|
// 直接匹配 |
|
|
|
|
|
if (elementMap.containsKey(reference)) { |
|
|
|
|
|
return elementMap.get(reference); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 检查是否是带内容的引用格式:文本(内容) 或 类型(内容) |
|
|
|
|
|
Pattern contentPattern = Pattern.compile("^(文本|一维码|二维码|图片|流水号)\\((.+)\\)$"); |
|
|
|
|
|
Matcher contentMatcher = contentPattern.matcher(reference); |
|
|
|
|
|
|
|
|
|
|
|
if (contentMatcher.matches()) { |
|
|
|
|
|
String typeName = contentMatcher.group(1); |
|
|
|
|
|
String expectedContent = contentMatcher.group(2); |
|
|
|
|
|
|
|
|
|
|
|
// 根据类型名称获取对应的类型键 |
|
|
|
|
|
String targetType = getTypeKeyByName(typeName); |
|
|
|
|
|
if (targetType != null) { |
|
|
|
|
|
// 精确匹配:同时匹配类型和内容 |
|
|
|
|
|
for (ReportLabelList element : elementMap.values()) { |
|
|
|
|
|
if (targetType.equals(element.getType()) && |
|
|
|
|
|
expectedContent.equals(element.getData())) { |
|
|
|
|
|
log.debug("精确匹配到元素: type={}, data={}", element.getType(), element.getData()); |
|
|
|
|
|
return element; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 如果精确匹配失败,尝试部分内容匹配 |
|
|
|
|
|
for (ReportLabelList element : elementMap.values()) { |
|
|
|
|
|
if (targetType.equals(element.getType()) && |
|
|
|
|
|
element.getData() != null && |
|
|
|
|
|
element.getData().contains(expectedContent)) { |
|
|
|
|
|
log.debug("部分匹配到元素: type={}, data={}, expected={}", |
|
|
|
|
|
element.getType(), element.getData(), expectedContent); |
|
|
|
|
|
return element; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 模糊匹配(按类型)- 只有在没有内容指定时才使用 |
|
|
|
|
|
String[] typeNames = {"文本", "一维码", "二维码", "图片", "流水号"}; |
|
|
|
|
|
String[] typeKeys = {"text", "onecode", "qrcode", "pic", "serialNumber"}; |
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < typeNames.length; i++) { |
|
|
|
|
|
if (reference.equals(typeNames[i])) { // 改为精确匹配类型名称 |
|
|
|
|
|
// 返回该类型的第一个元素(保持向后兼容) |
|
|
|
|
|
for (ReportLabelList element : elementMap.values()) { |
|
|
|
|
|
if (typeKeys[i].equals(element.getType())) { |
|
|
|
|
|
log.debug("类型匹配到元素: type={}, data={}", element.getType(), element.getData()); |
|
|
|
|
|
return element; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
log.warn("未找到匹配的元素引用: {}", reference); |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 根据类型名称获取类型键 |
|
|
|
|
|
*/ |
|
|
|
|
|
private String getTypeKeyByName(String typeName) { |
|
|
|
|
|
switch (typeName) { |
|
|
|
|
|
case "文本": return "text"; |
|
|
|
|
|
case "一维码": return "onecode"; |
|
|
|
|
|
case "二维码": return "qrcode"; |
|
|
|
|
|
case "图片": return "pic"; |
|
|
|
|
|
case "流水号": return "serialNumber"; |
|
|
|
|
|
default: return null; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 获取元素显示名称 |
|
|
|
|
|
*/ |
|
|
|
|
|
private String getElementDisplayName(ReportLabelList element) { |
|
|
|
|
|
Map<String, String> typeNames = new HashMap<>(); |
|
|
|
|
|
typeNames.put("text", "文本"); |
|
|
|
|
|
typeNames.put("onecode", "一维码"); |
|
|
|
|
|
typeNames.put("qrcode", "二维码"); |
|
|
|
|
|
typeNames.put("pic", "图片"); |
|
|
|
|
|
typeNames.put("serialNumber", "流水号"); |
|
|
|
|
|
|
|
|
|
|
|
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()) + ")" |
|
|
|
|
|
: ""; |
|
|
|
|
|
|
|
|
|
|
|
return typeName + content; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 处理文本元素,支持数字、日期、字符串格式化 |
|
|
* 处理文本元素,支持数字、日期、字符串格式化 |
|
|
*/ |
|
|
*/ |
|
|
|