feat(ai-report): 新增委托单位管理和报告审批功能
- 新增委托单位管理模块,支持增删改查、导入导出功能 - 实现委托单位 Excel 模板下载和数据导入功能 - 添加报告审批抽象处理器,支持审批流程日志记录 - 统一文件存储路径配置,将各模块独立路径改为统一根目录 - 新增 PQDIF 文件名存储字段,完善文件管理功能 - 修复代码中的乱码问题和错误提示信息 - 优化系统常量定义和字典配置管理
This commit is contained in:
58
comservice/pom.xml
Normal file
58
comservice/pom.xml
Normal file
@@ -0,0 +1,58 @@
|
||||
<?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://www.w3.org/2001/XMLSchema-instance http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.njcn.gather</groupId>
|
||||
<artifactId>CN_Tool</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>comservice</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>spingboot2.3.12</artifactId>
|
||||
<version>2.3.12</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.36</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
<version>4.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>4.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-scratchpad</artifactId>
|
||||
<version>4.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.njcn.gather.comservice.filepreview;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.apache.poi.hwpf.HWPFDocument;
|
||||
import org.apache.poi.hwpf.extractor.WordExtractor;
|
||||
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFDocument;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.util.HtmlUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* 公共文件预览服务,统一处理报告模板相关预览内容转换。
|
||||
*/
|
||||
@Component
|
||||
public class FilePreviewService {
|
||||
|
||||
private static final String PDF_EXTENSION = ".pdf";
|
||||
private static final String DOC_EXTENSION = ".doc";
|
||||
private static final String DOCX_EXTENSION = ".docx";
|
||||
private static final String HTML_CONTENT_TYPE = MediaType.TEXT_HTML_VALUE + ";charset=UTF-8";
|
||||
|
||||
public PreviewContent preview(Path filePath, String fileName) {
|
||||
String extension = resolveExtension(fileName);
|
||||
try {
|
||||
if (PDF_EXTENSION.equals(extension)) {
|
||||
return new PreviewContent(MediaType.APPLICATION_PDF_VALUE, Files.readAllBytes(filePath));
|
||||
}
|
||||
if (DOCX_EXTENSION.equals(extension)) {
|
||||
return buildHtmlPreview(extractDocxText(filePath));
|
||||
}
|
||||
if (DOC_EXTENSION.equals(extension)) {
|
||||
return buildHtmlPreview(extractDocText(filePath));
|
||||
}
|
||||
throw new IllegalArgumentException("仅支持预览 .docx、.doc、.pdf 文件");
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalArgumentException("读取文件预览内容失败", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private PreviewContent buildHtmlPreview(String text) {
|
||||
String[] lines = StrUtil.nullToEmpty(text).replace("\r\n", "\n").replace('\r', '\n').split("\n");
|
||||
StringBuilder htmlBuilder = new StringBuilder(256);
|
||||
htmlBuilder.append("<!DOCTYPE html><html><head><meta charset=\"UTF-8\">")
|
||||
.append("<title>文件预览</title>")
|
||||
.append("<style>")
|
||||
.append("body{margin:0;padding:24px;background:#f5f7fa;color:#1f2329;font:14px/1.7 'Microsoft YaHei',sans-serif;}")
|
||||
.append(".preview{max-width:960px;margin:0 auto;padding:24px;background:#fff;border-radius:8px;box-shadow:0 4px 16px rgba(31,35,41,.08);}")
|
||||
.append("p{margin:0 0 12px;white-space:pre-wrap;word-break:break-word;}")
|
||||
.append("</style></head><body><div class=\"preview\">");
|
||||
if (lines.length == 1 && StrUtil.isBlank(lines[0])) {
|
||||
htmlBuilder.append("<p>文件内容为空</p>");
|
||||
} else {
|
||||
for (String line : lines) {
|
||||
if (StrUtil.isBlank(line)) {
|
||||
htmlBuilder.append("<p> </p>");
|
||||
} else {
|
||||
htmlBuilder.append("<p>").append(HtmlUtils.htmlEscape(line)).append("</p>");
|
||||
}
|
||||
}
|
||||
}
|
||||
htmlBuilder.append("</div></body></html>");
|
||||
return new PreviewContent(HTML_CONTENT_TYPE, htmlBuilder.toString().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
private String extractDocxText(Path filePath) throws IOException {
|
||||
try (XWPFDocument document = new XWPFDocument(Files.newInputStream(filePath));
|
||||
XWPFWordExtractor extractor = new XWPFWordExtractor(document)) {
|
||||
return extractor.getText();
|
||||
}
|
||||
}
|
||||
|
||||
private String extractDocText(Path filePath) throws IOException {
|
||||
try (HWPFDocument document = new HWPFDocument(Files.newInputStream(filePath));
|
||||
WordExtractor extractor = new WordExtractor(document)) {
|
||||
return extractor.getText();
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveExtension(String fileName) {
|
||||
String normalized = StrUtil.nullToEmpty(fileName).trim().toLowerCase();
|
||||
if (normalized.endsWith(DOCX_EXTENSION)) {
|
||||
return DOCX_EXTENSION;
|
||||
}
|
||||
if (normalized.endsWith(DOC_EXTENSION)) {
|
||||
return DOC_EXTENSION;
|
||||
}
|
||||
if (normalized.endsWith(PDF_EXTENSION)) {
|
||||
return PDF_EXTENSION;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static class PreviewContent {
|
||||
private final String contentType;
|
||||
private final byte[] content;
|
||||
|
||||
public PreviewContent(String contentType, byte[] content) {
|
||||
this.contentType = contentType;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public byte[] getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.njcn.gather.comservice.filepreview;
|
||||
|
||||
import org.apache.poi.hwpf.HWPFDocument;
|
||||
import org.apache.poi.hwpf.usermodel.Range;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFDocument;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class FilePreviewServiceTest {
|
||||
|
||||
@Test
|
||||
public void previewPdfReturnsInlinePdfContent() throws Exception {
|
||||
Path file = Files.createTempFile("common-preview", ".pdf");
|
||||
byte[] content = "%PDF-1.4".getBytes(StandardCharsets.UTF_8);
|
||||
Files.write(file, content);
|
||||
FilePreviewService service = new FilePreviewService();
|
||||
|
||||
FilePreviewService.PreviewContent previewContent = service.preview(file, "sample.pdf");
|
||||
|
||||
Assert.assertEquals(MediaType.APPLICATION_PDF_VALUE, previewContent.getContentType());
|
||||
Assert.assertArrayEquals(content, previewContent.getContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void previewDocxReturnsHtmlContent() throws Exception {
|
||||
Path file = Files.createTempFile("common-preview", ".docx");
|
||||
try (XWPFDocument document = new XWPFDocument();
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
document.createParagraph().createRun().setText("第一行");
|
||||
document.createParagraph().createRun().setText("第二行<tag>");
|
||||
document.write(outputStream);
|
||||
Files.write(file, outputStream.toByteArray());
|
||||
}
|
||||
FilePreviewService service = new FilePreviewService();
|
||||
|
||||
FilePreviewService.PreviewContent previewContent = service.preview(file, "sample.docx");
|
||||
String html = new String(previewContent.getContent(), StandardCharsets.UTF_8);
|
||||
|
||||
Assert.assertEquals(MediaType.TEXT_HTML_VALUE + ";charset=UTF-8", previewContent.getContentType());
|
||||
Assert.assertTrue(html.contains("第一行"));
|
||||
Assert.assertTrue(html.contains("第二行<tag>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void previewDocReturnsHtmlContent() throws Exception {
|
||||
Path file = Files.createTempFile("common-preview", ".doc");
|
||||
try (HWPFDocument document = new HWPFDocument();
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
Range range = document.getRange();
|
||||
range.insertAfter("DOC 正文");
|
||||
document.write(outputStream);
|
||||
Files.write(file, outputStream.toByteArray());
|
||||
}
|
||||
FilePreviewService service = new FilePreviewService();
|
||||
|
||||
FilePreviewService.PreviewContent previewContent = service.preview(file, "sample.doc");
|
||||
String html = new String(previewContent.getContent(), StandardCharsets.UTF_8);
|
||||
|
||||
Assert.assertEquals(MediaType.TEXT_HTML_VALUE + ";charset=UTF-8", previewContent.getContentType());
|
||||
Assert.assertTrue(html.contains("DOC 正文"));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void previewRejectsUnsupportedExtension() throws Exception {
|
||||
Path file = Files.createTempFile("common-preview", ".txt");
|
||||
Files.write(file, "text".getBytes(StandardCharsets.UTF_8));
|
||||
FilePreviewService service = new FilePreviewService();
|
||||
|
||||
service.preview(file, "sample.txt");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user