diff --git a/src/main/java/com/gaotao/modules/base/service/Impl/LabelDataProcessorServiceImpl.java b/src/main/java/com/gaotao/modules/base/service/Impl/LabelDataProcessorServiceImpl.java index ca65edc..2916c3e 100644 --- a/src/main/java/com/gaotao/modules/base/service/Impl/LabelDataProcessorServiceImpl.java +++ b/src/main/java/com/gaotao/modules/base/service/Impl/LabelDataProcessorServiceImpl.java @@ -19,10 +19,12 @@ import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * 标签数据处理服务实现类 @@ -45,22 +47,68 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService } List processedElements = new ArrayList<>(); + + // 第一轮处理:处理基本数据替换(不包含元素组合) + List firstPassElements = new ArrayList<>(); for (ReportLabelList element : elements) { 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()) || "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()); continue; } - processedElements.add(processedElement); + processedElements.add(finalElement); } return processedElements; } + /** + * 处理元素组合引用 + */ + private ReportLabelList processElementCombinations(ReportLabelList element, Map dataMap, + List 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 public ReportLabelList processElementData(ReportLabelList element, Map dataMap) { if (element == null || element.getData() == null || element.getData().isEmpty()) { @@ -93,7 +141,14 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService break; case "onecode": case "qrcode": - processedElement.setData(replaceDataSourceFields(originalData, dataMap)); + // 检查是否包含元素组合引用 + if (containsElementReferences(originalData)) { + // 需要传入所有元素列表来处理组合 + // 这里暂时使用基本的数据源替换,完整的组合处理在processLabelData中进行 + processedElement.setData(replaceDataSourceFields(originalData, dataMap)); + } else { + processedElement.setData(replaceDataSourceFields(originalData, dataMap)); + } break; case "serialNumber": // 处理流水号元素,生成真实的流水号 @@ -152,6 +207,237 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService return new CoordinateTransformer.CanvasSize(width, height, name, description); } + /** + * 处理元素组合数据 + * 支持将多个元素的内容按特定格式组合 + */ + private String processElementCombination(String originalData, Map dataMap, + List 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 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 dataMap, + List 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 allElements, + Map dataMap) { + // 简单实现:elements.filter(e => e.type === 'text').map(e => e.data).join('-') + if (expression.contains("e.type === 'text'")) { + List 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 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 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 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; + } + /** * 处理文本元素,支持数字、日期、字符串格式化 */