data, FormMetaData e);
+}
diff --git a/common/src/main/java/com/yyy/common/factory/word/strategy/labels/ChartMultiSeriesReferenceImportStrategy.java b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/ChartMultiSeriesReferenceImportStrategy.java
new file mode 100644
index 0000000..efd9da7
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/ChartMultiSeriesReferenceImportStrategy.java
@@ -0,0 +1,43 @@
+package com.yyy.common.factory.word.strategy.labels;
+
+import com.deepoove.poi.data.ChartMultiSeriesRenderData;
+import com.yyy.common.factory.word.AbstractImportStrategy;
+import com.yyy.common.utils.poi.FormMetaData;
+
+import java.util.Map;
+
+/**
+ * Word引用标签:多系列图标
+ *
+ * json样例:
+ * {
+ * "chartTitle": "ChartTitle",
+ * "categories": [
+ * "中文", "English"
+ * ],
+ * "seriesDatas": [
+ * {
+ * "name": "countries",
+ * "values": [
+ * 15, 6
+ * ]
+ * },
+ * {
+ * "name": "speakers",
+ * "values": [
+ * 223, 119
+ * ]
+ * }
+ * ]
+ * }
+ */
+public class ChartMultiSeriesReferenceImportStrategy extends AbstractImportStrategy {
+ @Override
+ public void importData(Map data, FormMetaData e) {
+ if (e.getValue() instanceof ChartMultiSeriesRenderData) {
+ data.put(e.getKey(), e.getValue());
+ } else {
+ //System.out.println("数据格式错误");
+ }
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/factory/word/strategy/labels/ChartSingleSeriesReferenceImportStrategy.java b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/ChartSingleSeriesReferenceImportStrategy.java
new file mode 100644
index 0000000..038ada5
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/ChartSingleSeriesReferenceImportStrategy.java
@@ -0,0 +1,37 @@
+package com.yyy.common.factory.word.strategy.labels;
+
+import com.deepoove.poi.data.ChartSingleSeriesRenderData;
+import com.yyy.common.factory.word.AbstractImportStrategy;
+import com.yyy.common.utils.poi.FormMetaData;
+
+import java.util.Map;
+
+/**
+ * Word引用标签:单系列图标
+ *
+ * json样例:
+ * {
+ * "chartTitle": "ChartTitle",
+ * "categories": [
+ * "美国",
+ * "中国"
+ * ],
+ * "seriesData": {
+ * "name": "countries",
+ * "values": [
+ * 9826675,
+ * 9596961
+ * ]
+ * }
+ * }
+ */
+public class ChartSingleSeriesReferenceImportStrategy extends AbstractImportStrategy {
+ @Override
+ public void importData(Map data, FormMetaData e) {
+ if (e.getValue() instanceof ChartSingleSeriesRenderData) {
+ data.put(e.getKey(), e.getValue());
+ } else {
+ //System.out.println("数据格式错误");
+ }
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/factory/word/strategy/labels/ChartsReferenceImportStrategy.java b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/ChartsReferenceImportStrategy.java
new file mode 100644
index 0000000..5902d02
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/ChartsReferenceImportStrategy.java
@@ -0,0 +1,43 @@
+package com.yyy.common.factory.word.strategy.labels;
+
+import com.deepoove.poi.data.ChartMultiSeriesRenderData;
+import com.yyy.common.factory.word.AbstractImportStrategy;
+import com.yyy.common.utils.poi.FormMetaData;
+
+import java.util.Map;
+
+/**
+ * Word引用标签:组合图表
+ *
+ * json样例:
+ * {
+ * "chartTitle": "ChartTitle",
+ * "categories": [
+ * "中文", "English"
+ * ],
+ * "seriesDatas": [
+ * {
+ * "name": "countries",
+ * "values": [
+ * 15, 6
+ * ]
+ * },
+ * {
+ * "name": "speakers",
+ * "values": [
+ * 223, 119
+ * ]
+ * }
+ * ]
+ * }
+ */
+public class ChartsReferenceImportStrategy extends AbstractImportStrategy {
+ @Override
+ public void importData(Map data, FormMetaData e) {
+ if (e.getValue() instanceof ChartMultiSeriesRenderData) {
+ data.put(e.getKey(), e.getValue());
+ } else {
+ //System.out.println("数据格式错误");
+ }
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/factory/word/strategy/labels/ImgReferenceImportStrategy.java b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/ImgReferenceImportStrategy.java
new file mode 100644
index 0000000..815737a
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/ImgReferenceImportStrategy.java
@@ -0,0 +1,22 @@
+package com.yyy.common.factory.word.strategy.labels;
+
+import com.deepoove.poi.data.Pictures;
+import com.yyy.common.factory.word.AbstractImportStrategy;
+import com.yyy.common.utils.poi.FormMetaData;
+
+import java.util.Map;
+
+/**
+ * Word引用标签:图片
+ *
+ */
+public class ImgReferenceImportStrategy extends AbstractImportStrategy {
+ @Override
+ public void importData(Map data, FormMetaData e) {
+ if (e.getValue() instanceof String) {
+ data.put(e.getKey(), Pictures.ofLocal((String) e.getValue()).create());
+ } else {
+ //System.out.println("数据格式错误");
+ }
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/factory/word/strategy/labels/IncludeImportStrategy.java b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/IncludeImportStrategy.java
new file mode 100644
index 0000000..86a8a15
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/IncludeImportStrategy.java
@@ -0,0 +1,23 @@
+package com.yyy.common.factory.word.strategy.labels;
+
+import com.deepoove.poi.data.DocxRenderData;
+import com.yyy.common.factory.word.AbstractImportStrategy;
+import com.yyy.common.utils.poi.FormMetaData;
+
+import java.util.Map;
+
+/**
+ * Word标签:嵌套
+ *
+ * json样例:
+ */
+public class IncludeImportStrategy extends AbstractImportStrategy {
+ @Override
+ public void importData(Map data, FormMetaData e) {
+ if (e.getValue() instanceof DocxRenderData) {
+ data.put(e.getKey(), e.getValue());
+ } else {
+ //System.out.println("数据格式错误");
+ }
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/factory/word/strategy/labels/LoopColumnTableImportStrategy.java b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/LoopColumnTableImportStrategy.java
new file mode 100644
index 0000000..69aafb6
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/LoopColumnTableImportStrategy.java
@@ -0,0 +1,28 @@
+package com.yyy.common.factory.word.strategy.labels;
+
+import com.alibaba.fastjson.JSONObject;
+import com.yyy.common.factory.word.AbstractImportStrategy;
+import com.yyy.common.utils.poi.FormMetaData;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Word插件标签:表格列循环
+ *
+ * json样例:
+
+ */
+public class LoopColumnTableImportStrategy extends AbstractImportStrategy {
+ @Override
+ public void importData(Map data, FormMetaData e) {
+ if (e.getValue() instanceof List) {
+ data.put(e.getKey(), e.getValue());
+ } else {
+ JSONObject config = JSONObject.parseObject(JSONObject.toJSONString(e.getValue()));
+ if (config.containsKey("value")){
+ data.put(e.getKey(), config.getJSONArray("value"));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/yyy/common/factory/word/strategy/labels/LoopRowTableImportStrategy.java b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/LoopRowTableImportStrategy.java
new file mode 100644
index 0000000..3a9c44c
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/LoopRowTableImportStrategy.java
@@ -0,0 +1,52 @@
+package com.yyy.common.factory.word.strategy.labels;
+
+import com.alibaba.fastjson.JSONObject;
+import com.yyy.common.factory.word.AbstractImportStrategy;
+import com.yyy.common.utils.poi.FormMetaData;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Word插件标签:表格行循环
+ *
+ * json样例:
+
+ */
+public class LoopRowTableImportStrategy extends AbstractImportStrategy {
+ @Override
+ public void importData(Map data, FormMetaData e) {
+ if (e.getValue() instanceof List) {
+ data.put(e.getKey(), e.getValue());
+ } else {
+// {
+// "fields": [
+// "key1",
+// "key2",
+// "key3"
+// ],
+// "value": [
+// {
+// "key1": "value1",
+// "key2": "value2",
+// "key3": "value3"
+// },
+// {
+// "key1": "value11",
+// "key2": "value22",
+// "key3": "value33"
+// },
+// {
+// "key1": "value111",
+// "key2": "value222",
+// "key3": "value333"
+// }
+// ]
+// }
+ JSONObject config = JSONObject.parseObject(JSONObject.toJSONString(e.getValue()));
+ if (config.containsKey("value")){
+ data.put(e.getKey(), config.getJSONArray("value"));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/yyy/common/factory/word/strategy/labels/NumberingImportStrategy.java b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/NumberingImportStrategy.java
new file mode 100644
index 0000000..cba2161
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/NumberingImportStrategy.java
@@ -0,0 +1,22 @@
+package com.yyy.common.factory.word.strategy.labels;
+
+import com.deepoove.poi.data.NumberingRenderData;
+import com.yyy.common.factory.word.AbstractImportStrategy;
+import com.yyy.common.utils.poi.FormMetaData;
+
+import java.util.Map;
+
+/**
+ * Word标签:列表
+ *
+ */
+public class NumberingImportStrategy extends AbstractImportStrategy {
+ @Override
+ public void importData(Map data, FormMetaData e) {
+ if (e.getValue() instanceof NumberingRenderData) {
+ data.put(e.getKey(), e.getValue());
+ } else {
+ //System.out.println("数据格式错误");
+ }
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/factory/word/strategy/labels/PictureImportStrategy.java b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/PictureImportStrategy.java
new file mode 100644
index 0000000..a85c45e
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/PictureImportStrategy.java
@@ -0,0 +1,32 @@
+package com.yyy.common.factory.word.strategy.labels;
+
+import com.deepoove.poi.data.PictureRenderData;
+import com.yyy.common.factory.word.AbstractImportStrategy;
+import com.yyy.common.utils.poi.FormMetaData;
+
+import java.util.Map;
+
+/**
+ * Word标签:图片
+ *
+ * json样例:
+ * {
+ * "pictureType" : "PNG",
+ * "path": "logo.png",
+ * "pictureStyle": {
+ * "width": 100,
+ * "height": 100
+ * },
+ * "altMeta": "图片不存在"
+ * }
+ */
+public class PictureImportStrategy extends AbstractImportStrategy {
+ @Override
+ public void importData(Map data, FormMetaData e) {
+ if (e.getValue() instanceof String || e.getValue() instanceof PictureRenderData) {
+ data.put(e.getKey(), e.getValue());
+ } else {
+ //System.out.println("数据格式错误");
+ }
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/factory/word/strategy/labels/SectionImportStrategy.java b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/SectionImportStrategy.java
new file mode 100644
index 0000000..65d3adf
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/SectionImportStrategy.java
@@ -0,0 +1,59 @@
+package com.yyy.common.factory.word.strategy.labels;
+
+
+import com.alibaba.fastjson.JSONObject;
+import com.yyy.common.factory.word.AbstractImportStrategy;
+import com.yyy.common.utils.poi.FormMetaData;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Word标签:区块对
+ *
+ * json样例:
+ * {
+ * "announce": false
+ * }
+ *
+ *
+ * template.docx
+ * Made it,Ma!{{?announce}}Top of the world!{{/announce}}
+ *
+ * Made it,Ma!
+ *
+ * {{?announce}}
+ *
+ * Top of the world!
+ *
+ * {{/announce}}
+ */
+public class SectionImportStrategy extends AbstractImportStrategy {
+ //当数据为false,null,集合大小为0时,区块对的内容不展示
+ // map.put("section1",false);
+ // map.put("section2",null);
+ // map.put("section3",new ArrayList());
+ // String name = "jack";
+ // map.put("section5",name);
+ // HashMap stringObjectHashMap = new HashMap<>();
+ // stringObjectHashMap.put("value",strings);
+ // map.put("section6",stringObjectHashMap);
+ @Override
+ public void importData(Map data, FormMetaData e) {
+// "value": {
+// "fields": [
+// "选项1",
+// "选项2",
+// "选项3"
+// ],
+// "value": "选项1"
+// }
+
+ JSONObject config = JSONObject.parseObject(JSONObject.toJSONString(e.getValue()));
+ if (config.containsKey("value")){
+ Map value = new HashMap();
+ value.put(config.getString("value"), "√");
+ data.put(e.getKey(), value);
+ }
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/factory/word/strategy/labels/TableImportStrategy.java b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/TableImportStrategy.java
new file mode 100644
index 0000000..c174b09
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/TableImportStrategy.java
@@ -0,0 +1,62 @@
+package com.yyy.common.factory.word.strategy.labels;
+
+import com.deepoove.poi.data.TableRenderData;
+import com.yyy.common.factory.word.AbstractImportStrategy;
+import com.yyy.common.utils.poi.FormMetaData;
+
+import java.util.Map;
+
+/**
+ * Word标签:表格
+ *
+ * json样例:
+ * {
+ * "rows": [
+ * {
+ * "cells": [
+ * {
+ * "paragraphs": [
+ * {
+ * "contents": [
+ * {
+ * [TextRenderData]
+ * },
+ * {
+ * [PictureRenderData]
+ * }
+ * ],
+ * "paragraphStyle": null
+ * }
+ * ],
+ * "cellStyle": {
+ * "backgroundColor": "00000",
+ * "vertAlign": "CENTER"
+ * }
+ * }
+ * ],
+ * "rowStyle": {
+ * "height": 2.0f
+ * }
+ * }
+ * ],
+ * "tableStyle": {
+ * "width": 14.63f,
+ * "colWidths": null
+ * },
+ * "mergeRule": {
+ * "mapping": {
+ * "0-0": "1-2"
+ * }
+ * }
+ * }
+ */
+public class TableImportStrategy extends AbstractImportStrategy {
+ @Override
+ public void importData(Map data, FormMetaData e) {
+ if (e.getValue() instanceof TableRenderData) {
+ data.put(e.getKey(), e.getValue());
+ } else {
+ //System.out.println("数据格式错误");
+ }
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/factory/word/strategy/labels/TextImportStrategy.java b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/TextImportStrategy.java
new file mode 100644
index 0000000..5d65ace
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/factory/word/strategy/labels/TextImportStrategy.java
@@ -0,0 +1,38 @@
+package com.yyy.common.factory.word.strategy.labels;
+
+import com.deepoove.poi.data.TextRenderData;
+import com.yyy.common.factory.word.AbstractImportStrategy;
+import com.yyy.common.utils.poi.FormMetaData;
+
+import java.util.Map;
+
+/**
+ * Word标签:文本
+ *
+ * json样例:
+ * {
+ * "text": "Sayi",
+ * "style": {
+ * "strike": false,
+ * "bold": true,
+ * "italic": false,
+ * "color": "00FF00",
+ * "underLine": false,
+ * "fontFamily": "微软雅黑",
+ * "fontSize": 12,
+ * "highlightColor": "green",
+ * "vertAlign": "superscript",
+ * "characterSpacing" : 20
+ * }
+ * }
+ */
+public class TextImportStrategy extends AbstractImportStrategy {
+ @Override
+ public void importData(Map data, FormMetaData e) {
+ if (e.getValue() instanceof String || e.getValue() instanceof TextRenderData) {
+ data.put(e.getKey(), e.getValue());
+ } else {
+ //System.out.println("数据格式错误");
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/yyy/common/factory/word/strategy/plugins/MyLoopRowTableRenderPolicy.java b/common/src/main/java/com/yyy/common/factory/word/strategy/plugins/MyLoopRowTableRenderPolicy.java
new file mode 100644
index 0000000..bbc4242
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/factory/word/strategy/plugins/MyLoopRowTableRenderPolicy.java
@@ -0,0 +1,158 @@
+package com.yyy.common.factory.word.strategy.plugins;
+
+import com.deepoove.poi.XWPFTemplate;
+import com.deepoove.poi.exception.RenderException;
+import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
+import com.deepoove.poi.policy.RenderPolicy;
+import com.deepoove.poi.render.compute.EnvModel;
+import com.deepoove.poi.render.compute.RenderDataCompute;
+import com.deepoove.poi.render.processor.DocumentProcessor;
+import com.deepoove.poi.render.processor.EnvIterator;
+import com.deepoove.poi.resolver.TemplateResolver;
+import com.deepoove.poi.template.ElementTemplate;
+import com.deepoove.poi.template.MetaTemplate;
+import com.deepoove.poi.template.run.RunTemplate;
+import com.deepoove.poi.util.ReflectionUtils;
+import com.deepoove.poi.util.TableTools;
+import org.apache.poi.xwpf.usermodel.*;
+import org.apache.xmlbeans.XmlCursor;
+import org.apache.xmlbeans.XmlObject;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * 完善对空白行的处理
+ */
+public class MyLoopRowTableRenderPolicy implements RenderPolicy {
+ private String prefix;
+ private String suffix;
+ private boolean onSameLine;
+
+ public MyLoopRowTableRenderPolicy() {
+ this(false);
+ }
+
+ public MyLoopRowTableRenderPolicy(boolean onSameLine) {
+ this("[", "]", onSameLine);
+ }
+
+ public MyLoopRowTableRenderPolicy(String prefix, String suffix) {
+ this(prefix, suffix, false);
+ }
+
+ public MyLoopRowTableRenderPolicy(String prefix, String suffix, boolean onSameLine) {
+ this.prefix = prefix;
+ this.suffix = suffix;
+ this.onSameLine = onSameLine;
+ }
+
+ public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
+ RunTemplate runTemplate = (RunTemplate) eleTemplate;
+ XWPFRun run = runTemplate.getRun();
+ try {
+ if (!TableTools.isInsideTable(run)) {
+ throw new IllegalStateException("The template tag " + runTemplate.getSource() + " must be inside a table");
+ } else {
+ // 获取包含模板标签的单元格
+ XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
+ XWPFTable table = tagCell.getTableRow().getTable();
+ run.setText("", 0); // 清空运行对象的文本内容
+ int templateRowIndex = this.getTemplateRowIndex(tagCell); // 获取模板行的索引
+ if (data != null && data instanceof Iterable) {
+ Iterator> iterator = ((Iterable>) data).iterator();
+ XWPFTableRow templateRow = table.getRow(templateRowIndex);
+ TemplateResolver resolver = new TemplateResolver(template.getConfig().copy(this.prefix, this.suffix));
+ boolean firstFlag = true;
+ int index = 0;
+ boolean hasNext = iterator.hasNext();
+ while (hasNext) {
+ Object root = iterator.next();
+ hasNext = iterator.hasNext();
+ int insertPosition = templateRowIndex++;
+ // 检查插入位置下方是否有空白行
+ boolean hasEmptyRowBelow = false;
+ if (insertPosition < table.getNumberOfRows()) {
+ XWPFTableRow rowBelow = table.getRow(insertPosition);
+ if (isRowEmpty(rowBelow)) {
+ hasEmptyRowBelow = true;
+ }
+ }
+ // 仅当下方没有空白行时,才插入新行
+ if (!hasEmptyRowBelow) {
+ // 新建一行
+ table.insertNewTableRow(insertPosition);
+ this.setTableRow(table, templateRow, insertPosition);
+ // 获取新插入行的游标
+ XmlCursor newCursor = templateRow.getCtRow().newCursor();
+ newCursor.toPrevSibling();
+ XmlObject object = newCursor.getObject();
+ XWPFTableRow nextRow = new XWPFTableRow((CTRow) object, table);
+ if (!firstFlag) {
+ // 处理单元格的垂直合并属性
+ for (XWPFTableCell cell : nextRow.getTableCells()) {
+ CTTcPr tcPr = TableTools.getTcPr(cell);
+ CTVMerge vMerge = tcPr.getVMerge();
+ if (vMerge != null && STMerge.RESTART == vMerge.getVal()) {
+ vMerge.setVal(STMerge.CONTINUE);
+ }
+ }
+ } else {
+ firstFlag = false;
+ }
+ this.setTableRow(table, nextRow, insertPosition);
+ // 计算渲染数据
+ RenderDataCompute dataCompute = template.getConfig()
+ .getRenderDataComputeFactory()
+ .newCompute(EnvModel.of(root, EnvIterator.makeEnv(index++, hasNext)));
+ // 渲染每个单元格
+ List cells = nextRow.getTableCells();
+ for (XWPFTableCell cell : cells) {
+ List templates = resolver.resolveBodyElements(cell.getBodyElements());
+ new DocumentProcessor(template, resolver, dataCompute).process(templates);
+ }
+ }
+ }
+ }
+ // 移除模板行
+ table.removeRow(templateRowIndex);
+ this.afterloop(table, data);
+ }
+ } catch (Exception e) {
+ throw new RenderException("HackLoopTable for " + eleTemplate + " error: " + e.getMessage(), e);
+ }
+ }
+
+ private int getTemplateRowIndex(XWPFTableCell tagCell) {
+ XWPFTableRow tagRow = tagCell.getTableRow();
+ return this.onSameLine ? this.getRowIndex(tagRow) : this.getRowIndex(tagRow) + 1;
+ }
+
+ protected void afterloop(XWPFTable table, Object data) {
+ }
+
+ private void setTableRow(XWPFTable table, XWPFTableRow templateRow, int pos) {
+ List rows = (List) ReflectionUtils.getValue("tableRows", table);
+ rows.set(pos, templateRow);
+ table.getCTTbl().setTrArray(pos, templateRow.getCtRow());
+ }
+
+ private int getRowIndex(XWPFTableRow row) {
+ List rows = row.getTable().getRows();
+ return rows.indexOf(row);
+ }
+
+ private boolean isRowEmpty(XWPFTableRow row) {
+ for (XWPFTableCell cell : row.getTableCells()) {
+ String text = cell.getText();
+ if (text != null && !text.trim().isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/form/FieldData.java b/common/src/main/java/com/yyy/common/form/FieldData.java
new file mode 100644
index 0000000..ca6c54d
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/form/FieldData.java
@@ -0,0 +1,68 @@
+package com.yyy.common.form;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.yyy.common.annotation.excel.Excel;
+import com.yyy.common.enums.FieldDataTypeEnum;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 表单字段基本单元
+ */
+@Data
+@AllArgsConstructor
+@Builder
+@ApiModel("表单字段基本单元")
+public class FieldData implements Serializable {
+ private static final long serialVersionUID = 1L;
+ /**
+ * 标识符
+ */
+ @ApiModelProperty(notes = "标识符")
+ @Excel(name = "标识符")
+ private String key;
+
+ /**
+ * 名称
+ */
+ @ApiModelProperty(notes = "名称")
+ @Excel(name = "名称")
+ private String name;
+
+ /**
+ * 数据
+ */
+ @ApiModelProperty(notes = "数据")
+ @Excel(name = "数据")
+ private Object value;
+
+ /**
+ * 类型
+ */
+ @ApiModelProperty(notes = "类型 (String=文本,Number=数字,Date=日期,Radio=单选框,List=列表数据)")
+ @Excel(name = "类型", readConverterExp = "String=文本,Number=数字,Date=日期,Radio=单选框,List=列表数据", combo = "文本,数字,日期,单选框,列表数据")
+ private FieldDataTypeEnum type;
+
+ /**
+ * 排序(从小到大) 默认为0
+ */
+ @ApiModelProperty(notes = "数据(默认值为0)")
+ private Integer sort;
+
+ @ApiModelProperty(notes = "备注")
+ private String remark;
+
+ public void setType(String type) {
+ this.type = FieldDataTypeEnum.fromTypeName(type);
+ }
+
+ public FieldData() {
+ this.sort = 0;
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/utils/bean/BeanUtils.java b/common/src/main/java/com/yyy/common/utils/bean/BeanUtils.java
new file mode 100644
index 0000000..fb50e9c
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/bean/BeanUtils.java
@@ -0,0 +1,104 @@
+package com.yyy.common.utils.bean;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @Author: fy
+ * @Date: 2024/03/25
+ * @Description: Bean 工具类
+ */
+public class BeanUtils extends org.springframework.beans.BeanUtils {
+ /**
+ * Bean方法名中属性名开始的下标
+ */
+ private static final int BEAN_METHOD_PROP_INDEX = 3;
+
+ /**
+ * 匹配getter方法的正则表达式
+ */
+ private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)");
+
+ /**
+ * 匹配setter方法的正则表达式
+ */
+ private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)");
+
+ /**
+ * Bean属性复制工具方法。
+ *
+ * @param dest 目标对象
+ * @param src 源对象
+ */
+ public static void copyBeanProp(Object dest, Object src) {
+ try {
+ copyProperties(src, dest);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 获取对象的setter方法。
+ *
+ * @param obj 对象
+ * @return 对象的setter方法列表
+ */
+ public static List getSetterMethods(Object obj) {
+ // setter方法列表
+ List setterMethods = new ArrayList();
+
+ // 获取所有方法
+ Method[] methods = obj.getClass().getMethods();
+
+ // 查找setter方法
+
+ for (Method method : methods) {
+ Matcher m = SET_PATTERN.matcher(method.getName());
+ if (m.matches() && (method.getParameterTypes().length == 1)) {
+ setterMethods.add(method);
+ }
+ }
+ // 返回setter方法列表
+ return setterMethods;
+ }
+
+ /**
+ * 获取对象的getter方法。
+ *
+ * @param obj 对象
+ * @return 对象的getter方法列表
+ */
+
+ public static List getGetterMethods(Object obj) {
+ // getter方法列表
+ List getterMethods = new ArrayList();
+ // 获取所有方法
+ Method[] methods = obj.getClass().getMethods();
+ // 查找getter方法
+ for (Method method : methods) {
+ Matcher m = GET_PATTERN.matcher(method.getName());
+ if (m.matches() && (method.getParameterTypes().length == 0)) {
+ getterMethods.add(method);
+ }
+ }
+ // 返回getter方法列表
+ return getterMethods;
+ }
+
+ /**
+ * 检查Bean方法名中的属性名是否相等。
+ * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。
+ *
+ * @param m1 方法名1
+ * @param m2 方法名2
+ * @return 属性名一样返回true,否则返回false
+ */
+
+ public static boolean isMethodPropEquals(String m1, String m2) {
+ return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/yyy/common/utils/chart/ChartValue.java b/common/src/main/java/com/yyy/common/utils/chart/ChartValue.java
new file mode 100644
index 0000000..47ca6ef
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/chart/ChartValue.java
@@ -0,0 +1,19 @@
+package com.yyy.common.utils.chart;
+
+import io.swagger.annotations.ApiModel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+@ApiModel("柱状图、饼图类型数据")
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ChartValue implements Serializable {
+ private String name;
+ private Long value;
+}
diff --git a/common/src/main/java/com/yyy/common/utils/common/CMDUtils.java b/common/src/main/java/com/yyy/common/utils/common/CMDUtils.java
new file mode 100644
index 0000000..35bae82
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/common/CMDUtils.java
@@ -0,0 +1,28 @@
+package com.yyy.common.utils.common;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+
+@Slf4j
+public class CMDUtils {
+ /**
+ * 执行命令行
+ *
+ * @param cmd 命令行
+ * @return
+ */
+ public static boolean executeLinuxCmd(String cmd) {
+ try {
+ Process process = Runtime.getRuntime().exec(cmd);
+ process.waitFor();
+ } catch (InterruptedException e) {
+ log.error("executeLinuxCmd 执行Linux命令异常:", e);
+ Thread.currentThread().interrupt();
+ return false;
+ } catch (IOException e) {
+ log.error("获取系统命令执行环境异常", e);
+ }
+ return true;
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/utils/common/CacheUtils.java b/common/src/main/java/com/yyy/common/utils/common/CacheUtils.java
new file mode 100644
index 0000000..8e74330
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/common/CacheUtils.java
@@ -0,0 +1,358 @@
+package com.yyy.common.utils.common;
+
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.yyy.common.utils.spring.SpringUtils;
+import org.apache.shiro.cache.Cache;
+import org.apache.shiro.cache.CacheManager;
+import org.apache.shiro.cache.ehcache.EhCacheManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Cache缓存工具类
+ */
+public class CacheUtils {
+ private static Logger logger = LoggerFactory.getLogger(CacheUtils.class);
+
+ private static final String SYS_CACHE = "sys-cache";
+
+ /**
+ * 全局缓存(本地),可切换成redis
+ */
+ private static final Map> caches = new ConcurrentHashMap<>();
+
+ /**
+ * 获取SYS_CACHE缓存
+ *
+ * @param key
+ * @return
+ */
+ public static Object get(String key) {
+ return get(SYS_CACHE, key);
+ }
+
+ /**
+ * 获取SYS_CACHE缓存
+ *
+ * @param key
+ * @param defaultValue
+ * @return
+ */
+ public static Object get(String key, Object defaultValue) {
+ Object value = get(SYS_CACHE, key);
+ return value != null ? value : defaultValue;
+ }
+
+ /**
+ * 写入SYS_CACHE缓存
+ *
+ * @param key
+ * @param value
+ */
+ public static void put(String key, Object value) {
+ put(SYS_CACHE, key, value);
+ }
+
+ /**
+ * 从SYS_CACHE缓存中移除
+ *
+ * @param key
+ */
+ public static void remove(String key) {
+ remove(SYS_CACHE, key);
+ }
+
+ /**
+ * 获取缓存
+ *
+ * @param cacheName
+ * @param key
+ * @return
+ */
+ public static Object get(String cacheName, String key) {
+ return getCache(cacheName).get(getKey(key));
+ }
+
+ /**
+ * 获取缓存
+ *
+ * @param cacheName
+ * @param key
+ * @param defaultValue
+ * @return
+ */
+ public static Object get(String cacheName, String key, Object defaultValue) {
+ Object value = get(cacheName, key);
+ return value != null ? value : defaultValue;
+ }
+
+ /**
+ * 写入缓存
+ *
+ * @param cacheName
+ * @param key
+ * @param value
+ */
+ public static void put(String cacheName, String key, Object value) {
+ getCache(cacheName).put(getKey(key), value);
+ }
+
+ /**
+ * 从缓存中移除
+ *
+ * @param cacheName
+ * @param key
+ */
+ public static void remove(String cacheName, String key) {
+ getCache(cacheName).remove(getKey(key));
+ }
+
+ /**
+ * 从缓存中移除所有
+ *
+ * @param cacheName
+ */
+ public static void removeAll(String cacheName) {
+ Map cache = getCache(cacheName);
+ Set keys = cache.keySet();
+ for (Iterator it = keys.iterator(); it.hasNext(); ) {
+ cache.remove(it.next());
+ }
+ logger.info("清理缓存: {} => {}", cacheName, keys);
+ }
+
+ /**
+ * 从缓存中移除指定key
+ *
+ * @param keys
+ */
+ public static void removeByKeys(Set keys) {
+ removeByKeys(SYS_CACHE, keys);
+ }
+
+ /**
+ * 从缓存中移除指定key
+ *
+ * @param cacheName
+ * @param keys
+ */
+ public static void removeByKeys(String cacheName, Set keys) {
+ for (Iterator it = keys.iterator(); it.hasNext(); ) {
+ remove(cacheName, it.next());
+ }
+ logger.info("清理缓存: {} => {}", cacheName, keys);
+ }
+
+ /**
+ * 获取缓存键名
+ *
+ * @param key
+ * @return
+ */
+ private static String getKey(String key) {
+ return key;
+ }
+
+ /**
+ * 获得一个Cache,没有则创建一个新的Cache。
+ *
+ * @param cacheName
+ * @return
+ */
+ public static Map getCache(String cacheName) {
+ return caches.computeIfAbsent(cacheName, k -> new ConcurrentHashMap<>());
+ }
+
+ /**
+ * 获取所有缓存名称
+ *
+ * @return 缓存组
+ */
+ public static Set getCacheNames() {
+ return caches.keySet();
+ }
+
+// private static Logger logger = LoggerFactory.getLogger(CacheUtils.class);
+//
+// private static CacheManager cacheManager = SpringUtils.getBean(CacheManager.class);
+//
+// private static final String SYS_CACHE = "sys-cache";
+//
+// /**
+// * 获取SYS_CACHE缓存
+// *
+// * @param key
+// * @return
+// */
+// public static Object get(String key)
+// {
+// return get(SYS_CACHE, key);
+// }
+//
+// /**
+// * 获取SYS_CACHE缓存
+// *
+// * @param key
+// * @param defaultValue
+// * @return
+// */
+// public static Object get(String key, Object defaultValue)
+// {
+// Object value = get(key);
+// return value != null ? value : defaultValue;
+// }
+//
+// /**
+// * 写入SYS_CACHE缓存
+// *
+// * @param key
+// * @return
+// */
+// public static void put(String key, Object value)
+// {
+// put(SYS_CACHE, key, value);
+// }
+//
+// /**
+// * 从SYS_CACHE缓存中移除
+// *
+// * @param key
+// * @return
+// */
+// public static void remove(String key)
+// {
+// remove(SYS_CACHE, key);
+// }
+//
+// /**
+// * 获取缓存
+// *
+// * @param cacheName
+// * @param key
+// * @return
+// */
+// public static Object get(String cacheName, String key)
+// {
+// return getCache(cacheName).get(getKey(key));
+// }
+//
+// /**
+// * 获取缓存
+// *
+// * @param cacheName
+// * @param key
+// * @param defaultValue
+// * @return
+// */
+// public static Object get(String cacheName, String key, Object defaultValue)
+// {
+// Object value = get(cacheName, getKey(key));
+// return value != null ? value : defaultValue;
+// }
+//
+// /**
+// * 写入缓存
+// *
+// * @param cacheName
+// * @param key
+// * @param value
+// */
+// public static void put(String cacheName, String key, Object value)
+// {
+// getCache(cacheName).put(getKey(key), value);
+// }
+//
+// /**
+// * 从缓存中移除
+// *
+// * @param cacheName
+// * @param key
+// */
+// public static void remove(String cacheName, String key)
+// {
+// getCache(cacheName).remove(getKey(key));
+// }
+//
+// /**
+// * 从缓存中移除所有
+// *
+// * @param cacheName
+// */
+// public static void removeAll(String cacheName)
+// {
+// Cache cache = getCache(cacheName);
+// Set keys = cache.keys();
+// for (Iterator it = keys.iterator(); it.hasNext();)
+// {
+// cache.remove(it.next());
+// }
+// logger.info("清理缓存: {} => {}", cacheName, keys);
+// }
+//
+// /**
+// * 从缓存中移除指定key
+// *
+// * @param keys
+// */
+// public static void removeByKeys(Set keys)
+// {
+// removeByKeys(SYS_CACHE, keys);
+// }
+//
+// /**
+// * 从缓存中移除指定key
+// *
+// * @param cacheName
+// * @param keys
+// */
+// public static void removeByKeys(String cacheName, Set keys)
+// {
+// for (Iterator it = keys.iterator(); it.hasNext();)
+// {
+// remove(it.next());
+// }
+// logger.info("清理缓存: {} => {}", cacheName, keys);
+// }
+//
+// /**
+// * 获取缓存键名
+// *
+// * @param key
+// * @return
+// */
+// private static String getKey(String key)
+// {
+// return key;
+// }
+//
+// /**
+// * 获得一个Cache,没有则显示日志。
+// *
+// * @param cacheName
+// * @return
+// */
+// public static Cache getCache(String cacheName)
+// {
+// Cache cache = cacheManager.getCache(cacheName);
+// if (cache == null)
+// {
+// throw new RuntimeException("当前系统中没有定义“" + cacheName + "”这个缓存。");
+// }
+// return cache;
+// }
+//
+// /**
+// * 获取所有缓存
+// *
+// * @return 缓存组
+// */
+// public static String[] getCacheNames()
+// {
+// return ((EhCacheManager) cacheManager).getCacheManager().getCacheNames();
+// }
+}
diff --git a/common/src/main/java/com/yyy/common/utils/common/DateUtils.java b/common/src/main/java/com/yyy/common/utils/common/DateUtils.java
new file mode 100644
index 0000000..425ad6b
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/common/DateUtils.java
@@ -0,0 +1,250 @@
+package com.yyy.common.utils.common;
+
+import org.apache.commons.lang3.time.DateFormatUtils;
+
+import java.lang.management.ManagementFactory;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.*;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * @Author: fy
+ * @Date: 2024/03/22
+ * @Description:时间工具类
+ */
+public class DateUtils extends org.apache.commons.lang3.time.DateUtils
+{
+ public static String YYYY = "yyyy";
+
+ public static String YYYY_MM = "yyyy-MM";
+
+ public static String YYYY_MM_DD = "yyyy-MM-dd";
+
+ public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
+
+ public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
+
+ private static String[] parsePatterns = {
+ "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
+ "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
+ "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
+
+ /**
+ * 获取当前Date型日期
+ *
+ * @return Date() 当前日期
+ */
+ public static Date getNowDate()
+ {
+ return new Date();
+ }
+
+ /**
+ * 获取当前日期, 默认格式为yyyy-MM-dd
+ *
+ * @return String
+ */
+ public static String getDate()
+ {
+ return dateTimeNow(YYYY_MM_DD);
+ }
+
+ public static final String getTime()
+ {
+ return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
+ }
+
+ public static final String dateTimeNow()
+ {
+ return dateTimeNow(YYYYMMDDHHMMSS);
+ }
+
+ public static final String dateTimeNow(final String format)
+ {
+ return parseDateToStr(format, new Date());
+ }
+
+ public static final String dateTime(final Date date)
+ {
+ return parseDateToStr(YYYY_MM_DD, date);
+ }
+
+ public static final String parseDateToStr(final String format, final Date date)
+ {
+ return new SimpleDateFormat(format).format(date);
+ }
+
+ public static final Date dateTime(final String format, final String ts)
+ {
+ try
+ {
+ return new SimpleDateFormat(format).parse(ts);
+ }
+ catch (ParseException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * 日期路径 即年/月/日 如2018/08/08
+ */
+ public static final String datePath()
+ {
+ Date now = new Date();
+ return DateFormatUtils.format(now, "yyyy/MM/dd");
+ }
+
+ /**
+ * 日期路径 即年/月/日 如20180808
+ */
+ public static final String dateTime()
+ {
+ Date now = new Date();
+ return DateFormatUtils.format(now, "yyyyMMdd");
+ }
+
+ /**
+ * 日期型字符串转化为日期 格式
+ */
+ public static Date parseDate(Object str)
+ {
+ if (str == null)
+ {
+ return null;
+ }
+ try
+ {
+ return parseDate(str.toString(), parsePatterns);
+ }
+ catch (ParseException e)
+ {
+ return null;
+ }
+ }
+
+ public static Date parseDate(String dateString, String format)
+ {
+ SimpleDateFormat dateFormat = new SimpleDateFormat(format);
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ try {
+ return dateFormat.parse(dateString);
+ } catch (ParseException e) {
+ return null;
+ }
+ }
+
+ /**
+ * 日期型字符串转化为日期 格式
+ */
+ public static Date parseDate1(Date date)
+ {
+
+ SimpleDateFormat sdf = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS);
+ String formattedDateStr = sdf.format(date);
+ try {
+ return sdf.parse(formattedDateStr);
+ } catch (ParseException e) {
+ return null;
+ }
+ }
+
+ /**
+ * 获取服务器启动时间
+ */
+ public static Date getServerStartDate()
+ {
+ long time = ManagementFactory.getRuntimeMXBean().getStartTime();
+ return new Date(time);
+ }
+
+ /**
+ * 计算相差月数
+ */
+ public static int getMonthDiff(Date date1, Date date2)
+ {
+ Calendar c1 = Calendar.getInstance();
+ Calendar c2 = Calendar.getInstance();
+ c1.setTime(date1);
+ c2.setTime(date2);
+ int year1 = c1.get(Calendar.YEAR);
+ int year2 = c2.get(Calendar.YEAR);
+ int month1 = c1.get(Calendar.MONTH);
+ int month2 = c2.get(Calendar.MONTH);
+ // 获取年的差值
+ int yearInterval = year1 - year2;
+ // 获取月数差值
+ int monthInterval = month1 - month2;
+ int monthsDiff = yearInterval * 12 + monthInterval;
+ return monthsDiff;
+ }
+
+ /**
+ * 计算相差天数
+ */
+ public static int differentDaysByMillisecond(Date date1, Date date2)
+ {
+ return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
+ }
+
+ /**
+ * 计算相差小时
+ */
+ public static long getHourDiff(Date endDate, Date startDate)
+ {
+ long diffInMillis = endDate.getTime() - startDate.getTime();
+ double diffInHours = (double) diffInMillis / (1000 * 60 * 60);
+ //四舍五入
+ return Math.round(diffInHours);
+ }
+
+ /**
+ * 计算时间差
+ *
+ * @param endDate 最后时间
+ * @param startTime 开始时间
+ * @return 时间差(天/小时/分钟)
+ */
+ public static String timeDistance(Date endDate, Date startTime)
+ {
+ long nd = 1000 * 24 * 60 * 60;
+ long nh = 1000 * 60 * 60;
+ long nm = 1000 * 60;
+ // long ns = 1000;
+ // 获得两个时间的毫秒时间差异
+ long diff = endDate.getTime() - startTime.getTime();
+ // 计算差多少天
+ long day = diff / nd;
+ // 计算差多少小时
+ long hour = diff % nd / nh;
+ // 计算差多少分钟
+ long min = diff % nd % nh / nm;
+ // 计算差多少秒//输出结果
+ // long sec = diff % nd % nh % nm / ns;
+ return day + "天" + hour + "小时" + min + "分钟";
+ }
+
+ /**
+ * 增加 LocalDateTime ==> Date
+ */
+ public static Date toDate(LocalDateTime temporalAccessor)
+ {
+ ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
+ return Date.from(zdt.toInstant());
+ }
+
+ /**
+ * 增加 LocalDate ==> Date
+ */
+ public static Date toDate(LocalDate temporalAccessor)
+ {
+ LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
+ ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
+ return Date.from(zdt.toInstant());
+ }
+
+}
diff --git a/common/src/main/java/com/yyy/common/utils/common/DictUtils.java b/common/src/main/java/com/yyy/common/utils/common/DictUtils.java
new file mode 100644
index 0000000..7c0f4bd
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/common/DictUtils.java
@@ -0,0 +1,246 @@
+package com.yyy.common.utils.common;
+
+import com.yyy.common.constant.Constants;
+import com.yyy.common.core.domain.entity.SysDictData;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 字典工具类
+ *
+ */
+@Component
+public class DictUtils
+{
+ /**
+ * 分隔符
+ */
+ public static final String SEPARATOR = ",";
+
+ /**
+ * 设置字典缓存
+ *
+ * @param key 参数键
+ * @param dictDatas 字典数据列表
+ */
+ public static void setDictCache(String key, List dictDatas)
+ {
+ CacheUtils.put(getCacheName(), getCacheKey(key), dictDatas);
+ }
+
+ /**
+ * 获取字典缓存
+ *
+ * @param key 参数键
+ * @return dictDatas 字典数据列表
+ */
+ public static List getDictCache(String key)
+ {
+ Object cacheObj = CacheUtils.get(getCacheName(), getCacheKey(key));
+ if (StringUtils.isNotNull(cacheObj))
+ {
+ return StringUtils.cast(cacheObj);
+ }
+ return null;
+ }
+
+ /**
+ * 根据字典类型和字典值获取字典标签
+ *
+ * @param dictType 字典类型
+ * @param dictValue 字典值
+ * @return 字典标签
+ */
+ public static String getDictLabel(String dictType, String dictValue)
+ {
+ if (StringUtils.isEmpty(dictValue))
+ {
+ return StringUtils.EMPTY;
+ }
+ return getDictLabel(dictType, dictValue, SEPARATOR);
+ }
+
+ /**
+ * 根据字典类型和字典标签获取字典值
+ *
+ * @param dictType 字典类型
+ * @param dictLabel 字典标签
+ * @return 字典值
+ */
+ public static String getDictValue(String dictType, String dictLabel)
+ {
+ if (StringUtils.isEmpty(dictLabel))
+ {
+ return StringUtils.EMPTY;
+ }
+ return getDictValue(dictType, dictLabel, SEPARATOR);
+ }
+
+ /**
+ * 根据字典类型和字典值获取字典标签
+ *
+ * @param dictType 字典类型
+ * @param dictValue 字典值
+ * @param separator 分隔符
+ * @return 字典标签
+ */
+ public static String getDictLabel(String dictType, String dictValue, String separator)
+ {
+ StringBuilder propertyString = new StringBuilder();
+ List datas = getDictCache(dictType);
+ if (StringUtils.isNull(datas))
+ {
+ return StringUtils.EMPTY;
+ }
+ if (StringUtils.containsAny(dictValue, separator))
+ {
+ for (SysDictData dict : datas)
+ {
+ for (String value : dictValue.split(separator))
+ {
+ if (value.equals(dict.getDictValue()))
+ {
+ propertyString.append(dict.getDictLabel()).append(separator);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (SysDictData dict : datas)
+ {
+ if (dictValue.equals(dict.getDictValue()))
+ {
+ return dict.getDictLabel();
+ }
+ }
+ }
+ return StringUtils.stripEnd(propertyString.toString(), separator);
+ }
+
+ /**
+ * 根据字典类型和字典标签获取字典值
+ *
+ * @param dictType 字典类型
+ * @param dictLabel 字典标签
+ * @param separator 分隔符
+ * @return 字典值
+ */
+ public static String getDictValue(String dictType, String dictLabel, String separator)
+ {
+ StringBuilder propertyString = new StringBuilder();
+ List datas = getDictCache(dictType);
+ if (StringUtils.isNull(datas))
+ {
+ return StringUtils.EMPTY;
+ }
+ if (StringUtils.containsAny(dictLabel, separator))
+ {
+ for (SysDictData dict : datas)
+ {
+ for (String label : dictLabel.split(separator))
+ {
+ if (label.equals(dict.getDictLabel()))
+ {
+ propertyString.append(dict.getDictValue()).append(separator);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (SysDictData dict : datas)
+ {
+ if (dictLabel.equals(dict.getDictLabel()))
+ {
+ return dict.getDictValue();
+ }
+ }
+ }
+ return StringUtils.stripEnd(propertyString.toString(), separator);
+ }
+
+ /**
+ * 根据字典类型获取字典所有值
+ *
+ * @param dictType 字典类型
+ * @return 字典值
+ */
+ public static String getDictValues(String dictType)
+ {
+ StringBuilder propertyString = new StringBuilder();
+ List datas = getDictCache(dictType);
+ if (StringUtils.isNull(datas))
+ {
+ return StringUtils.EMPTY;
+ }
+ for (SysDictData dict : datas)
+ {
+ propertyString.append(dict.getDictValue()).append(SEPARATOR);
+ }
+ return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
+ }
+
+ /**
+ * 根据字典类型获取字典所有标签
+ *
+ * @param dictType 字典类型
+ * @return 字典值
+ */
+ public static String getDictLabels(String dictType)
+ {
+ StringBuilder propertyString = new StringBuilder();
+ List datas = getDictCache(dictType);
+ if (StringUtils.isNull(datas))
+ {
+ return StringUtils.EMPTY;
+ }
+ for (SysDictData dict : datas)
+ {
+ propertyString.append(dict.getDictLabel()).append(SEPARATOR);
+ }
+ return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
+ }
+
+ /**
+ * 删除指定字典缓存
+ *
+ * @param key 字典键
+ */
+ public static void removeDictCache(String key)
+ {
+ CacheUtils.remove(getCacheName(), getCacheKey(key));
+ }
+
+ /**
+ * 清空字典缓存
+ */
+ public static void clearDictCache()
+ {
+ CacheUtils.removeAll(getCacheName());
+ }
+
+ /**
+ * 获取cache name
+ *
+ * @return 缓存名
+ */
+ public static String getCacheName()
+ {
+ return Constants.SYS_DICT_CACHE;
+ }
+
+ /**
+ * 设置cache key
+ *
+ * @param configKey 参数键
+ * @return 缓存键key
+ */
+ public static String getCacheKey(String configKey)
+ {
+ return Constants.SYS_DICT_KEY + configKey;
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/utils/common/ExceptionUtil.java b/common/src/main/java/com/yyy/common/utils/common/ExceptionUtil.java
new file mode 100644
index 0000000..9255118
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/common/ExceptionUtil.java
@@ -0,0 +1,39 @@
+package com.yyy.common.utils.common;
+
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+
+/**
+ * 错误信息处理类。
+ *
+ */
+public class ExceptionUtil
+{
+ /**
+ * 获取exception的详细错误信息。
+ */
+ public static String getExceptionMessage(Throwable e)
+ {
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw, true));
+ return sw.toString();
+ }
+
+ public static String getRootErrorMessage(Exception e)
+ {
+ Throwable root = ExceptionUtils.getRootCause(e);
+ root = (root == null ? e : root);
+ if (root == null)
+ {
+ return "";
+ }
+ String msg = root.getMessage();
+ if (msg == null)
+ {
+ return "null";
+ }
+ return StringUtils.defaultString(msg);
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/yyy/common/utils/common/MD5Utils.java b/common/src/main/java/com/yyy/common/utils/common/MD5Utils.java
new file mode 100644
index 0000000..02a3a42
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/common/MD5Utils.java
@@ -0,0 +1,48 @@
+package com.yyy.common.utils.common;
+
+import org.springframework.stereotype.Component;
+import org.springframework.util.DigestUtils;
+
+import java.security.SecureRandom;
+import java.util.Base64;
+
+/**
+ * Created with IntelliJ IDEA.
+ *
+ * @Author: fy
+ * @Date: 2023/04/19/10:09
+ * @Description:MD5密码加密
+ */
+public class MD5Utils {
+ public static String md5(String src) {
+ return DigestUtils.md5DigestAsHex(src.getBytes());
+ }
+
+ //第一次加密
+ public static String inputPassToFormPass(String inputPass, String salt) {
+ //md5加密密码前,先对密码进行处理,按以下salt的规则处理密码
+ String str = "" + salt.charAt(0) + salt.charAt(2) + inputPass + salt.charAt(5) + salt.charAt(4);
+ return md5(str);
+ }
+
+ //第二次加密
+ public static String formPassToDBPass(String formPass, String salt) {
+ String str = "" + salt.charAt(0) + salt.charAt(2) + formPass + salt.charAt(5) + salt.charAt(4);
+ return md5(str);
+ }
+
+ //实际调用的方法,将第一次加密和第二次加密合并,结果应该一致
+ public static String inputPassToDBPass(String inputPass, String salt) {
+ String formPass = inputPassToFormPass(inputPass, salt);
+ String dbPass = formPassToDBPass(formPass, salt);
+ return dbPass;
+ }
+
+ public static String randomSalt() {
+ int length = 16;
+ SecureRandom random = new SecureRandom();
+ byte[] salt = new byte[length];
+ random.nextBytes(salt);
+ return Base64.getEncoder().encodeToString(salt);
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/utils/common/OSUtils.java b/common/src/main/java/com/yyy/common/utils/common/OSUtils.java
new file mode 100644
index 0000000..e644f53
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/common/OSUtils.java
@@ -0,0 +1,7 @@
+package com.yyy.common.utils.common;
+
+public class OSUtils {
+ public static String getOsName() {
+ return System.getProperty("os.name").toLowerCase();
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/utils/common/PageUtils.java b/common/src/main/java/com/yyy/common/utils/common/PageUtils.java
new file mode 100644
index 0000000..5114294
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/common/PageUtils.java
@@ -0,0 +1,29 @@
+package com.yyy.common.utils.common;
+
+import com.github.pagehelper.PageHelper;
+import com.yyy.common.core.page.PageDomain;
+import com.yyy.common.core.page.TableSupport;
+import com.yyy.common.utils.sql.SqlUtil;
+
+public class PageUtils extends PageHelper {
+ /**
+ * 设置请求分页数据
+ */
+ public static void startPage()
+ {
+ PageDomain pageDomain = TableSupport.buildPageRequest();
+ Integer pageNum = pageDomain.getPageNum();
+ Integer pageSize = pageDomain.getPageSize();
+ String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
+ Boolean reasonable = pageDomain.getReasonable();
+ PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
+ }
+
+ /**
+ * 清理分页的线程变量
+ */
+ public static void clearPage()
+ {
+ PageHelper.clearPage();
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/utils/common/ServletUtils.java b/common/src/main/java/com/yyy/common/utils/common/ServletUtils.java
new file mode 100644
index 0000000..214c133
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/common/ServletUtils.java
@@ -0,0 +1,230 @@
+package com.yyy.common.utils.common;
+
+import com.yyy.common.constant.Constants;
+import com.yyy.common.core.text.Convert;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+
+/**
+ * @Author: fy
+ * @Date: 2024/03/22
+ * @Description:客户端工具类
+ */
+public class ServletUtils
+{
+ /**
+ * 定义移动端请求的所有可能类型
+ */
+ private final static String[] agent = { "Android", "iPhone", "iPod", "iPad", "Windows Phone", "MQQBrowser" };
+
+ /**
+ * 获取String参数
+ */
+ public static String getParameter(String name)
+ {
+ return getRequest().getParameter(name);
+ }
+
+ /**
+ * 获取String参数
+ */
+ public static String getParameter(String name, String defaultValue)
+ {
+ return Convert.toStr(getRequest().getParameter(name), defaultValue);
+ }
+
+ /**
+ * 获取Integer参数
+ */
+ public static Integer getParameterToInt(String name)
+ {
+ return Convert.toInt(getRequest().getParameter(name));
+ }
+
+ /**
+ * 获取Integer参数
+ */
+ public static Integer getParameterToInt(String name, Integer defaultValue)
+ {
+ return Convert.toInt(getRequest().getParameter(name), defaultValue);
+ }
+
+ /**
+ * 获取Boolean参数
+ */
+ public static Boolean getParameterToBool(String name)
+ {
+ return Convert.toBool(getRequest().getParameter(name));
+ }
+
+ /**
+ * 获取Boolean参数
+ */
+ public static Boolean getParameterToBool(String name, Boolean defaultValue)
+ {
+ return Convert.toBool(getRequest().getParameter(name), defaultValue);
+ }
+
+ /**
+ * 获取request
+ */
+ public static HttpServletRequest getRequest()
+ {
+ return getRequestAttributes().getRequest();
+ }
+
+ /**
+ * 获取response
+ */
+ public static HttpServletResponse getResponse()
+ {
+ return getRequestAttributes().getResponse();
+ }
+
+ /**
+ * 获取session
+ */
+ public static HttpSession getSession()
+ {
+ return getRequest().getSession();
+ }
+
+ public static ServletRequestAttributes getRequestAttributes()
+ {
+ RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
+ return (ServletRequestAttributes) attributes;
+ }
+
+ /**
+ * 将字符串渲染到客户端
+ *
+ * @param response 渲染对象
+ * @param string 待渲染的字符串
+ * @return null
+ */
+ public static String renderString(HttpServletResponse response, String string)
+ {
+ try
+ {
+ response.setContentType("application/json");
+ response.setCharacterEncoding("utf-8");
+ response.getWriter().print(string);
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 是否是Ajax异步请求
+ *
+ * @param request
+ */
+ public static boolean isAjaxRequest(HttpServletRequest request)
+ {
+ String accept = request.getHeader("accept");
+ if (accept != null && accept.contains("application/json"))
+ {
+ return true;
+ }
+
+ String xRequestedWith = request.getHeader("X-Requested-With");
+ if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest"))
+ {
+ return true;
+ }
+
+ String uri = request.getRequestURI();
+ if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml"))
+ {
+ return true;
+ }
+
+ String ajax = request.getParameter("__ajax");
+ return StringUtils.inStringIgnoreCase(ajax, "json", "xml");
+ }
+
+ /**
+ * 判断User-Agent 是不是来自于手机
+ */
+ public static boolean checkAgentIsMobile(String ua)
+ {
+ boolean flag = false;
+ if (!ua.contains("Windows NT") || (ua.contains("Windows NT") && ua.contains("compatible; MSIE 9.0;")))
+ {
+ // 排除 苹果桌面系统
+ if (!ua.contains("Windows NT") && !ua.contains("Macintosh"))
+ {
+ for (String item : agent)
+ {
+ if (ua.contains(item))
+ {
+ flag = true;
+ break;
+ }
+ }
+ }
+ }
+ return flag;
+ }
+
+ /**
+ * 内容编码
+ *
+ * @param str 内容
+ * @return 编码后的内容
+ */
+ public static String urlEncode(String str)
+ {
+ try
+ {
+ return URLEncoder.encode(str, Constants.UTF8);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ return StringUtils.EMPTY;
+ }
+ }
+
+ /**
+ * 内容解码
+ *
+ * @param str 内容
+ * @return 解码后的内容
+ */
+ public static String urlDecode(String str)
+ {
+ try
+ {
+ return URLDecoder.decode(str, Constants.UTF8);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ return StringUtils.EMPTY;
+ }
+ }
+
+ /**
+ * 判断当前是否通过 HTTP 请求调用
+ *
+ * @return 如果是 HTTP 请求则返回 true,否则返回 false
+ */
+ public static boolean isHttpRequest() {
+ RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+ if (requestAttributes == null || ! (requestAttributes instanceof ServletRequestAttributes)){
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/utils/common/StringUtils.java b/common/src/main/java/com/yyy/common/utils/common/StringUtils.java
new file mode 100644
index 0000000..230bd4d
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/common/StringUtils.java
@@ -0,0 +1,636 @@
+package com.yyy.common.utils.common;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.yyy.common.constant.Constants;
+import com.yyy.common.core.text.StrFormatter;
+import org.springframework.util.AntPathMatcher;
+/**
+ * @Author: fy
+ * @Date: 2024/03/22
+ * @Description:
+ */
+
+/**
+ * 字符串工具类
+ *
+ * @author ruoyi
+ */
+public class StringUtils extends org.apache.commons.lang3.StringUtils
+{
+ /** 空字符串 */
+ private static final String NULLSTR = "";
+
+ /** 下划线 */
+ private static final char SEPARATOR = '_';
+
+ /**
+ * 获取参数不为空值
+ *
+ * @param value defaultValue 要判断的value
+ * @return value 返回值
+ */
+ public static T nvl(T value, T defaultValue)
+ {
+ return value != null ? value : defaultValue;
+ }
+
+ /**
+ * * 判断一个Collection是否为空, 包含List,Set,Queue
+ *
+ * @param coll 要判断的Collection
+ * @return true:为空 false:非空
+ */
+ public static boolean isEmpty(Collection> coll)
+ {
+ return isNull(coll) || coll.isEmpty();
+ }
+
+ /**
+ * * 判断一个Collection是否非空,包含List,Set,Queue
+ *
+ * @param coll 要判断的Collection
+ * @return true:非空 false:空
+ */
+ public static boolean isNotEmpty(Collection> coll)
+ {
+ return !isEmpty(coll);
+ }
+
+ /**
+ * * 判断一个对象数组是否为空
+ *
+ * @param objects 要判断的对象数组
+ ** @return true:为空 false:非空
+ */
+ public static boolean isEmpty(Object[] objects)
+ {
+ return isNull(objects) || (objects.length == 0);
+ }
+
+ /**
+ * * 判断一个对象数组是否非空
+ *
+ * @param objects 要判断的对象数组
+ * @return true:非空 false:空
+ */
+ public static boolean isNotEmpty(Object[] objects)
+ {
+ return !isEmpty(objects);
+ }
+
+ /**
+ * * 判断一个Map是否为空
+ *
+ * @param map 要判断的Map
+ * @return true:为空 false:非空
+ */
+ public static boolean isEmpty(Map, ?> map)
+ {
+ return isNull(map) || map.isEmpty();
+ }
+
+ /**
+ * * 判断一个Map是否为空
+ *
+ * @param map 要判断的Map
+ * @return true:非空 false:空
+ */
+ public static boolean isNotEmpty(Map, ?> map)
+ {
+ return !isEmpty(map);
+ }
+
+ /**
+ * * 判断一个字符串是否为空串
+ *
+ * @param str String
+ * @return true:为空 false:非空
+ */
+ public static boolean isEmpty(String str)
+ {
+ return isNull(str) || NULLSTR.equals(str.trim());
+ }
+
+ /**
+ * * 判断一个字符串是否为非空串
+ *
+ * @param str String
+ * @return true:非空串 false:空串
+ */
+ public static boolean isNotEmpty(String str)
+ {
+ return !isEmpty(str);
+ }
+
+ /**
+ * * 判断一个对象是否为空
+ *
+ * @param object Object
+ * @return true:为空 false:非空
+ */
+ public static boolean isNull(Object object)
+ {
+ return object == null;
+ }
+
+ /**
+ * * 判断一个对象是否非空
+ *
+ * @param object Object
+ * @return true:非空 false:空
+ */
+ public static boolean isNotNull(Object object)
+ {
+ return !isNull(object);
+ }
+
+ /**
+ * * 判断一个对象是否是数组类型(Java基本型别的数组)
+ *
+ * @param object 对象
+ * @return true:是数组 false:不是数组
+ */
+ public static boolean isArray(Object object)
+ {
+ return isNotNull(object) && object.getClass().isArray();
+ }
+
+ /**
+ * 去空格
+ */
+ public static String trim(String str)
+ {
+ return (str == null ? "" : str.trim());
+ }
+
+ /**
+ * 截取字符串
+ *
+ * @param str 字符串
+ * @param start 开始
+ * @return 结果
+ */
+ public static String substring(final String str, int start)
+ {
+ if (str == null)
+ {
+ return NULLSTR;
+ }
+
+ if (start < 0)
+ {
+ start = str.length() + start;
+ }
+
+ if (start < 0)
+ {
+ start = 0;
+ }
+ if (start > str.length())
+ {
+ return NULLSTR;
+ }
+
+ return str.substring(start);
+ }
+
+ /**
+ * 截取字符串
+ *
+ * @param str 字符串
+ * @param start 开始
+ * @param end 结束
+ * @return 结果
+ */
+ public static String substring(final String str, int start, int end)
+ {
+ if (str == null)
+ {
+ return NULLSTR;
+ }
+
+ if (end < 0)
+ {
+ end = str.length() + end;
+ }
+ if (start < 0)
+ {
+ start = str.length() + start;
+ }
+
+ if (end > str.length())
+ {
+ end = str.length();
+ }
+
+ if (start > end)
+ {
+ return NULLSTR;
+ }
+
+ if (start < 0)
+ {
+ start = 0;
+ }
+ if (end < 0)
+ {
+ end = 0;
+ }
+
+ return str.substring(start, end);
+ }
+
+ /**
+ * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ *
+ * @param template 文本模板,被替换的部分用 {} 表示
+ * @param params 参数值
+ * @return 格式化后的文本
+ */
+ public static String format(String template, Object... params)
+ {
+ if (isEmpty(params) || isEmpty(template))
+ {
+ return template;
+ }
+ return StrFormatter.format(template, params);
+ }
+
+ /**
+ * 是否为http(s)://开头
+ *
+ * @param link 链接
+ * @return 结果
+ */
+ public static boolean ishttp(String link)
+ {
+ return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
+ }
+
+ /**
+ * 字符串转set
+ *
+ * @param str 字符串
+ * @param sep 分隔符
+ * @return set集合
+ */
+ public static final Set str2Set(String str, String sep)
+ {
+ return new HashSet(str2List(str, sep, true, false));
+ }
+
+ /**
+ * 字符串转list
+ *
+ * @param str 字符串
+ * @param sep 分隔符
+ * @param filterBlank 过滤纯空白
+ * @param trim 去掉首尾空白
+ * @return list集合
+ */
+ public static final List str2List(String str, String sep, boolean filterBlank, boolean trim)
+ {
+ List list = new ArrayList();
+ if (StringUtils.isEmpty(str))
+ {
+ return list;
+ }
+
+ // 过滤空白字符串
+ if (filterBlank && StringUtils.isBlank(str))
+ {
+ return list;
+ }
+ String[] split = str.split(sep);
+ for (String string : split)
+ {
+ if (filterBlank && StringUtils.isBlank(string))
+ {
+ continue;
+ }
+ if (trim)
+ {
+ string = string.trim();
+ }
+ list.add(string);
+ }
+
+ return list;
+ }
+
+ /**
+ * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
+ *
+ * @param collection 给定的集合
+ * @param array 给定的数组
+ * @return boolean 结果
+ */
+ public static boolean containsAny(Collection collection, String... array)
+ {
+ if (isEmpty(collection) || isEmpty(array))
+ {
+ return false;
+ }
+ else
+ {
+ for (String str : array)
+ {
+ if (collection.contains(str))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
+ *
+ * @param cs 指定字符串
+ * @param searchCharSequences 需要检查的字符串数组
+ * @return 是否包含任意一个字符串
+ */
+ public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences)
+ {
+ if (isEmpty(cs) || isEmpty(searchCharSequences))
+ {
+ return false;
+ }
+ for (CharSequence testStr : searchCharSequences)
+ {
+ if (containsIgnoreCase(cs, testStr))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 驼峰转下划线命名
+ */
+ public static String toUnderScoreCase(String str)
+ {
+ if (str == null)
+ {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ // 前置字符是否大写
+ boolean preCharIsUpperCase = true;
+ // 当前字符是否大写
+ boolean curreCharIsUpperCase = true;
+ // 下一字符是否大写
+ boolean nexteCharIsUpperCase = true;
+ for (int i = 0; i < str.length(); i++)
+ {
+ char c = str.charAt(i);
+ if (i > 0)
+ {
+ preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
+ }
+ else
+ {
+ preCharIsUpperCase = false;
+ }
+
+ curreCharIsUpperCase = Character.isUpperCase(c);
+
+ if (i < (str.length() - 1))
+ {
+ nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
+ }
+
+ if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
+ {
+ sb.append(SEPARATOR);
+ }
+ else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
+ {
+ sb.append(SEPARATOR);
+ }
+ sb.append(Character.toLowerCase(c));
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * 是否包含字符串
+ *
+ * @param str 验证字符串
+ * @param strs 字符串组
+ * @return 包含返回true
+ */
+ public static boolean inStringIgnoreCase(String str, String... strs)
+ {
+ if (str != null && strs != null)
+ {
+ for (String s : strs)
+ {
+ if (str.equalsIgnoreCase(trim(s)))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 删除最后一个字符串
+ *
+ * @param str 输入字符串
+ * @param spit 以什么类型结尾的
+ * @return 截取后的字符串
+ */
+ public static String lastStringDel(String str, String spit)
+ {
+ if (!StringUtils.isEmpty(str) && str.endsWith(spit))
+ {
+ return str.subSequence(0, str.length() - 1).toString();
+ }
+ return str;
+ }
+
+ /**
+ * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
+ *
+ * @param name 转换前的下划线大写方式命名的字符串
+ * @return 转换后的驼峰式命名的字符串
+ */
+ public static String convertToCamelCase(String name)
+ {
+ StringBuilder result = new StringBuilder();
+ // 快速检查
+ if (name == null || name.isEmpty())
+ {
+ // 没必要转换
+ return "";
+ }
+ else if (!name.contains("_"))
+ {
+ // 不含下划线,仅将首字母大写
+ return name.substring(0, 1).toUpperCase() + name.substring(1);
+ }
+ // 用下划线将原始字符串分割
+ String[] camels = name.split("_");
+ for (String camel : camels)
+ {
+ // 跳过原始字符串中开头、结尾的下换线或双重下划线
+ if (camel.isEmpty())
+ {
+ continue;
+ }
+ // 首字母大写
+ result.append(camel.substring(0, 1).toUpperCase());
+ result.append(camel.substring(1).toLowerCase());
+ }
+ return result.toString();
+ }
+
+ /**
+ * 驼峰式命名法
+ * 例如:user_name->userName
+ */
+ public static String toCamelCase(String s)
+ {
+ if (s == null)
+ {
+ return null;
+ }
+ if (s.indexOf(SEPARATOR) == -1)
+ {
+ return s;
+ }
+ s = s.toLowerCase();
+ StringBuilder sb = new StringBuilder(s.length());
+ boolean upperCase = false;
+ for (int i = 0; i < s.length(); i++)
+ {
+ char c = s.charAt(i);
+
+ if (c == SEPARATOR)
+ {
+ upperCase = true;
+ }
+ else if (upperCase)
+ {
+ sb.append(Character.toUpperCase(c));
+ upperCase = false;
+ }
+ else
+ {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
+ *
+ * @param str 指定字符串
+ * @param strs 需要检查的字符串数组
+ * @return 是否匹配
+ */
+ public static boolean matches(String str, List strs)
+ {
+ if (isEmpty(str) || isEmpty(strs))
+ {
+ return false;
+ }
+ for (String pattern : strs)
+ {
+ if (isMatch(pattern, str))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 判断url是否与规则配置:
+ * ? 表示单个字符;
+ * * 表示一层路径内的任意字符串,不可跨层级;
+ * ** 表示任意层路径;
+ *
+ * @param pattern 匹配规则
+ * @param url 需要匹配的url
+ * @return
+ */
+ public static boolean isMatch(String pattern, String url)
+ {
+ AntPathMatcher matcher = new AntPathMatcher();
+ return matcher.match(pattern, url);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T cast(Object obj)
+ {
+ return (T) obj;
+ }
+
+ /**
+ * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
+ *
+ * @param num 数字对象
+ * @param size 字符串指定长度
+ * @return 返回数字的字符串格式,该字符串为指定长度。
+ */
+ public static final String padl(final Number num, final int size)
+ {
+ return padl(num.toString(), size, '0');
+ }
+
+ /**
+ * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
+ *
+ * @param s 原始字符串
+ * @param size 字符串指定长度
+ * @param c 用于补齐的字符
+ * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
+ */
+ public static final String padl(final String s, final int size, final char c)
+ {
+ final StringBuilder sb = new StringBuilder(size);
+ if (s != null)
+ {
+ final int len = s.length();
+ if (s.length() <= size)
+ {
+ for (int i = size - len; i > 0; i--)
+ {
+ sb.append(c);
+ }
+ sb.append(s);
+ }
+ else
+ {
+ return s.substring(len - size, len);
+ }
+ }
+ else
+ {
+ for (int i = size; i > 0; i--)
+ {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/utils/common/Threads.java b/common/src/main/java/com/yyy/common/utils/common/Threads.java
new file mode 100644
index 0000000..fbb7014
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/common/Threads.java
@@ -0,0 +1,94 @@
+package com.yyy.common.utils.common;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.*;
+
+/**
+ * 线程相关工具类.
+ */
+public class Threads
+{
+ private static final Logger logger = LoggerFactory.getLogger(Threads.class);
+
+ /**
+ * sleep等待,单位为毫秒
+ */
+ public static void sleep(long milliseconds)
+ {
+ try
+ {
+ Thread.sleep(milliseconds);
+ }
+ catch (InterruptedException e)
+ {
+ return;
+ }
+ }
+
+ /**
+ * 停止线程池
+ * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
+ * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.
+ * 如果仍人超時,則強制退出.
+ * 另对在shutdown时线程本身被调用中断做了处理.
+ */
+ public static void shutdownAndAwaitTermination(ExecutorService pool)
+ {
+ if (pool != null && !pool.isShutdown())
+ {
+ pool.shutdown();
+ try
+ {
+ if (!pool.awaitTermination(120, TimeUnit.SECONDS))
+ {
+ pool.shutdownNow();
+ if (!pool.awaitTermination(120, TimeUnit.SECONDS))
+ {
+ logger.info("Pool did not terminate");
+ }
+ }
+ }
+ catch (InterruptedException ie)
+ {
+ pool.shutdownNow();
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ /**
+ * 打印线程异常信息
+ */
+ public static void printException(Runnable r, Throwable t)
+ {
+ if (t == null && r instanceof Future>)
+ {
+ try
+ {
+ Future> future = (Future>) r;
+ if (future.isDone())
+ {
+ future.get();
+ }
+ }
+ catch (CancellationException ce)
+ {
+ t = ce;
+ }
+ catch (ExecutionException ee)
+ {
+ t = ee.getCause();
+ }
+ catch (InterruptedException ie)
+ {
+ Thread.currentThread().interrupt();
+ }
+ }
+ if (t != null)
+ {
+ logger.error(t.getMessage(), t);
+ }
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/utils/eam/DocumentsUtil.java b/common/src/main/java/com/yyy/common/utils/eam/DocumentsUtil.java
new file mode 100644
index 0000000..70cdf9c
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/eam/DocumentsUtil.java
@@ -0,0 +1,4 @@
+package com.yyy.common.utils.eam;
+
+public class DocumentsUtil {
+}
diff --git a/common/src/main/java/com/yyy/common/utils/eam/ResidualValueCalculator.java b/common/src/main/java/com/yyy/common/utils/eam/ResidualValueCalculator.java
new file mode 100644
index 0000000..9cd433d
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/eam/ResidualValueCalculator.java
@@ -0,0 +1,71 @@
+package com.yyy.common.utils.eam;
+
+import java.math.BigDecimal;
+
+public class ResidualValueCalculator {
+ // 年限平均法
+ public static BigDecimal calculateResidualValueByAverageLife(BigDecimal originalValue, int usefulLife, int yearsDepreciated) {
+ BigDecimal depreciationPerYear = originalValue.divide(BigDecimal.valueOf(usefulLife), 2, BigDecimal.ROUND_HALF_UP);
+ BigDecimal accumulatedDepreciation = depreciationPerYear.multiply(BigDecimal.valueOf(yearsDepreciated));
+ BigDecimal residualValue = originalValue.subtract(accumulatedDepreciation);
+ return residualValue;
+ }
+
+ // 按月折旧的年限平均法
+ public static BigDecimal calculateMonthlyDepreciationByAverageLife(BigDecimal originalValue, int usefulLifeInMonths, int monthsDepreciated) {
+ BigDecimal depreciationPerMonth = originalValue.divide(BigDecimal.valueOf(usefulLifeInMonths), 2, BigDecimal.ROUND_HALF_UP);
+ BigDecimal accumulatedDepreciation = depreciationPerMonth.multiply(BigDecimal.valueOf(monthsDepreciated));
+ BigDecimal residualValue = originalValue.subtract(accumulatedDepreciation);
+ return residualValue;
+ }
+
+ // 工作量法
+ public static BigDecimal calculateResidualValueByUnitsOfWork(BigDecimal originalValue, BigDecimal totalUnitsOfWork, BigDecimal unitsOfWorkDone) {
+ BigDecimal depreciationPerUnit = originalValue.divide(totalUnitsOfWork, 2, BigDecimal.ROUND_HALF_UP);
+ BigDecimal accumulatedDepreciation = depreciationPerUnit.multiply(unitsOfWorkDone);
+ BigDecimal residualValue = originalValue.subtract(accumulatedDepreciation);
+ return residualValue;
+ }
+
+ // 双倍余额递减法
+ public static BigDecimal calculateResidualValueByDoubleDecliningBalance(BigDecimal originalValue, BigDecimal depreciationRate, int usefulLife, int yearsDepreciated) {
+ BigDecimal bookValue = originalValue;
+ for (int i = 1; i <= yearsDepreciated; i++) {
+ BigDecimal depreciationExpense = bookValue.multiply(depreciationRate).setScale(2, BigDecimal.ROUND_HALF_UP);
+ bookValue = bookValue.subtract(depreciationExpense);
+ }
+ return bookValue;
+ }
+
+ // 年数总和法
+ public static BigDecimal calculateResidualValueBySumOfTheYearsDigits(BigDecimal originalValue, int usefulLife, int yearsDepreciated) {
+ int sumOfYears = usefulLife * (usefulLife + 1) / 2;
+ BigDecimal depreciationPerYear = originalValue.divide(BigDecimal.valueOf(sumOfYears), 2, BigDecimal.ROUND_HALF_UP);
+ BigDecimal accumulatedDepreciation = BigDecimal.ZERO;
+ for (int i = 1; i <= yearsDepreciated; i++) {
+ accumulatedDepreciation = accumulatedDepreciation.add(depreciationPerYear.multiply(BigDecimal.valueOf(usefulLife - i + 1)));
+ }
+ BigDecimal residualValue = originalValue.subtract(accumulatedDepreciation);
+ return residualValue;
+ }
+
+ public static void main(String[] args) {
+ BigDecimal originalValue = new BigDecimal("3000000.00"); // 资产原值
+ int usefulLife = 5; // 使用年限
+ int yearsDepreciated = 3; // 已折旧年限
+ BigDecimal totalUnitsOfWork = new BigDecimal("1000"); // 总工作量
+ BigDecimal unitsOfWorkDone = new BigDecimal("500"); // 完成的工作量
+ BigDecimal depreciationRate = new BigDecimal("0.2"); // 折旧率
+
+ BigDecimal residualValueAverageLife = calculateResidualValueByAverageLife(originalValue, usefulLife, yearsDepreciated);
+ BigDecimal residualValueUnitsOfWork = calculateResidualValueByUnitsOfWork(new BigDecimal("60.00"), new BigDecimal("50.00"), new BigDecimal("0.40"));
+ BigDecimal residualValueDoubleDecliningBalance = calculateResidualValueByDoubleDecliningBalance(originalValue, depreciationRate, usefulLife, yearsDepreciated);
+ BigDecimal residualValueSumOfTheYearsDigits = calculateResidualValueBySumOfTheYearsDigits(originalValue, usefulLife, yearsDepreciated);
+
+ System.out.println("资产残值 - 年限平均法: " + residualValueAverageLife);
+ System.out.println("年限平均法(按月): 资产残值 - " + calculateMonthlyDepreciationByAverageLife(originalValue,5*12,9));
+ System.out.println("资产残值 - 工作量法: " + residualValueUnitsOfWork);
+ System.out.println("资产残值 - 双倍余额递减法: " + residualValueDoubleDecliningBalance);
+ System.out.println("资产残值 - 年数总和法: " + residualValueSumOfTheYearsDigits);
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/utils/file/FileTypeUtils.java b/common/src/main/java/com/yyy/common/utils/file/FileTypeUtils.java
new file mode 100644
index 0000000..0800a63
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/file/FileTypeUtils.java
@@ -0,0 +1,135 @@
+package com.yyy.common.utils.file;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * @Author: fy
+ * @Date: 2024/03/22
+ * @Description:文件类型工具类
+ */
+@Slf4j
+public class FileTypeUtils
+{
+ /**
+ * 获取文件类型
+ *
+ * 例如: ruoyi.txt, 返回: txt
+ *
+ * @param file 文件名
+ * @return 后缀(不含".")
+ */
+ public static String getFileType(File file)
+ {
+ if (null == file)
+ {
+ return StringUtils.EMPTY;
+ }
+ return getFileType(file.getName());
+ }
+
+ /**
+ * 获取文件类型
+ *
+ * 例如: ruoyi.txt, 返回: txt
+ *
+ * @param fileName 文件名
+ * @return 后缀(不含".")
+ */
+ public static String getFileType(String fileName)
+ {
+ int separatorIndex = fileName.lastIndexOf(".");
+ if (separatorIndex < 0)
+ {
+ return "";
+ }
+ return fileName.substring(separatorIndex + 1).toLowerCase();
+ }
+
+ /**
+ * 获取文件类型
+ *
+ * @param photoByte 文件字节码
+ * @return 后缀(不含".")
+ */
+ public static String getFileExtendName(byte[] photoByte)
+ {
+ String strFileExtendName = "JPG";
+ if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
+ && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
+ {
+ strFileExtendName = "GIF";
+ }
+ else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
+ {
+ strFileExtendName = "JPG";
+ }
+ else if ((photoByte[0] == 66) && (photoByte[1] == 77))
+ {
+ strFileExtendName = "BMP";
+ }
+ else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
+ {
+ strFileExtendName = "PNG";
+ }
+ return strFileExtendName;
+ }
+
+ /**
+ * 获取导出表单文件类型
+ *
+ * @param tempFileFile 模板文件
+ * @return 后缀(不含"."),如 "docx", "doc", "xlsx", "xls"
+ */
+ public static String getFormType(File tempFileFile) {
+ String fileName = tempFileFile.getName().toLowerCase();
+ try (FileInputStream fis = new FileInputStream(tempFileFile)) {
+ if (fileName.endsWith(".docx") || fileName.endsWith(".xlsx")) {
+ byte[] header = new byte[4];
+ if (fis.read(header) != 4) {
+ log.error("无法读取文件头前4个字节");
+ return null;
+ }
+ // ZIP文件头的魔数:50 4B 03 04
+ if (header[0] == 0x50 && header[1] == 0x4B && header[2] == 0x03 && header[3] == 0x04) {
+ return fileName.endsWith(".docx") ? "docx" : "xlsx";
+ } else {
+ log.error("文件扩展名与文件头不匹配: {}", fileName);
+ return null;
+ }
+ } else if (fileName.endsWith(".doc") || fileName.endsWith(".xls")) {
+ byte[] header = new byte[8];
+ if (fis.read(header) != 8) {
+ log.error("无法读取文件头前8个字节");
+ return null;
+ }
+ // OLE Compound File 魔数:D0 CF 11 E0 A1 B1 1A E1
+ byte[] oleHeader = {(byte)0xD0, (byte)0xCF, (byte)0x11, (byte)0xE0,
+ (byte)0xA1, (byte)0xB1, (byte)0x1A, (byte)0xE1};
+ boolean isOLE = true;
+ for (int i = 0; i < oleHeader.length; i++) {
+ if (header[i] != oleHeader[i]) {
+ isOLE = false;
+ break;
+ }
+ }
+ if (isOLE) {
+ return fileName.endsWith(".doc") ? "doc" : "xls";
+ } else {
+ log.error("文件扩展名与文件头不匹配: {}", fileName);
+ return null;
+ }
+ } else {
+ log.warn("不支持的文件类型: {}", fileName);
+ return null;
+ }
+ } catch (IOException e) {
+ log.error("判断文件类型报错:{}", e.getMessage(), e);
+ return null;
+ }
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/utils/file/FileUploadUtils.java b/common/src/main/java/com/yyy/common/utils/file/FileUploadUtils.java
new file mode 100644
index 0000000..c1620b3
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/file/FileUploadUtils.java
@@ -0,0 +1,271 @@
+package com.yyy.common.utils.file;
+
+import com.documents4j.api.DocumentType;
+import com.documents4j.api.IConverter;
+import com.documents4j.job.LocalConverter;
+import com.yyy.common.config.AppConfig;
+import com.yyy.common.constant.Constants;
+import com.yyy.common.exception.file.FileNameLengthLimitExceededException;
+import com.yyy.common.exception.file.FileSizeLimitExceededException;
+import com.yyy.common.exception.file.InvalidExtensionException;
+import com.yyy.common.utils.common.CMDUtils;
+import com.yyy.common.utils.common.DateUtils;
+import com.yyy.common.utils.common.OSUtils;
+import com.yyy.common.utils.common.StringUtils;
+import com.yyy.common.utils.uuid.Seq;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FilenameUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+
+import java.io.*;
+import java.nio.file.Paths;
+import java.util.Objects;
+
+/**
+ * 文件上传工具类
+ */
+@Slf4j
+public class FileUploadUtils {
+ /**
+ * 默认大小 50M
+ */
+ public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
+
+ /**
+ * 默认的文件名最大长度 100
+ */
+ public static final int DEFAULT_FILE_NAME_LENGTH = 100;
+
+ /**
+ * 默认上传的地址
+ */
+ private static String defaultBaseDir = AppConfig.getProfile();
+
+ public static void setDefaultBaseDir(String defaultBaseDir) {
+ FileUploadUtils.defaultBaseDir = defaultBaseDir;
+ }
+
+ public static String getDefaultBaseDir() {
+ return defaultBaseDir;
+ }
+
+ /**
+ * 以默认配置进行文件上传
+ *
+ * @param file 上传的文件
+ * @return 文件名称
+ * @throws Exception
+ */
+ public static final String upload(MultipartFile file) throws IOException {
+ try {
+ return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
+ } catch (Exception e) {
+ throw new IOException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 根据文件路径上传
+ *
+ * @param baseDir 相对应用的基目录
+ * @param file 上传的文件
+ * @return 文件名称
+ * @throws IOException
+ */
+ public static final String upload(String baseDir, MultipartFile file) throws IOException {
+ try {
+ return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
+ } catch (Exception e) {
+ throw new IOException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 文件上传
+ *
+ * @param baseDir 相对应用的基目录
+ * @param file 上传的文件
+ * @param allowedExtension 上传文件类型
+ * @return 返回上传成功的文件名
+ * @throws FileSizeLimitExceededException 如果超出最大大小
+ * @throws FileNameLengthLimitExceededException 文件名太长
+ * @throws IOException 比如读写文件出错时
+ * @throws InvalidExtensionException 文件校验异常
+ */
+ public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
+ throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
+ InvalidExtensionException {
+ int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length();
+ if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
+ throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
+ }
+
+ assertAllowed(file, allowedExtension);
+
+ String fileName = extractFilename(file);
+
+ String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
+ file.transferTo(Paths.get(absPath));
+ return getPathFileName(baseDir, fileName);
+ }
+
+ /**
+ * 编码文件名
+ */
+ public static final String extractFilename(MultipartFile file) {
+ return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
+ FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
+ }
+
+ public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
+ File desc = new File(uploadDir + File.separator + fileName);
+
+ if (!desc.exists()) {
+ if (!desc.getParentFile().exists()) {
+ desc.getParentFile().mkdirs();
+ }
+ }
+ return desc;
+ }
+
+ public static final String getPathFileName(String uploadDir, String fileName) throws IOException {
+ int dirLastIndex = AppConfig.getProfile().length() + 1;
+ String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
+ return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
+ }
+
+ /**
+ * 文件大小校验
+ *
+ * @param file 上传的文件
+ * @return
+ * @throws FileSizeLimitExceededException 如果超出最大大小
+ * @throws InvalidExtensionException
+ */
+ public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
+ throws FileSizeLimitExceededException, InvalidExtensionException {
+ long size = file.getSize();
+ if (size > DEFAULT_MAX_SIZE) {
+ throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
+ }
+
+ String fileName = file.getOriginalFilename();
+ String extension = getExtension(file);
+ if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
+ if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) {
+ throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
+ fileName);
+ } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) {
+ throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
+ fileName);
+ } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) {
+ throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
+ fileName);
+ } else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) {
+ throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
+ fileName);
+ } else {
+ throw new InvalidExtensionException(allowedExtension, extension, fileName);
+ }
+ }
+ }
+
+ /**
+ * 判断MIME类型是否是允许的MIME类型
+ *
+ * @param extension
+ * @param allowedExtension
+ * @return
+ */
+ public static final boolean isAllowedExtension(String extension, String[] allowedExtension) {
+ for (String str : allowedExtension) {
+ if (str.equalsIgnoreCase(extension)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 获取文件名的后缀
+ *
+ * @param file 表单文件
+ * @return 后缀名
+ */
+ public static final String getExtension(MultipartFile file) {
+ String extension = FilenameUtils.getExtension(file.getOriginalFilename());
+ if (StringUtils.isEmpty(extension)) {
+ extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
+ }
+ return extension;
+ }
+
+ /**
+ * 将 MultipartFile 转换为 File 并保存
+ *
+ * @param multipartFile 上传的文件
+ * @param destPath 保存的目标路径
+ * @return 保存后的 File 对象
+ * @throws IOException 如果在保存过程中发生错误
+ */
+ public static File convertMultipartFileToFile(MultipartFile multipartFile, String destPath) throws IOException {
+ File destFile = new File(destPath);
+ // 如果目标路径不存在,创建目录
+ if (!destFile.getParentFile().exists()) {
+ destFile.getParentFile().mkdirs();
+ }
+ multipartFile.transferTo(destFile);
+ return destFile;
+ }
+
+ /**
+ * @param inputWord
+ * @param outputFile
+ * @return
+ */
+ public static boolean doc2docx(File inputWord, File outputFile) {
+ boolean flag = false;
+
+ String osName = OSUtils.getOsName();
+ //根据不同操作系统类型选择不同的数据获取方法
+ if (osName.startsWith("windows")) {
+ String fileType = FileTypeUtils.getFileType(inputWord);
+ try (InputStream docxInputStream = new FileInputStream(inputWord);
+ OutputStream outputStream = new FileOutputStream(outputFile)) {
+ IConverter converter = LocalConverter.builder().build();
+ if ("docx".equals(fileType)) {// docx转doc
+ flag = converter.convert(docxInputStream).as(DocumentType.DOCX).to(outputStream).as(DocumentType.DOC).execute();
+ } else if ("doc".equals(fileType)) { //
+ flag = converter.convert(docxInputStream).as(DocumentType.DOC).to(outputStream).as(DocumentType.DOCX).execute();
+ }
+ if (flag) {
+ converter.shutDown();
+ }
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ }
+ } else if (osName.startsWith("linux")) {
+ try {
+ // 兼容linux
+ // 构建LibreOffice的命令行工具命令
+ // libreoffice --invisible --convert-to docx -outdir /data/eam/temp /data/eam/temp/test.doc
+ String command = "libreoffice --invisible --convert-to docx --outdir " + outputFile.getParent() + " " + inputWord.getAbsolutePath();
+ log.info(command);
+ // 执行转换命令
+ try {
+ CMDUtils.executeLinuxCmd(command);
+ } catch (Exception e) {
+ log.error("linuxDocToDocx linux环境Doc转换为Docx时出现异常:", e);
+ }
+ } catch (Exception e) {
+ log.error("DocToDocx转换失败: " + e.getMessage(), e);
+ } finally {
+
+ }
+ } else {//其他服务器类型
+
+ }
+ return flag;
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/yyy/common/utils/file/FileUtils.java b/common/src/main/java/com/yyy/common/utils/file/FileUtils.java
new file mode 100644
index 0000000..02d8b1e
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/file/FileUtils.java
@@ -0,0 +1,245 @@
+package com.yyy.common.utils.file;
+
+import com.yyy.common.config.AppConfig;
+import com.yyy.common.utils.common.DateUtils;
+import com.yyy.common.utils.common.StringUtils;
+import com.yyy.common.utils.uuid.IdUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.ArrayUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * @Author: fy
+ * @Date: 2024/03/22
+ * @Description:文件处理工具类
+ */
+@Slf4j
+public class FileUtils {
+ public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
+
+ /**
+ * 输出指定文件的byte数组
+ *
+ * @param filePath 文件路径
+ * @param os 输出流
+ * @return
+ */
+ public static void writeBytes(String filePath, OutputStream os) throws IOException {
+ FileInputStream fis = null;
+ try {
+ File file = new File(filePath);
+ if (!file.exists()) {
+ throw new FileNotFoundException(filePath);
+ }
+ fis = new FileInputStream(file);
+ byte[] b = new byte[1024];
+ int length;
+ while ((length = fis.read(b)) > 0) {
+ os.write(b, 0, length);
+ }
+ } catch (IOException e) {
+ throw e;
+ } finally {
+ IOUtils.close(os);
+ IOUtils.close(fis);
+ }
+ }
+
+ /**
+ * 写数据到文件中
+ *
+ * @param data 数据
+ * @return 目标文件
+ * @throws IOException IO异常
+ */
+ public static String writeImportBytes(byte[] data) throws IOException {
+ return writeBytes(data, AppConfig.getImportPath());
+ }
+
+ /**
+ * 写数据到文件中
+ *
+ * @param data 数据
+ * @param uploadDir 目标文件
+ * @return 目标文件
+ * @throws IOException IO异常
+ */
+ public static String writeBytes(byte[] data, String uploadDir) throws IOException {
+ FileOutputStream fos = null;
+ String pathName = "";
+ try {
+ String extension = getFileExtendName(data);
+ pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
+ File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
+ fos = new FileOutputStream(file);
+ fos.write(data);
+ } finally {
+ IOUtils.close(fos);
+ }
+ return FileUploadUtils.getPathFileName(uploadDir, pathName);
+ }
+
+ /**
+ * 删除文件
+ *
+ * @param filePath 文件
+ * @return
+ */
+ public static boolean deleteFile(String filePath) {
+ boolean flag = false;
+ File file = new File(filePath);
+ // 路径为文件且不为空则进行删除
+ if (file.isFile() && file.exists()) {
+ flag = file.delete();
+ }
+ return flag;
+ }
+
+ /**
+ * 文件名称验证
+ *
+ * @param filename 文件名称
+ * @return true 正常 false 非法
+ */
+ public static boolean isValidFilename(String filename) {
+ return filename.matches(FILENAME_PATTERN);
+ }
+
+ /**
+ * 检查文件是否可下载
+ *
+ * @param resource 需要下载的文件
+ * @return true 正常 false 非法
+ */
+ public static boolean checkAllowDownload(String resource) {
+ // 禁止目录上跳级别
+ if (StringUtils.contains(resource, "..")) {
+ return false;
+ }
+
+ // 检查允许下载的文件规则
+ if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) {
+ return true;
+ }
+
+ // 不在允许下载的文件规则
+ return false;
+ }
+
+ /**
+ * 下载文件名重新编码
+ *
+ * @param request 请求对象
+ * @param fileName 文件名
+ * @return 编码后的文件名
+ */
+ public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException {
+ final String agent = request.getHeader("USER-AGENT");
+ String filename = fileName;
+ if (agent.contains("MSIE")) {
+ // IE浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ filename = filename.replace("+", " ");
+ } else if (agent.contains("Firefox")) {
+ // 火狐浏览器
+ filename = new String(fileName.getBytes(), "ISO8859-1");
+ } else if (agent.contains("Chrome")) {
+ // google浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ } else {
+ // 其它浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ }
+ return filename;
+ }
+
+ /**
+ * 下载文件名重新编码
+ *
+ * @param response 响应对象
+ * @param realFileName 真实文件名
+ * @return
+ */
+ public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
+ String percentEncodedFileName = percentEncode(realFileName);
+
+ StringBuilder contentDispositionValue = new StringBuilder();
+ contentDispositionValue.append("attachment; filename=")
+ .append(percentEncodedFileName)
+ .append(";")
+ .append("filename*=")
+ .append("utf-8''")
+ .append(percentEncodedFileName);
+
+ response.setHeader("Content-disposition", contentDispositionValue.toString());
+ }
+
+ /**
+ * 百分号编码工具方法
+ *
+ * @param s 需要百分号编码的字符串
+ * @return 百分号编码后的字符串
+ */
+ public static String percentEncode(String s) throws UnsupportedEncodingException {
+ String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
+ return encode.replaceAll("\\+", "%20");
+ }
+
+ /**
+ * 获取图像后缀
+ *
+ * @param photoByte 图像数据
+ * @return 后缀名
+ */
+ public static String getFileExtendName(byte[] photoByte) {
+ String strFileExtendName = "jpg";
+ if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
+ && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
+ strFileExtendName = "gif";
+ } else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
+ strFileExtendName = "jpg";
+ } else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
+ strFileExtendName = "bmp";
+ } else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
+ strFileExtendName = "png";
+ }
+ return strFileExtendName;
+ }
+
+ /**
+ * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png
+ *
+ * @param fileName 路径名称
+ * @return 没有文件路径的名称
+ */
+ public static String getName(String fileName) {
+ if (fileName == null) {
+ return null;
+ }
+ int lastUnixPos = fileName.lastIndexOf('/');
+ int lastWindowsPos = fileName.lastIndexOf('\\');
+ int index = Math.max(lastUnixPos, lastWindowsPos);
+ return fileName.substring(index + 1);
+ }
+
+ /**
+ * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi
+ *
+ * @param fileName 路径名称
+ * @return 没有文件路径和后缀的名称
+ */
+ public static String getNameNotSuffix(String fileName) {
+ if (fileName == null) {
+ return null;
+ }
+ String baseName = FilenameUtils.getBaseName(fileName);
+ return baseName;
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/yyy/common/utils/file/ImageUtils.java b/common/src/main/java/com/yyy/common/utils/file/ImageUtils.java
new file mode 100644
index 0000000..e0619e8
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/file/ImageUtils.java
@@ -0,0 +1,259 @@
+package com.yyy.common.utils.file;
+
+
+import com.yyy.common.config.AppConfig;
+import com.yyy.common.constant.Constants;
+import com.yyy.common.utils.common.StringUtils;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import sun.misc.BASE64Decoder;
+import sun.misc.BASE64Encoder;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Arrays;
+import java.util.Base64;
+
+/**
+ * 图片处理工具类
+ */
+public class ImageUtils
+{
+ private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
+
+ public static byte[] getImage(String imagePath)
+ {
+ InputStream is = getFile(imagePath);
+ try
+ {
+ return IOUtils.toByteArray(is);
+ }
+ catch (Exception e)
+ {
+ log.error("图片加载异常 {}", e);
+ return null;
+ }
+ finally
+ {
+ IOUtils.closeQuietly(is);
+ }
+ }
+
+ public static InputStream getFile(String imagePath)
+ {
+ try
+ {
+ byte[] result = readFile(imagePath);
+ result = Arrays.copyOf(result, result.length);
+ return new ByteArrayInputStream(result);
+ }
+ catch (Exception e)
+ {
+ log.error("获取图片异常 {}", e);
+ }
+ return null;
+ }
+
+ /**
+ * 读取文件为字节数据
+ *
+ * @param url 地址
+ * @return 字节数据
+ */
+ public static byte[] readFile(String url)
+ {
+ InputStream in = null;
+ try
+ {
+ if (url.startsWith("http"))
+ {
+ // 网络地址
+ URL urlObj = new URL(url);
+ URLConnection urlConnection = urlObj.openConnection();
+ urlConnection.setConnectTimeout(30 * 1000);
+ urlConnection.setReadTimeout(60 * 1000);
+ urlConnection.setDoInput(true);
+ in = urlConnection.getInputStream();
+ }
+ else
+ {
+ // 本机地址
+ String localPath = AppConfig.getProfile();
+ String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX);
+ in = new FileInputStream(downloadPath);
+ }
+ return IOUtils.toByteArray(in);
+ }
+ catch (Exception e)
+ {
+ log.error("获取文件路径异常 {}", e);
+ return null;
+ }
+ finally
+ {
+ IOUtils.closeQuietly(in);
+ }
+ }
+
+ /**
+ * 通过BufferedImage图片流调整图片大小
+ */
+ public static BufferedImage resizeImage(BufferedImage originalImage, int targetWidth, int targetHeight) throws IOException {
+ Image resultingImage = originalImage.getScaledInstance(targetWidth, targetHeight, Image.SCALE_AREA_AVERAGING);
+ BufferedImage outputImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
+ outputImage.getGraphics().drawImage(resultingImage, 0, 0, null);
+ return outputImage;
+ }
+
+ /**
+ * 返回base64图片
+ * @param data
+ * @return
+ */
+ public static String imageToBase64(byte[] data) {
+ BASE64Encoder encoder = new BASE64Encoder();
+ // 返回Base64编码过的字节数组字符串
+ return encoder.encode(data);
+ }
+
+ /**
+ * base64转换成byte数组
+ * @param base64
+ * @return
+ * @throws IOException
+ */
+ public static byte[] base64ToByte(String base64) throws IOException {
+ BASE64Decoder decoder = new BASE64Decoder();
+ // 返回Base64编码过的字节数组字符串
+ return decoder.decodeBuffer(base64);
+ }
+
+ /**
+ * BufferedImage图片流转byte[]数组
+ */
+ public static byte[] imageToBytes(BufferedImage bImage) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ ImageIO.write(bImage, "png", out);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return out.toByteArray();
+ }
+
+ /**
+ * byte[]数组转BufferedImage图片流
+ */
+ public static BufferedImage bytesToBufferedImage(byte[] ImageByte) {
+ ByteArrayInputStream in = new ByteArrayInputStream(ImageByte);
+ BufferedImage image = null;
+ try {
+ image = ImageIO.read(in);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return image;
+ }
+
+ /**
+ * 在线图片资源转base
+ * @param imageUrl
+ * @return
+ * @throws IOException
+ */
+ public static String convertToBase64(String imageUrl) throws IOException {
+ URL url = new URL(imageUrl);
+ String fileType = imageUrl.substring(imageUrl.length()-3);
+ String base64Str = "data:" + fileType + ";base64,";
+ InputStream inputStream = url.openStream();
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, bytesRead);
+ }
+ byte[] imageBytes = outputStream.toByteArray();
+ String base64String = base64Str + Base64.getEncoder().encodeToString(imageBytes);
+ return base64String;
+ }
+
+ //图片转化成base64字符串
+ public static String getImageStr(String imgPath) throws IOException {
+ File file = new File(imgPath);
+ String fileContentBase64 = null;
+ if(file.exists()){
+ String fileType = imgPath.substring(imgPath.length()-3);
+ String base64Str = "data:" + fileType + ";base64,";
+ String content = null;
+ //将图片文件转化为字节数组字符串,并对其进行Base64编码处理
+ InputStream in = null;
+ byte[] data = null;
+ //读取图片字节数组
+ try {
+ in = new FileInputStream(file);
+ data = new byte[in.available()];
+ in.read(data);
+ in.close();
+ //对字节数组Base64编码
+ if (data == null || data.length == 0) {
+ return null;
+ }
+ //content = Base64.encodeBytes(data);
+ content = new BASE64Encoder().encode(data);
+ if (content == null || "".equals(content)) {
+ return null;
+ }
+ // 缩小图片
+ if (StringUtils.isNotBlank(content)) {
+ BufferedImage bufferedImage = ImageUtils.bytesToBufferedImage(ImageUtils.base64ToByte(content));
+ if (bufferedImage != null){
+ int height = bufferedImage.getHeight();
+ int width = bufferedImage.getWidth();
+ // 如果图片宽度大于650,图片缩放
+ if (width > 500) {
+ //高度等比缩放
+ height = (int)(height*500.0/width);
+ BufferedImage imgZoom = ImageUtils.resizeImage(bufferedImage, 500, height);
+ content = ImageUtils.imageToBase64(ImageUtils.imageToBytes(imgZoom));
+ }
+ }
+ }
+ fileContentBase64 = base64Str + content;
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (in != null) {
+ in.close();
+ }
+ }
+ }
+
+ return fileContentBase64;
+ }
+
+
+ /**
+ * 验证给定的字符串是否为有效的 Base64 编码。
+ *
+ * @param base64Str 要验证的字符串
+ * @return 如果是有效的 Base64 编码,则返回 true;否则,返回 false
+ */
+ public static boolean isValidBase64(String base64Str) {
+ if (base64Str == null || base64Str.isEmpty()) {
+ return false;
+ }
+ try {
+ // 尝试解码
+ Base64.getDecoder().decode(base64Str);
+ return true;
+ } catch (IllegalArgumentException e) {
+ // 捕获解码异常,说明不是有效的 Base64 字符串
+ return false;
+ }
+ }
+}
diff --git a/common/src/main/java/com/yyy/common/utils/file/MimeTypeUtils.java b/common/src/main/java/com/yyy/common/utils/file/MimeTypeUtils.java
new file mode 100644
index 0000000..77eb820
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/file/MimeTypeUtils.java
@@ -0,0 +1,59 @@
+package com.yyy.common.utils.file;
+
+/**
+ * @Author: fy
+ * @Date: 2024/03/22
+ * @Description:媒体类型工具类
+ */
+public class MimeTypeUtils
+{
+ public static final String IMAGE_PNG = "image/png";
+
+ public static final String IMAGE_JPG = "image/jpg";
+
+ public static final String IMAGE_JPEG = "image/jpeg";
+
+ public static final String IMAGE_BMP = "image/bmp";
+
+ public static final String IMAGE_GIF = "image/gif";
+
+ public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" };
+
+ public static final String[] FLASH_EXTENSION = { "swf", "flv" };
+
+ public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
+ "asf", "rm", "rmvb" };
+
+ public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" };
+
+ public static final String[] DEFAULT_ALLOWED_EXTENSION = {
+ // 图片
+ "bmp", "gif", "jpg", "jpeg", "png",
+ // word excel powerpoint
+ "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
+ // 压缩文件
+ "rar", "zip", "gz", "bz2",
+ // 视频格式
+ "mp4", "avi", "rmvb",
+ // pdf
+ "pdf" };
+
+ public static String getExtension(String prefix)
+ {
+ switch (prefix)
+ {
+ case IMAGE_PNG:
+ return "png";
+ case IMAGE_JPG:
+ return "jpg";
+ case IMAGE_JPEG:
+ return "jpeg";
+ case IMAGE_BMP:
+ return "bmp";
+ case IMAGE_GIF:
+ return "gif";
+ default:
+ return "";
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/yyy/common/utils/poi/ExcelHandlerAdapter.java b/common/src/main/java/com/yyy/common/utils/poi/ExcelHandlerAdapter.java
new file mode 100644
index 0000000..0e01bc7
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/poi/ExcelHandlerAdapter.java
@@ -0,0 +1,25 @@
+package com.yyy.common.utils.poi;
+
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Workbook;
+
+/**
+ * @Author: fy
+ * @Date: 2024/04/08
+ * @Description:Excel数据格式处理适配器
+ */
+public interface ExcelHandlerAdapter
+{
+ /**
+ * 格式化
+ *
+ * @param value 单元格数据值
+ * @param args excel注解args参数组
+ * @param cell 单元格对象
+ * @param wb 工作簿对象
+ *
+ * @return 处理后的值
+ */
+ Object format(Object value, String[] args, Cell cell, Workbook wb);
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/yyy/common/utils/poi/ExcelUtil.java b/common/src/main/java/com/yyy/common/utils/poi/ExcelUtil.java
new file mode 100644
index 0000000..b9698c7
--- /dev/null
+++ b/common/src/main/java/com/yyy/common/utils/poi/ExcelUtil.java
@@ -0,0 +1,1718 @@
+package com.yyy.common.utils.poi;
+
+import com.yyy.common.annotation.excel.Excel;
+import com.yyy.common.annotation.excel.Excel.ColumnType;
+import com.yyy.common.annotation.excel.Excel.Type;
+import com.yyy.common.annotation.excel.Excels;
+import com.yyy.common.config.AppConfig;
+import com.yyy.common.core.domain.Result;
+import com.yyy.common.core.domain.ResultCode;
+import com.yyy.common.core.text.Convert;
+import com.yyy.common.exception.UtilException;
+import com.yyy.common.utils.common.DateUtils;
+import com.yyy.common.utils.common.DictUtils;
+import com.yyy.common.utils.common.StringUtils;
+import com.yyy.common.utils.file.FileTypeUtils;
+import com.yyy.common.utils.file.FileUtils;
+import com.yyy.common.utils.file.ImageUtils;
+import com.yyy.common.utils.reflect.ReflectUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.RegExUtils;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.poi.hssf.usermodel.*;
+import org.apache.poi.ooxml.POIXMLDocumentPart;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellRangeAddressList;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.*;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Excel相关处理
+ *
+ */
+public class ExcelUtil {
+ private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
+
+ public static final String FORMULA_REGEX_STR = "=|-|\\+|@";
+
+ public static final String[] FORMULA_STR = { "=", "-", "+", "@" };
+
+ /**
+ * 用于dictType属性数据存储,避免重复查缓存
+ */
+ public Map sysDictMap = new HashMap();
+
+ /**
+ * Excel sheet最大行数,默认65536
+ */
+ public static final int sheetSize = 65536;
+
+ /**
+ * 工作表名称
+ */
+ private String sheetName;
+
+ /**
+ * 导出类型(EXPORT:导出数据;IMPORT:导入模板)
+ */
+ private Excel.Type type;
+
+ /**
+ * 工作薄对象
+ */
+ private Workbook wb;
+
+ /**
+ * 工作表对象
+ */
+ private Sheet sheet;
+
+ /**
+ * 样式列表
+ */
+ private Map styles;
+
+ /**
+ * 导入导出数据列表
+ */
+ private List list;
+
+ /**
+ * 注解列表
+ */
+ private List