diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/util/VisualStringUtils.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/util/VisualStringUtils.java new file mode 100644 index 000000000..0bd1bb553 --- /dev/null +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/util/VisualStringUtils.java @@ -0,0 +1,64 @@ +package cn.axzo.workflow.server.common.util; + +/** + * 处理字符串视觉显示宽度的工具类 + * 适用于中英文混排的对齐场景 + */ +public class VisualStringUtils { + + // 私有构造器,防止实例化 + private VisualStringUtils() { + throw new UnsupportedOperationException("Utility class"); + } + + /** + * 将输入字符串转换为等效视觉长度的空格字符串 + * 例:"Hi中" -> " " (4个空格:H=1, i=1, 中=2) + * + * @param input 原始字符串 + * @return 等长的空格字符串,如果输入为null则返回空串 + */ + public static String toEqualLengthSpace(String input) { + if (input == null || input.isEmpty()) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + for (char c : input.toCharArray()) { + if (isWideChar(c)) { + sb.append(" "); // 宽字符占2格 + } else { + sb.append(" "); // 窄字符占1格 + } + } + return sb.toString(); + } + + /** + * 获取字符串的视觉显示长度 + * (中文算2,英文算1) + * + * @param input 输入字符串 + * @return 视觉长度 + */ + public static int getVisualLength(String input) { + if (input == null || input.isEmpty()) { + return 0; + } + int length = 0; + for (char c : input.toCharArray()) { + length += isWideChar(c) ? 2 : 1; + } + return length; + } + + /** + * 判断字符是否为宽字符(占2个显示宽度) + * 逻辑:非 ASCII 字符 (code > 255) 视为宽字符 + */ + private static boolean isWideChar(char c) { + // 0-255 包括了数字、英文大小写、英文标点符号 (半角) + // 大于 255 的涵盖了汉字、全角符号、日韩文等 + return c > 255; + } +} \ No newline at end of file diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/util/WpsUtil.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/util/WpsUtil.java index d0905551c..97f719f0d 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/util/WpsUtil.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/util/WpsUtil.java @@ -45,7 +45,9 @@ import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCE import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_PHONE_DESC; import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_POSITION; import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_POSITION_DESC; +import static cn.axzo.workflow.common.model.dto.VariableObjectDTO.Type.img; import static cn.axzo.workflow.common.model.dto.VariableObjectDTO.Type.signatureAndAdvice; +import static cn.axzo.workflow.common.model.dto.VariableObjectDTO.Type.text; /** * WPS 工具类 @@ -63,6 +65,13 @@ public class WpsUtil { private ServerFileServiceApi serverFileServiceApi; @Resource private OrganizationalNodeUserQueryApi organizationalNodeUserQueryApi; + private static final String TRANSPARENT_IMAGE_URL = "https://axzo-obs-public.obs.cn-north-4.myhuaweicloud.com:443/obs-public/obs-public/62F92618016840F89D8810CD1816E04D.png"; + + public static void main(String[] args) { + List strList = new ArrayList<>(); + strList.add("[中文]"); + strList.add("[中文,English,]"); + } /** * 调用 wps 文件变量替换接口 @@ -79,7 +88,7 @@ public class WpsUtil { if (Objects.equals(Boolean.TRUE, autoReplaceVariables)) { return true; } else { - return Objects.nonNull(i.getValue()); + return Objects.nonNull(i.getValue()) && checkNotEmptyColl(i); } }).collect(Collectors.toList()); List fileReplaceContents = BeanMapper.copyList(variables, FileReplaceContent.class, (s, t) -> { @@ -91,7 +100,9 @@ public class WpsUtil { } else { uploadFieldDTOS.addAll(JSON.parseArray(JSON.toJSONString(s.getValue()), UploadFieldDTO.class)); } - t.setContent(CollectionUtils.isEmpty(uploadFieldDTOS) ? "" : uploadFieldDTOS.get(0).getFileUrl()); + t.setContent(CollectionUtils.isEmpty(uploadFieldDTOS) ? + // 强制设置为透明图片 + TRANSPARENT_IMAGE_URL : uploadFieldDTOS.get(0).getFileUrl()); } else { t.setContent(Objects.nonNull(s.getValue()) ? s.getValue().toString() : ""); } @@ -105,24 +116,19 @@ public class WpsUtil { fileReplaceContents.stream() .filter(i -> !StringUtils.hasText(i.getContent())) .forEach(i -> { - // TODO 将变量替换未空串 + if (Objects.equals(i.getType(), img.name())) { + //强制设置为透明图片 + i.setContent(TRANSPARENT_IMAGE_URL); + } else if (Objects.equals(i.getType(), text.name())) { + // 将变量替换未空串 + String equalLengthSpace = VisualStringUtils.toEqualLengthSpace(i.getPrefix() + i.getKey() + i.getSuffix()); + i.setContent(equalLengthSpace); + } }); } - if (StringUtils.hasText(fileCode)) { - FileTemplateReplaceRequest request = new FileTemplateReplaceRequest(); - request.setFileCode(fileCode); - request.setFileKey(fileKey); - request.setReplaceContentList(fileReplaceContents); - return RpcExternalUtil.rpcProcessor(() -> fileTemplateApi.replaceWordText(request), "替换 WPS 文档变量", request); - } else { - SimpleFileTemplateReplaceRequest request = new SimpleFileTemplateReplaceRequest(); - request.setFileName(fileName); - request.setFileKey(fileKey); - request.setReplaceContentList(fileReplaceContents); - return RpcExternalUtil.rpcProcessor(() -> fileTemplateApi.replaceWordTextForFileKey(request), "替换 WPS 文档变量(simple)", request); - } + return replaceWordText(fileCode, fileKey, fileName, fileReplaceContents); } /** @@ -135,9 +141,8 @@ public class WpsUtil { public String wpsFileVariableReplace(List wpsVariables, String fileCode, String fileKey, String fileName) { - List fileReplaceContents = BeanMapper.copyList(wpsVariables.stream() + /*List fileReplaceContents = BeanMapper.copyList(wpsVariables.stream() .filter(i -> Objects.nonNull(i.getValue()) && checkNotEmptyColl(i)) - .filter(i -> !(Objects.equals(i.getType().name(), "img") && !StringUtils.hasText(i.getValue().toString()))) .filter(i -> Objects.equals(i.getType().name(), "img") || Objects.equals(i.getType().name(), "text")) .collect(Collectors.toList()), FileReplaceContent.class, (s, t) -> { t.setKey(s.getDesc()); @@ -156,6 +161,11 @@ public class WpsUtil { t.setPrefix("["); t.setSuffix("]"); }); + return replaceWordText(fileCode, fileKey, fileName, fileReplaceContents);*/ + return wpsFileVariableReplace(wpsVariables, fileCode, fileKey, fileName, false); + } + + private String replaceWordText(String fileCode, String fileKey, String fileName, List fileReplaceContents) { if (StringUtils.hasText(fileCode)) { FileTemplateReplaceRequest request = new FileTemplateReplaceRequest(); request.setFileCode(fileCode);