feat: get file tags

This commit is contained in:
zzs 2025-03-01 15:27:56 +08:00
parent 965429050c
commit bccdbb6a55
19 changed files with 636 additions and 1 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -0,0 +1,4 @@
/**
* word 模板组件
*/
package com.wmyun.farmwork.word;

View File

@ -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 包名 -->

View File

@ -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());
}
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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());
}
}