From bccdbb6a55681f2be991d38b2170720a44f36077 Mon Sep 17 00:00:00 2001
From: zzs <hi@vio.vin>
Date: Sat, 1 Mar 2025 15:27:56 +0800
Subject: [PATCH 1/2] feat: get file tags

---
 wmyun-framework/pom.xml                       |  1 +
 .../wmyun-spring-boot-starter-word/pom.xml    | 89 ++++++++++++++++++
 .../farmwork/word/config/DefaultConfig.java   | 19 ++++
 .../word/core/TemplateReplaceExecutor.java    | 60 ++++++++++++
 .../word/core/TemplateTagsGenerate.java       | 91 +++++++++++++++++++
 .../word/core/model/constant/TagType.java     | 30 ++++++
 .../model/exec/TemplateExecDataModel.java     | 23 +++++
 .../word/core/model/gen/TagsGenDataModel.java | 34 +++++++
 .../word/core/model/type/TypeImage.java       | 50 ++++++++++
 .../word/core/model/type/TypeTable.java       | 27 ++++++
 .../word/core/model/type/TypeText.java        | 75 +++++++++++++++
 .../farmwork/word/core/utils/WPUtils.java     | 27 ++++++
 .../com/wmyun/farmwork/word/package-info.java |  4 +
 .../wmyun-module-infra-biz/pom.xml            |  5 +
 .../admin/word/WordTemplateController.java    | 37 ++++++++
 .../service/file/FilePreviewService.java      |  3 +
 .../service/file/FilePreviewServiceImpl.java  |  2 +-
 .../service/file/WordTemplateTagService.java  | 13 +++
 .../file/WordTemplateTagServiceImpl.java      | 47 ++++++++++
 19 files changed, 636 insertions(+), 1 deletion(-)
 create mode 100644 wmyun-framework/wmyun-spring-boot-starter-word/pom.xml
 create mode 100644 wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/config/DefaultConfig.java
 create mode 100644 wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/TemplateReplaceExecutor.java
 create mode 100644 wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/TemplateTagsGenerate.java
 create mode 100644 wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/constant/TagType.java
 create mode 100644 wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/exec/TemplateExecDataModel.java
 create mode 100644 wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/gen/TagsGenDataModel.java
 create mode 100644 wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/type/TypeImage.java
 create mode 100644 wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/type/TypeTable.java
 create mode 100644 wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/type/TypeText.java
 create mode 100644 wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/utils/WPUtils.java
 create mode 100644 wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/package-info.java
 create mode 100644 wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/controller/admin/word/WordTemplateController.java
 create mode 100644 wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagService.java
 create mode 100644 wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagServiceImpl.java

diff --git a/wmyun-framework/pom.xml b/wmyun-framework/pom.xml
index 1f91161..2e0740a 100644
--- a/wmyun-framework/pom.xml
+++ b/wmyun-framework/pom.xml
@@ -31,6 +31,7 @@
         <module>wmyun-spring-boot-starter-biz-tenant</module>
         <module>wmyun-spring-boot-starter-biz-data-permission</module>
         <module>wmyun-spring-boot-starter-biz-ip</module>
+        <module>wmyun-spring-boot-starter-word</module>
     </modules>
 
     <artifactId>wmyun-framework</artifactId>
diff --git a/wmyun-framework/wmyun-spring-boot-starter-word/pom.xml b/wmyun-framework/wmyun-spring-boot-starter-word/pom.xml
new file mode 100644
index 0000000..2185f10
--- /dev/null
+++ b/wmyun-framework/wmyun-spring-boot-starter-word/pom.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.wmyun</groupId>
+        <artifactId>wmyun-framework</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>wmyun-spring-boot-starter-word</artifactId>
+    <packaging>jar</packaging>
+
+    <name>${project.artifactId}</name>
+    <description>Word 拓展</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.wmyun</groupId>
+            <artifactId>wmyun-common</artifactId>
+        </dependency>
+
+        <!-- Spring 核心 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <!-- RPC 远程调用相关 -->
+        <dependency>
+            <groupId>com.wmyun</groupId>
+            <artifactId>wmyun-spring-boot-starter-rpc</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <!-- 业务组件 -->
+        <dependency>
+            <groupId>com.wmyun</groupId>
+            <artifactId>wmyun-module-system-api</artifactId> <!-- 需要使用它,进行 Dict 的查询 -->
+            <version>${revision}</version>
+        </dependency>
+
+        <!-- Web 相关 -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <scope>provided</scope> <!-- 设置为 provided,只有 ExcelUtils 使用 -->
+        </dependency>
+
+        <dependency>
+            <groupId>jakarta.servlet</groupId>
+            <artifactId>jakarta.servlet-api</artifactId>
+            <scope>provided</scope> <!-- 设置为 provided,只有 ExcelUtils 使用 -->
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+        <!-- Test 测试相关 -->
+        <dependency>
+            <groupId>com.wmyun</groupId>
+            <artifactId>wmyun-spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Word 相关 -->
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>5.2.5</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>com.deepoove</groupId>
+            <artifactId>poi-tl</artifactId>
+            <version>1.12.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-expression</artifactId>
+            <version>5.3.18</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/config/DefaultConfig.java b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/config/DefaultConfig.java
new file mode 100644
index 0000000..3f5fb0f
--- /dev/null
+++ b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/config/DefaultConfig.java
@@ -0,0 +1,19 @@
+package com.wmyun.farmwork.word.config;
+
+import com.deepoove.poi.config.Configure;
+import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
+import com.wmyun.farmwork.word.core.model.constant.TagType;
+
+/**
+ * @Description: TODO
+ * @Date: 2025/3/1 11:27
+ * @Created: by ZZSLL
+ */
+
+public class DefaultConfig {
+    public static Configure def() {
+        return Configure.builder()
+                .addPlugin(TagType.LOOP_ROW_TABLE.getSign().charAt(0), new LoopRowTableRenderPolicy()).build();
+//                .addPlugin(TagType.LOOP_COL_TABLE.getSign().charAt(0), new LoopColumnTableRenderPolicy());
+    }
+}
diff --git a/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/TemplateReplaceExecutor.java b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/TemplateReplaceExecutor.java
new file mode 100644
index 0000000..12322b5
--- /dev/null
+++ b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/TemplateReplaceExecutor.java
@@ -0,0 +1,60 @@
+package com.wmyun.farmwork.word.core;
+
+import com.deepoove.poi.XWPFTemplate;
+import com.deepoove.poi.config.Configure;
+import com.deepoove.poi.data.Pictures;
+import com.deepoove.poi.data.Texts;
+import com.deepoove.poi.data.style.Style;
+import com.wmyun.farmwork.word.config.DefaultConfig;
+import com.wmyun.farmwork.word.core.model.exec.TemplateExecDataModel;
+import com.wmyun.farmwork.word.core.model.type.TypeImage;
+import com.wmyun.farmwork.word.core.model.type.TypeText;
+import jakarta.validation.constraints.NotNull;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * @Description: TODO
+ * @Date: 2025/3/1 11:52
+ * @Created: by ZZSLL
+ */
+
+@Component
+public class TemplateReplaceExecutor {
+    public String doReplace(@NotNull String absolutePath, TemplateExecDataModel data) {
+        return doReplace(absolutePath, data, DefaultConfig.def());
+    }
+
+    private String doReplace(@NotNull String absolutePath, TemplateExecDataModel data, Configure def) {
+        File wordFile = new File(absolutePath);
+        Map<String, Object> dataMap = new HashMap<>();
+        for (TypeText text : data.getText()) {
+            dataMap.put(text.getName(), Texts
+                    .of(text.getValue()).create());
+        }
+        for (TypeImage image : data.getImage()) {
+            dataMap.put(image.getName(), Pictures
+                    .of(image.getValue())
+                    .size(image.getWidth(), image.getHeight())
+                    .create());
+        }
+        XWPFTemplate word = XWPFTemplate.compile(absolutePath, def).render(dataMap);
+        String name = wordFile.getName();
+        String tmpDir = System.getProperty("java.io.tmpdir");
+        String output = tmpDir + File.separator + name;
+        try {
+            word.writeAndClose(new FileOutputStream(output));
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+
+        return output;
+    }
+}
diff --git a/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/TemplateTagsGenerate.java b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/TemplateTagsGenerate.java
new file mode 100644
index 0000000..f7a5c78
--- /dev/null
+++ b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/TemplateTagsGenerate.java
@@ -0,0 +1,91 @@
+package com.wmyun.farmwork.word.core;
+
+import com.deepoove.poi.XWPFTemplate;
+import com.deepoove.poi.config.Configure;
+import com.deepoove.poi.template.ElementTemplate;
+import com.deepoove.poi.template.MetaTemplate;
+import com.deepoove.poi.template.run.RunTemplate;
+import com.wmyun.farmwork.word.config.DefaultConfig;
+import com.wmyun.farmwork.word.core.model.constant.TagType;
+import com.wmyun.farmwork.word.core.model.gen.TagsGenDataModel;
+import com.wmyun.farmwork.word.core.utils.WPUtils;
+import jakarta.validation.constraints.NotNull;
+import org.apache.poi.xwpf.usermodel.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @Description: 模板标签生成
+ * @Date: 2025/3/1 11:01
+ * @Created: by ZZSLL
+ */
+
+
+
+public class TemplateTagsGenerate {
+
+    public static List<TagsGenDataModel> execAllTags(@NotNull String absolutePath) {
+        return execAllTags(absolutePath, DefaultConfig.def());
+    }
+
+
+    /**
+     * 获取文件中所有标签,如果是表格,获取表格中每一行标签
+     * @param absolutePath 文件路径
+     * @return List<TagsGenDataModel>
+     */
+    public static List<TagsGenDataModel> execAllTags(@NotNull String absolutePath, Configure conf) {
+
+        List<TagsGenDataModel> results = new ArrayList<>();
+
+        XWPFTemplate template = XWPFTemplate.compile(absolutePath, conf);
+        Pattern pattern = Pattern.compile("\\[(.*?)\\]");
+
+        List<MetaTemplate> list = template.getElementTemplates();
+        for (MetaTemplate metaTemplate : list) {
+            TagsGenDataModel result = new TagsGenDataModel();
+            if (metaTemplate instanceof ElementTemplate ele) {
+                String source = ele.getSource();
+                String tagName = ele.getTagName();
+                if (source.contains(TagType.PICTURE.getSign())) {
+                    result.setType(TagType.PICTURE);
+                    result.setName(tagName);
+                } else if (source.contains(TagType.LOOP_ROW_TABLE.getSign())) {
+                    result.setType(TagType.LOOP_ROW_TABLE);
+                    result.setName(tagName);
+                    if (metaTemplate instanceof RunTemplate runTemplate) {
+                        XWPFRun run = runTemplate.getRun();
+                        XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
+                        XWPFTable table = tagCell.getTableRow().getTable();
+                        int templateRowIndex = WPUtils.getTemplateRowIndex(tagCell, false);
+
+                        XWPFTableRow row = table.getRow(templateRowIndex);
+                        List<String> fields = new ArrayList<>();
+                        for (XWPFTableCell cell : row.getTableCells()) {
+                            String text = cell.getText();
+
+                            Matcher matcher = pattern.matcher(text);
+
+                            while (matcher.find()) {
+                                String colTag = matcher.group(1);
+                                fields.add(colTag);
+                            }
+                        }
+                        result.setFields(fields);
+                    }
+                }
+
+                else {
+                    result.setType(TagType.TEXT);
+                    result.setName(tagName);
+                }
+            }
+            results.add(result);
+        }
+
+        return results;
+    }
+}
diff --git a/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/constant/TagType.java b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/constant/TagType.java
new file mode 100644
index 0000000..f862a78
--- /dev/null
+++ b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/constant/TagType.java
@@ -0,0 +1,30 @@
+package com.wmyun.farmwork.word.core.model.constant;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @Classname TagType
+ * @Description TODO
+ * @Date 2025/3/1 11:10
+ * @Created by violet
+ */
+
+@Getter
+@AllArgsConstructor
+public enum TagType {
+
+    TEXT("TEXT", ""),
+
+    PICTURE("PICTURE", "@"),
+
+//    NEW_TABLE("NEW_TABLE", "#"),
+
+    LOOP_ROW_TABLE("LOOP_ROW_TABLE", "&");
+
+//    LOOP_COL_TABLE("LOOP_COL_TABLE", "*");
+
+    private final String type;
+
+    private final String sign;
+}
diff --git a/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/exec/TemplateExecDataModel.java b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/exec/TemplateExecDataModel.java
new file mode 100644
index 0000000..c3a5a97
--- /dev/null
+++ b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/exec/TemplateExecDataModel.java
@@ -0,0 +1,23 @@
+package com.wmyun.farmwork.word.core.model.exec;
+
+import com.wmyun.farmwork.word.core.model.type.TypeImage;
+import com.wmyun.farmwork.word.core.model.type.TypeText;
+import lombok.*;
+
+import java.util.List;
+
+/**
+ * @Description: TODO
+ * @Date: 2025/3/1 11:02
+ * @Created: by @ZZSLL
+ */
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class TemplateExecDataModel {
+
+    private List<TypeText> text;
+
+    private List<TypeImage> image;
+}
diff --git a/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/gen/TagsGenDataModel.java b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/gen/TagsGenDataModel.java
new file mode 100644
index 0000000..14121b8
--- /dev/null
+++ b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/gen/TagsGenDataModel.java
@@ -0,0 +1,34 @@
+package com.wmyun.farmwork.word.core.model.gen;
+
+import com.wmyun.farmwork.word.core.model.constant.TagType;
+import lombok.*;
+
+import java.util.List;
+
+/**
+ * @Description: 解析模板内所有标签,生成的DataModel,该数据将用于生成 com.wmyun.farmwork.word.core.model.exec.TemplateExecDataModel
+ * @Date: 2025/3/1 11:05
+ * @Created: by ZZSLL
+ */
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class TagsGenDataModel {
+
+    /**
+     * 数据类型 可选 文本、图片、 表格
+     */
+    private TagType type;
+
+    /**
+     * 标签名称
+     */
+    private String name;
+
+    /**
+     * 标签包含字段,如果 type为表格,fields为表格内字段
+     */
+    private List<String> fields;
+
+}
diff --git a/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/type/TypeImage.java b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/type/TypeImage.java
new file mode 100644
index 0000000..d2c78fd
--- /dev/null
+++ b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/type/TypeImage.java
@@ -0,0 +1,50 @@
+package com.wmyun.farmwork.word.core.model.type;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * @Description: TODO
+ * @Date: 2025/3/1 14:12
+ * @Created: by ZZSLL
+ */
+
+@Data
+@AllArgsConstructor
+public class TypeImage {
+
+    /**
+     * 标签名称
+     */
+    private String name;
+
+    /**
+     * 标签值
+     */
+    private String value;
+
+    /**
+     * 图片格式
+     */
+    private String type;
+
+    /**
+     * 宽度
+     */
+    private Integer width;
+
+    /**
+     * 高度
+     */
+    private Integer height;
+
+    /**
+     * 当无法获取图片时展示的文字
+     */
+    private String altMeta;
+
+    /**
+     * 标签值类型,绝对路径<path>、下载url<url>
+     */
+    private String valueType;
+}
diff --git a/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/type/TypeTable.java b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/type/TypeTable.java
new file mode 100644
index 0000000..39d75b1
--- /dev/null
+++ b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/type/TypeTable.java
@@ -0,0 +1,27 @@
+package com.wmyun.farmwork.word.core.model.type;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: TODO
+ * @Date: 2025/3/1 14:19
+ * @Created: by ZZSLL
+ */
+
+@Data
+@AllArgsConstructor
+public class TypeTable {
+    /**
+     * 标签名称
+     */
+    private String name;
+
+    /**
+     * 标签值
+     */
+    private List<Map<String, String>> value;
+}
diff --git a/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/type/TypeText.java b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/type/TypeText.java
new file mode 100644
index 0000000..e3932ae
--- /dev/null
+++ b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/model/type/TypeText.java
@@ -0,0 +1,75 @@
+package com.wmyun.farmwork.word.core.model.type;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * @Description: TODO
+ * @Date: 2025/3/1 14:07
+ * @Created: by ZZSLL
+ */
+
+@Data
+@AllArgsConstructor
+public class TypeText {
+
+    /**
+     * 标签名称
+     */
+    private String name;
+
+    /**
+     * 标签值
+     */
+    private String value;
+
+    /**
+     * 删除线
+     */
+    private Boolean strike;
+
+    /**
+     * 粗体
+     */
+    private Boolean bold;
+
+    /**
+     * 斜体
+     */
+    private Boolean italic;
+
+    /**
+     * 颜色
+     */
+    private String color;
+
+    /**
+     * 	下划线
+     */
+    private Boolean underline;
+
+    /**
+     * 字体
+     */
+    private String fontFamily;
+
+    /**
+     * 	字号
+     */
+    private Integer fontSize;
+
+    /**
+     * 背景高亮色
+     */
+    private String highlightColor;
+
+    /**
+     * 上标或者下标
+     */
+    private String vertAlign;
+
+    /**
+     * 	间距
+     */
+    private Integer characterSpacing;
+}
diff --git a/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/utils/WPUtils.java b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/utils/WPUtils.java
new file mode 100644
index 0000000..45f07b4
--- /dev/null
+++ b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/core/utils/WPUtils.java
@@ -0,0 +1,27 @@
+package com.wmyun.farmwork.word.core.utils;
+
+import org.apache.poi.xwpf.usermodel.XWPFTableCell;
+import org.apache.poi.xwpf.usermodel.XWPFTableRow;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @Description: TODO
+ * @Date: 2025/3/1 11:03
+ * @Created: by ZZSLL
+ */
+
+
+public class WPUtils {
+
+    public static int getTemplateRowIndex(XWPFTableCell tagCell, boolean onSameLine) {
+        XWPFTableRow tagRow = tagCell.getTableRow();
+        return onSameLine ? getRowIndex(tagRow) : (getRowIndex(tagRow) + 1);
+    }
+
+    private static int getRowIndex(XWPFTableRow row) {
+        List<XWPFTableRow> rows = row.getTable().getRows();
+        return rows.indexOf(row);
+    }
+}
diff --git a/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/package-info.java b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/package-info.java
new file mode 100644
index 0000000..d6b730e
--- /dev/null
+++ b/wmyun-framework/wmyun-spring-boot-starter-word/src/main/java/com/wmyun/farmwork/word/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * word 模板组件
+ */
+package com.wmyun.farmwork.word;
diff --git a/wmyun-module-infra/wmyun-module-infra-biz/pom.xml b/wmyun-module-infra/wmyun-module-infra-biz/pom.xml
index 1df2282..44d1348 100644
--- a/wmyun-module-infra/wmyun-module-infra-biz/pom.xml
+++ b/wmyun-module-infra/wmyun-module-infra-biz/pom.xml
@@ -147,6 +147,11 @@
             <artifactId>tika-core</artifactId> <!-- 文件客户端:文件类型的识别 -->
         </dependency>
 
+        <dependency>
+            <groupId>com.wmyun</groupId>
+            <artifactId>wmyun-spring-boot-starter-word</artifactId>
+            <version>2.3.0-SNAPSHOT</version>
+        </dependency>
     </dependencies>
     <build>
         <!-- 设置构建的 jar 包名 -->
diff --git a/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/controller/admin/word/WordTemplateController.java b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/controller/admin/word/WordTemplateController.java
new file mode 100644
index 0000000..0e20b09
--- /dev/null
+++ b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/controller/admin/word/WordTemplateController.java
@@ -0,0 +1,37 @@
+package com.wmyun.module.infra.controller.admin.word;
+
+import com.wmyun.framework.common.exception.ErrorCode;
+import com.wmyun.framework.common.pojo.CommonResult;
+import com.wmyun.module.infra.service.file.WordTemplateTagService;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @Description: TODO
+ * @Date: 2025/3/1 10:31
+ * @Created: by ZZSLL
+ */
+
+@Tag(name = "管理后台 - Word模板")
+@RestController
+@RequestMapping("/infra/word")
+public class WordTemplateController {
+
+    @Autowired
+    private WordTemplateTagService wordService;
+
+    @PostMapping("file/{fileId}")
+    public CommonResult<Object> requestAllTags(@PathVariable("fileId") String fileId) {
+        try {
+            return CommonResult.success(wordService.queryAllTag(fileId));
+        } catch (Exception e) {
+            e.printStackTrace();
+            return CommonResult.error(500, e.getMessage());
+        }
+    }
+
+}
diff --git a/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/FilePreviewService.java b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/FilePreviewService.java
index babec72..60d87e7 100644
--- a/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/FilePreviewService.java
+++ b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/FilePreviewService.java
@@ -1,6 +1,7 @@
 package com.wmyun.module.infra.service.file;
 
 import com.wmyun.module.infra.controller.admin.file.vo.preview.CheckFileInfoVo;
+import com.wmyun.module.infra.dal.dataobject.file.FileDO;
 import jakarta.servlet.http.HttpServletResponse;
 
 import java.io.InputStream;
@@ -16,6 +17,8 @@ public interface FilePreviewService {
 
     CheckFileInfoVo createPreviewInfo(String fileId);
 
+    FileDO queryFileInfoByFileId(String fileId);
+
     void saveFile(String fileId, InputStream content, Long size);
 
     void getFileContent(String fileId, HttpServletResponse response);
diff --git a/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/FilePreviewServiceImpl.java b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/FilePreviewServiceImpl.java
index b972529..d298adb 100644
--- a/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/FilePreviewServiceImpl.java
+++ b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/FilePreviewServiceImpl.java
@@ -84,7 +84,7 @@ public class FilePreviewServiceImpl implements FilePreviewService {
      * @param fileId 文件id
      * @return
      */
-    private FileDO queryFileInfoByFileId(String fileId) {
+    public FileDO queryFileInfoByFileId(String fileId) {
         LambdaQueryWrapperX<FileDO> queryWrapperX = new LambdaQueryWrapperX<>();
         queryWrapperX.eq(FileDO::getId, fileId).eq(FileDO::getDeleted, false);
         List<FileDO> files = fileMapper.selectList(queryWrapperX);
diff --git a/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagService.java b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagService.java
new file mode 100644
index 0000000..8898892
--- /dev/null
+++ b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagService.java
@@ -0,0 +1,13 @@
+package com.wmyun.module.infra.service.file;
+
+/**
+ * @Classname WordTemplateTagService
+ * @Description TODO
+ * @Date 2025/3/1 10:32
+ * @Created by violet
+ */
+
+public interface WordTemplateTagService {
+
+    Object queryAllTag(String fileId) throws Exception;
+}
diff --git a/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagServiceImpl.java b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagServiceImpl.java
new file mode 100644
index 0000000..8051bd4
--- /dev/null
+++ b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagServiceImpl.java
@@ -0,0 +1,47 @@
+package com.wmyun.module.infra.service.file;
+
+import com.wmyun.farmwork.word.core.TemplateTagsGenerate;
+import com.wmyun.farmwork.word.core.model.gen.TagsGenDataModel;
+import com.wmyun.framework.common.util.io.IoUtils;
+import com.wmyun.module.infra.dal.dataobject.file.FileDO;
+import com.wmyun.module.infra.dal.mysql.file.FileMapper;
+import com.wmyun.module.infra.framework.file.core.client.FileClient;
+import jakarta.annotation.Resource;
+import jodd.io.IOUtil;
+import org.apache.commons.io.IOUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+/**
+ * @Description: TODO
+ * @Date: 2025/3/1 10:33
+ * @Created: by ZZSLL
+ */
+
+@Service
+public class WordTemplateTagServiceImpl implements WordTemplateTagService {
+
+    @Autowired
+    private FilePreviewService previewService;
+
+    @Autowired
+    private FileConfigService fileConfigService;
+
+    @Override
+    public Object queryAllTag(String fileId) throws Exception {
+        FileDO file = previewService.queryFileInfoByFileId(fileId);
+        FileClient client = fileConfigService.getFileClient(file.getConfigId());
+        byte[] content = client.getContent(file.getPath());
+
+        String name = file.getName();
+        Path tempDir = Paths.get(System.getProperty("java.io.tmpdir"));
+        Path targetPath = tempDir.resolve(name);
+        Files.write(targetPath, content);
+        return TemplateTagsGenerate.execAllTags(targetPath.toAbsolutePath().toString());
+    }
+}

From 3bb6a3249fc37dfbe57e20b2a84230339a251ad0 Mon Sep 17 00:00:00 2001
From: zzs <hi@vio.vin>
Date: Sat, 1 Mar 2025 15:28:58 +0800
Subject: [PATCH 2/2] feat: get file tags

---
 .../infra/controller/admin/word/WordTemplateController.java | 6 ++++--
 .../module/infra/service/file/WordTemplateTagService.java   | 6 +++++-
 .../infra/service/file/WordTemplateTagServiceImpl.java      | 2 +-
 3 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/controller/admin/word/WordTemplateController.java b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/controller/admin/word/WordTemplateController.java
index 0e20b09..eb36dcc 100644
--- a/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/controller/admin/word/WordTemplateController.java
+++ b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/controller/admin/word/WordTemplateController.java
@@ -1,6 +1,6 @@
 package com.wmyun.module.infra.controller.admin.word;
 
-import com.wmyun.framework.common.exception.ErrorCode;
+import com.wmyun.farmwork.word.core.model.gen.TagsGenDataModel;
 import com.wmyun.framework.common.pojo.CommonResult;
 import com.wmyun.module.infra.service.file.WordTemplateTagService;
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -10,6 +10,8 @@ import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.List;
+
 /**
  * @Description: TODO
  * @Date: 2025/3/1 10:31
@@ -25,7 +27,7 @@ public class WordTemplateController {
     private WordTemplateTagService wordService;
 
     @PostMapping("file/{fileId}")
-    public CommonResult<Object> requestAllTags(@PathVariable("fileId") String fileId) {
+    public CommonResult<List<TagsGenDataModel>> requestAllTags(@PathVariable("fileId") String fileId) {
         try {
             return CommonResult.success(wordService.queryAllTag(fileId));
         } catch (Exception e) {
diff --git a/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagService.java b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagService.java
index 8898892..93d665b 100644
--- a/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagService.java
+++ b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagService.java
@@ -1,5 +1,9 @@
 package com.wmyun.module.infra.service.file;
 
+import com.wmyun.farmwork.word.core.model.gen.TagsGenDataModel;
+
+import java.util.List;
+
 /**
  * @Classname WordTemplateTagService
  * @Description TODO
@@ -9,5 +13,5 @@ package com.wmyun.module.infra.service.file;
 
 public interface WordTemplateTagService {
 
-    Object queryAllTag(String fileId) throws Exception;
+    List<TagsGenDataModel> queryAllTag(String fileId) throws Exception;
 }
diff --git a/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagServiceImpl.java b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagServiceImpl.java
index 8051bd4..27db6ed 100644
--- a/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagServiceImpl.java
+++ b/wmyun-module-infra/wmyun-module-infra-biz/src/main/java/com/wmyun/module/infra/service/file/WordTemplateTagServiceImpl.java
@@ -33,7 +33,7 @@ public class WordTemplateTagServiceImpl implements WordTemplateTagService {
     private FileConfigService fileConfigService;
 
     @Override
-    public Object queryAllTag(String fileId) throws Exception {
+    public List<TagsGenDataModel> queryAllTag(String fileId) throws Exception {
         FileDO file = previewService.queryFileInfoByFileId(fileId);
         FileClient client = fileConfigService.getFileClient(file.getConfigId());
         byte[] content = client.getContent(file.getPath());