mirror of
https://github.com/CodePhiliaX/Chat2DB.git
synced 2025-08-01 08:52:11 +08:00
表结构文档导出
This commit is contained in:
@ -56,6 +56,23 @@
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
</dependency>
|
||||
<!--poi-tl-->
|
||||
<dependency>
|
||||
<groupId>com.deepoove</groupId>
|
||||
<artifactId>poi-tl</artifactId>
|
||||
<version>1.10.5</version>
|
||||
</dependency>
|
||||
<!--pdf-->
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>itext-asian</artifactId>
|
||||
<version>5.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>itextpdf</artifactId>
|
||||
<version>5.5.13</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -0,0 +1,90 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb;
|
||||
|
||||
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
|
||||
import ai.chat2db.server.domain.api.param.TablePageQueryParam;
|
||||
import ai.chat2db.server.domain.api.param.TableQueryParam;
|
||||
import ai.chat2db.server.domain.api.param.TableSelector;
|
||||
import ai.chat2db.server.domain.api.service.TableService;
|
||||
import ai.chat2db.server.tools.base.wrapper.result.PageResult;
|
||||
import ai.chat2db.server.tools.common.util.EasyEnumUtils;
|
||||
import ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;
|
||||
import ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;
|
||||
import ai.chat2db.server.web.api.controller.rdb.factory.ExportServiceFactory;
|
||||
import ai.chat2db.server.web.api.controller.rdb.request.DataExportRequest;
|
||||
import ai.chat2db.server.web.api.controller.rdb.vo.TableVO;
|
||||
import ai.chat2db.spi.model.Table;
|
||||
import ai.chat2db.spi.sql.Chat2DBContext;
|
||||
import ai.chat2db.spi.util.JdbcUtils;
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import com.alibaba.druid.DbType;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* RdbDocController
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
@ConnectionInfoAspect
|
||||
@RequestMapping("/api/rdb/doc")
|
||||
@Controller
|
||||
@Slf4j
|
||||
public class RdbDocController {
|
||||
|
||||
@Autowired
|
||||
private TableService tableService;
|
||||
|
||||
@Autowired
|
||||
private RdbWebConverter rdbWebConverter;
|
||||
|
||||
/**
|
||||
* export data
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
@PostMapping("/export")
|
||||
public void export(@Valid @RequestBody DataExportRequest request, HttpServletResponse response) throws Exception {
|
||||
ExportTypeEnum exportType = EasyEnumUtils.getEnum(ExportTypeEnum.class, request.getExportType());
|
||||
response.setCharacterEncoding("utf-8");
|
||||
String fileName = URLEncoder.encode(
|
||||
request.getDatabaseName() + "_" + LocalDateTime.now().format(DatePattern.PURE_DATETIME_FORMATTER),
|
||||
StandardCharsets.UTF_8)
|
||||
.replaceAll("\\+", "%20");
|
||||
TablePageQueryParam queryParam = rdbWebConverter.tablePageRequest2param(request);
|
||||
queryParam.setPageNo(1);
|
||||
queryParam.setPageSize(Integer.MAX_VALUE);
|
||||
TableSelector tableSelector = new TableSelector();
|
||||
tableSelector.setColumnList(true);
|
||||
tableSelector.setIndexList(true);
|
||||
PageResult<Table> tableDTOPageResult = tableService.pageQuery(queryParam, tableSelector);
|
||||
List<TableVO> tableVOS = rdbWebConverter.tableDto2vo(tableDTOPageResult.getData());
|
||||
TableQueryParam param = rdbWebConverter.tableRequest2param(request);
|
||||
for (TableVO tableVO: tableVOS) {
|
||||
param.setTableName(tableVO.getName());
|
||||
tableVO.setColumnList(rdbWebConverter.columnDto2vo(tableService.queryColumns(param)));
|
||||
tableVO.setIndexList(rdbWebConverter.indexDto2vo(tableService.queryIndexes(param)));
|
||||
}
|
||||
Class<?> targetClass = ExportServiceFactory.get(exportType.getCode());
|
||||
Constructor<?> constructor = targetClass.getDeclaredConstructor();
|
||||
DatabaseExportService databaseExportService = (DatabaseExportService) constructor.newInstance();
|
||||
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + databaseExportService.getSuffix());
|
||||
response.setContentType(databaseExportService.getContentType());
|
||||
// 设置数据集合
|
||||
databaseExportService.setExportList(tableVOS);
|
||||
databaseExportService.generate(request.getDatabaseName(), response.getOutputStream(), new ExportOptions());
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ import ai.chat2db.server.domain.api.param.ShowCreateTableParam;
|
||||
import ai.chat2db.server.domain.api.param.TablePageQueryParam;
|
||||
import ai.chat2db.server.domain.api.param.TableQueryParam;
|
||||
import ai.chat2db.server.web.api.controller.data.source.vo.DatabaseVO;
|
||||
import ai.chat2db.server.web.api.controller.rdb.request.DataExportRequest;
|
||||
import ai.chat2db.server.web.api.controller.rdb.request.DdlCountRequest;
|
||||
import ai.chat2db.server.web.api.controller.rdb.request.DdlExportRequest;
|
||||
import ai.chat2db.server.web.api.controller.rdb.request.DdlRequest;
|
||||
@ -100,6 +101,20 @@ public abstract class RdbWebConverter {
|
||||
* @return
|
||||
*/
|
||||
public abstract TablePageQueryParam tablePageRequest2param(TableBriefQueryRequest request);
|
||||
/**
|
||||
* 参数转换
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public abstract TablePageQueryParam tablePageRequest2param(DataExportRequest request);
|
||||
/**
|
||||
* 参数转换
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public abstract TableQueryParam tableRequest2param(DataExportRequest request);
|
||||
|
||||
/**
|
||||
* 参数转换
|
||||
|
@ -0,0 +1,167 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.doc;
|
||||
|
||||
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
|
||||
import ai.chat2db.server.domain.api.model.IndexInfo;
|
||||
import ai.chat2db.server.domain.api.model.TableParameter;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;
|
||||
import ai.chat2db.server.web.api.controller.rdb.vo.ColumnVO;
|
||||
import ai.chat2db.server.web.api.controller.rdb.vo.IndexVO;
|
||||
import ai.chat2db.server.web.api.controller.rdb.vo.TableVO;
|
||||
import ai.chat2db.server.web.api.util.StringUtils;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.val;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* DatabaseExportService
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
public class DatabaseExportService {
|
||||
protected ExportTypeEnum exportTypeEnum;
|
||||
@Getter
|
||||
public String suffix;
|
||||
|
||||
@Getter
|
||||
public String contentType;
|
||||
/**
|
||||
* 导出excel 集合
|
||||
**/
|
||||
@Setter
|
||||
@Getter
|
||||
public List<TableVO> exportList;
|
||||
/**
|
||||
* 导出word、excel 表信息 集合
|
||||
**/
|
||||
public static Map<String, List<TableParameter>> listMap = new LinkedHashMap<>();
|
||||
/**
|
||||
* 导出word 索引 集合
|
||||
**/
|
||||
public static Map<String, List<IndexInfo>> indexMap = new HashMap<>(0);
|
||||
/**
|
||||
* 连接符
|
||||
**/
|
||||
public final static String JOINER = "---";
|
||||
|
||||
private void init() {
|
||||
listMap.clear();
|
||||
indexMap.clear();
|
||||
}
|
||||
|
||||
public void generate(String databaseName, OutputStream outputStream, ExportOptions exportOptions) {
|
||||
init();
|
||||
exportList.forEach(item -> {
|
||||
dataAssemble(databaseName, exportOptions, item);
|
||||
});
|
||||
try {
|
||||
export(outputStream, exportOptions);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("导出失败!请联系开发者,邮箱:963565242@qq.com" + e);
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据处理
|
||||
*
|
||||
* @param exportOptions 配置信息
|
||||
**/
|
||||
public void dataAssemble(String databaseName, ExportOptions exportOptions, TableVO item) {
|
||||
boolean isExportIndex = Optional.ofNullable(exportOptions.getIsExportIndex()).orElse(false);
|
||||
val t = new TableParameter();
|
||||
t.setFieldName(item.getName() + "[" + StringUtils.isNull(item.getComment()) + "]");
|
||||
List<TableParameter> colForTable = new LinkedList<>();
|
||||
for (ColumnVO info : item.getColumnList()) {
|
||||
val p = new TableParameter();
|
||||
p.setFieldName(info.getName()).setColumnDefault(info.getDefaultValue())
|
||||
.setColumnComment(info.getComment())
|
||||
.setColumnType(info.getColumnType())
|
||||
.setLength(String.valueOf(info.getCharacterMaximumLength())).setIsNullAble(String.valueOf(info.getNullable()))
|
||||
.setDecimalPlaces(String.valueOf(info.getNumericPrecision()));
|
||||
colForTable.add(p);
|
||||
}
|
||||
String key = databaseName + JOINER + t.getFieldName();
|
||||
listMap.put(key, colForTable);
|
||||
if (isExportIndex) {
|
||||
int index = key.lastIndexOf("[");
|
||||
String str = key.substring(0, index);
|
||||
indexMap.put(str, vo2Info(item.getIndexList()));
|
||||
}
|
||||
//赋值序号
|
||||
for (Map.Entry<String, List<TableParameter>> map : listMap.entrySet()) {
|
||||
//赋值序号
|
||||
List<TableParameter> list = map.getValue();
|
||||
IntStream.range(0, list.size()).forEach(x -> {
|
||||
list.get(x).setNo(String.valueOf(x + 1));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private List<IndexInfo> vo2Info(List<IndexVO> indexList) {
|
||||
return indexList.stream().map(v -> {
|
||||
IndexInfo info = new IndexInfo();
|
||||
info.setName(v.getName());
|
||||
info.setColumnName(v.getColumns());
|
||||
info.setIndexType(v.getType());
|
||||
info.setComment(v.getComment());
|
||||
return info;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出
|
||||
*
|
||||
* @param outputStream 文件流
|
||||
**/
|
||||
public void export(OutputStream outputStream, ExportOptions exportOptions) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理空串或null字符
|
||||
*
|
||||
* @param source 源字符
|
||||
* @return java.lang.String
|
||||
**/
|
||||
public String dealWith(String source) {
|
||||
return StringUtils.isNullOrEmpty(source);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public Object[] getIndexValues(IndexInfo indexInfoVO) {
|
||||
Object[] values = new Object[IndexInfo.class.getDeclaredFields().length];
|
||||
values[0] = dealWith(indexInfoVO.getName());
|
||||
values[1] = dealWith(indexInfoVO.getColumnName());
|
||||
values[2] = dealWith(indexInfoVO.getIndexType());
|
||||
values[3] = dealWith(indexInfoVO.getIndexMethod());
|
||||
values[4] = dealWith(indexInfoVO.getComment());
|
||||
return values;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public Object[] getColumnValues(TableParameter tableParameter) {
|
||||
Object[] values = new Object[TableParameter.class.getDeclaredFields().length];
|
||||
values[0] = StringUtils.isNull(tableParameter.getNo());
|
||||
values[1] = StringUtils.isNull(tableParameter.getFieldName());
|
||||
values[2] = StringUtils.isNull(tableParameter.getColumnType());
|
||||
values[3] = StringUtils.isNull(tableParameter.getLength());
|
||||
values[4] = StringUtils.isNull(tableParameter.getIsNullAble());
|
||||
values[5] = StringUtils.isNull(tableParameter.getColumnDefault());
|
||||
values[6] = StringUtils.isNull(tableParameter.getDecimalPlaces());
|
||||
values[7] = StringUtils.isNull(tableParameter.getColumnComment());
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.doc.adaptive;
|
||||
|
||||
import com.alibaba.excel.write.style.row.AbstractRowHeightStyleStrategy;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellType;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* CustomCellWriteHeightConfig
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
public class CustomCellWriteHeightConfig extends AbstractRowHeightStyleStrategy {
|
||||
/**
|
||||
* 默认高度
|
||||
*/
|
||||
private static final Integer DEFAULT_HEIGHT = 300;
|
||||
|
||||
@Override
|
||||
protected void setHeadColumnHeight(Row row, int relativeRowIndex) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setContentColumnHeight(Row row, int relativeRowIndex) {
|
||||
Iterator<Cell> cellIterator = row.cellIterator();
|
||||
if (!cellIterator.hasNext()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 默认为 1行高度
|
||||
int maxHeight = 1;
|
||||
while (cellIterator.hasNext()) {
|
||||
Cell cell = cellIterator.next();
|
||||
if (cell.getCellType() == CellType.STRING) {
|
||||
if (cell.getStringCellValue().contains("\n")) {
|
||||
int length = cell.getStringCellValue().split("\n").length;
|
||||
maxHeight = Math.max(maxHeight, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
row.setHeight((short) (maxHeight * DEFAULT_HEIGHT));
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.doc.adaptive;
|
||||
|
||||
import com.alibaba.excel.enums.CellDataTypeEnum;
|
||||
import com.alibaba.excel.metadata.Head;
|
||||
import com.alibaba.excel.metadata.data.CellData;
|
||||
import com.alibaba.excel.metadata.data.WriteCellData;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* CustomCellWriteWidthConfig
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
public class CustomCellWriteWidthConfig extends AbstractColumnWidthStyleStrategy {
|
||||
private Map<String, Map<Integer, Integer>> CACHE = new HashMap<>();
|
||||
|
||||
@Override
|
||||
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {
|
||||
boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
|
||||
if (needSetWidth) {
|
||||
Map<Integer, Integer> maxColumnWidthMap = CACHE.computeIfAbsent(writeSheetHolder.getSheetName(), k -> new HashMap<>(0));
|
||||
|
||||
Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
|
||||
if (columnWidth >= 0) {
|
||||
if (columnWidth > 119) {
|
||||
columnWidth = 120;
|
||||
}
|
||||
|
||||
Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
|
||||
if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
|
||||
maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
|
||||
Sheet sheet = writeSheetHolder.getSheet();
|
||||
sheet.setColumnWidth(cell.getColumnIndex(), Math.min(Double.valueOf(columnWidth * 256 * 1.8).intValue(), 16384));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算长度
|
||||
* @param cellDataList cell数据
|
||||
* @param cell cell
|
||||
* @param isHead 是否是标题
|
||||
* @return
|
||||
*/
|
||||
private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
|
||||
if (isHead) {
|
||||
return cell.getStringCellValue().getBytes().length;
|
||||
} else {
|
||||
CellData<?> cellData = cellDataList.get(0);
|
||||
CellDataTypeEnum type = cellData.getType();
|
||||
if (type == null) {
|
||||
return -1;
|
||||
} else {
|
||||
switch (type) {
|
||||
case STRING:
|
||||
// 换行符(数据需要提前解析好)
|
||||
int index = cellData.getStringValue().indexOf("\n");
|
||||
return index != -1 ?
|
||||
cellData.getStringValue().substring(0, index).getBytes().length + 1 : cellData.getStringValue().getBytes().length + 1;
|
||||
case BOOLEAN:
|
||||
return cellData.getBooleanValue().toString().getBytes().length;
|
||||
case NUMBER:
|
||||
return cellData.getNumberValue().toString().getBytes().length;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.doc.conf;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 生成选项
|
||||
*
|
||||
* @author lzy
|
||||
*/
|
||||
@Data
|
||||
public class ExportOptions {
|
||||
/**
|
||||
* 是否导出多sheet
|
||||
*/
|
||||
private Boolean isExportMoreSheet = Boolean.FALSE;
|
||||
/**
|
||||
* 是否导出索引
|
||||
*/
|
||||
private Boolean isExportIndex = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* 导出文件后缀
|
||||
**/
|
||||
private String fileSuffix;
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.doc.constant;
|
||||
|
||||
import ai.chat2db.server.tools.common.util.I18nUtils;
|
||||
import lombok.extern.java.Log;
|
||||
|
||||
/**
|
||||
* CommonConstant
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
@Log
|
||||
public final class CommonConstant {
|
||||
/**
|
||||
* 表head
|
||||
**/
|
||||
public static String[] INDEX_HEAD_NAMES =
|
||||
{I18nUtils.getMessage("main.indexName"),
|
||||
I18nUtils.getMessage("main.indexFieldName"),
|
||||
I18nUtils.getMessage("main.indexType"),
|
||||
I18nUtils.getMessage("main.indexMethod"),
|
||||
I18nUtils.getMessage("main.indexNote")};
|
||||
public static String[] COLUMN_HEAD_NAMES =
|
||||
{I18nUtils.getMessage("main.fieldNo"),
|
||||
I18nUtils.getMessage("main.fieldName"),
|
||||
I18nUtils.getMessage("main.fieldType"),
|
||||
I18nUtils.getMessage("main.fieldLength"),
|
||||
I18nUtils.getMessage("main.fieldIfEmpty"),
|
||||
I18nUtils.getMessage("main.fieldDefault"),
|
||||
I18nUtils.getMessage("main.fieldDecimalPlaces"),
|
||||
I18nUtils.getMessage("main.fieldNote")};
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.doc.constant;
|
||||
|
||||
|
||||
import ai.chat2db.server.tools.common.util.I18nUtils;
|
||||
|
||||
/**
|
||||
* PatternConstant
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
public final class PatternConstant {
|
||||
|
||||
/**
|
||||
* 公共
|
||||
*/
|
||||
public static final String MD_SPLIT = "|";
|
||||
|
||||
/**
|
||||
* Markdown
|
||||
*/
|
||||
public static final String TITLE = "# %s";
|
||||
public static final String CATALOG = "## %s";
|
||||
public static final String ALL_TABLE_HEADER = MD_SPLIT + I18nUtils.getMessage("main.fieldNo") + MD_SPLIT +
|
||||
I18nUtils.getMessage("main.fieldName") + MD_SPLIT +
|
||||
I18nUtils.getMessage("main.fieldType") + MD_SPLIT +
|
||||
I18nUtils.getMessage("main.fieldLength") + MD_SPLIT +
|
||||
I18nUtils.getMessage("main.fieldIfEmpty") + MD_SPLIT +
|
||||
I18nUtils.getMessage("main.fieldDefault") + MD_SPLIT +
|
||||
I18nUtils.getMessage("main.fieldDecimalPlaces") + MD_SPLIT +
|
||||
I18nUtils.getMessage("main.fieldNote") + MD_SPLIT;
|
||||
public static String TABLE_BODY = "|%s|%s|%s|%s|%s|%s|%s|%s|";
|
||||
public static String TABLE_SEPARATOR = "|:----:|----|----|----|----|----|----|----|";
|
||||
public static final String ALL_INDEX_TABLE_HEADER = MD_SPLIT + I18nUtils.getMessage("main.indexName") + MD_SPLIT +
|
||||
I18nUtils.getMessage("main.indexFieldName") + MD_SPLIT +
|
||||
I18nUtils.getMessage("main.indexType") + MD_SPLIT +
|
||||
I18nUtils.getMessage("main.indexMethod") + MD_SPLIT +
|
||||
I18nUtils.getMessage("main.indexNote") + MD_SPLIT;
|
||||
public static String INDEX_TABLE_BODY = "|%s|%s|%s|%s|";
|
||||
public static String INDEX_TABLE_SEPARATOR = "|:----:|----|----|----|";
|
||||
|
||||
/**
|
||||
* Html
|
||||
*/
|
||||
public static final String HTML_TITLE = "<h1 id=\"{0}\">{0}</h1>";
|
||||
public static final String HTML_CATALOG = "<h2 id=\"{0}\">{1}</h2>";
|
||||
public static final String HTML_INDEX_ITEM = "<a href=\"#{0}\" title=\"{0}\">{1}</a>";
|
||||
public static String HTML_TABLE_HEADER = "<tr><th>序号</th><th>字段名</th><th>类型</th><th>长度</th><th>是否为空</th><th>默认值</th><th>小数位</th><th>注释</th></tr>";
|
||||
public static String HTML_TABLE_BODY = "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>";
|
||||
public static String HTML_INDEX_TABLE_HEADER = "<tr><th>名称</th><th>字段</th><th>DDL</th><th>注释</th></tr>";
|
||||
public static String HTML_INDEX_TABLE_BODY = "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>";
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.doc.export;
|
||||
|
||||
import ai.chat2db.server.domain.api.enums.ExportFileSuffix;
|
||||
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
|
||||
import ai.chat2db.server.domain.api.model.TableParameter;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.adaptive.CustomCellWriteHeightConfig;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.adaptive.CustomCellWriteWidthConfig;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.merge.MyMergeExcel;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.style.CustomExcelStyle;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.val;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* ExportExcelService
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
public class ExportExcelService extends DatabaseExportService {
|
||||
|
||||
public ExportExcelService() {
|
||||
exportTypeEnum = ExportTypeEnum.EXCEL;
|
||||
suffix = ExportFileSuffix.EXCEL.getSuffix();
|
||||
contentType = "text/csv";
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void export(OutputStream outputStream, ExportOptions exportOptions) {
|
||||
List<TableParameter> export = new ArrayList<>();
|
||||
for (Map.Entry<String, List<TableParameter>> item : listMap.entrySet()) {
|
||||
val t = new TableParameter();
|
||||
t.setNo(item.getKey()).setColumnComment(MyMergeExcel.NAME);
|
||||
export.add(t);
|
||||
export.addAll(item.getValue());
|
||||
}
|
||||
EasyExcel.write(outputStream)
|
||||
.registerWriteHandler(new HorizontalCellStyleStrategy(CustomExcelStyle.getHeadStyle(), CustomExcelStyle.getContentWriteCellStyle()))
|
||||
.registerWriteHandler(new CustomCellWriteHeightConfig())
|
||||
.registerWriteHandler(new CustomCellWriteWidthConfig())
|
||||
.registerWriteHandler(new MyMergeExcel())
|
||||
.sheet("表结构")
|
||||
.doWrite(export);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.doc.export;
|
||||
|
||||
import ai.chat2db.server.domain.api.enums.ExportFileSuffix;
|
||||
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
|
||||
import ai.chat2db.server.domain.api.model.IndexInfo;
|
||||
import ai.chat2db.server.domain.api.model.TableParameter;
|
||||
import ai.chat2db.server.tools.common.config.GlobalDict;
|
||||
import ai.chat2db.server.tools.common.util.I18nUtils;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.constant.PatternConstant;
|
||||
import ai.chat2db.server.web.api.util.StringUtils;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* ExportHtmlService
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
public class ExportHtmlService extends DatabaseExportService {
|
||||
|
||||
public ExportHtmlService() {
|
||||
exportTypeEnum = ExportTypeEnum.HTML;
|
||||
suffix = ExportFileSuffix.HTML.getSuffix();
|
||||
contentType = "text/html";
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void export(OutputStream outputStream, ExportOptions exportOptions) {
|
||||
Map<String, List<Map.Entry<String, List<TableParameter>>>> allMap = listMap.entrySet()
|
||||
.stream().collect(Collectors.groupingBy(v -> v.getKey().split("---")[0]));
|
||||
StringBuilder htmlText = new StringBuilder();
|
||||
StringBuilder catalogue = new StringBuilder();
|
||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
|
||||
for (Map.Entry<String, List<Map.Entry<String, List<TableParameter>>>> myMap : allMap.entrySet()) {
|
||||
//数据库名
|
||||
String database = myMap.getKey();
|
||||
String title = MessageFormat.format(PatternConstant.HTML_TITLE, I18nUtils.getMessage("main.databaseText") + database);
|
||||
//数据库名-目录
|
||||
catalogue.append("<li>").append(MessageFormat.format(PatternConstant.HTML_INDEX_ITEM, I18nUtils.getMessage("main.databaseText")
|
||||
+ database, I18nUtils.getMessage("main.databaseText") + database)).append("<ol>");
|
||||
htmlText.append(title).append("\n");
|
||||
for (Map.Entry<String, List<TableParameter>> parameterMap : myMap.getValue()) {
|
||||
//表名
|
||||
String tableName = parameterMap.getKey().split("---")[1];
|
||||
//表名-目录
|
||||
catalogue.append("<li>").append(MessageFormat.format(PatternConstant.HTML_INDEX_ITEM, database + tableName, tableName));
|
||||
htmlText.append(MessageFormat.format(PatternConstant.HTML_CATALOG, database + tableName, tableName)).append("\n<p></p>");
|
||||
//索引Table
|
||||
if (!indexMap.isEmpty()) {
|
||||
htmlText.append("<table>\n");
|
||||
htmlText.append(PatternConstant.HTML_INDEX_TABLE_HEADER);
|
||||
String name = parameterMap.getKey().split("\\[")[0];
|
||||
List<IndexInfo> indexInfoVOList = indexMap.get(name);
|
||||
for (IndexInfo indexInfo : indexInfoVOList) {
|
||||
htmlText.append(String.format(PatternConstant.HTML_INDEX_TABLE_BODY, getIndexValues(indexInfo)));
|
||||
}
|
||||
htmlText.append("</table>\n");
|
||||
htmlText.append("\n<p></p>");
|
||||
} else {
|
||||
htmlText.append(String.format(PatternConstant.HTML_INDEX_TABLE_BODY, getIndexValues(new IndexInfo())));
|
||||
}
|
||||
//字段Table
|
||||
htmlText.append("<table>\n");
|
||||
htmlText.append(PatternConstant.HTML_TABLE_HEADER);
|
||||
List<TableParameter> exportList = parameterMap.getValue();
|
||||
for (TableParameter tableParameter : exportList) {
|
||||
htmlText.append(String.format(PatternConstant.HTML_TABLE_BODY, getColumnValues(tableParameter)));
|
||||
}
|
||||
htmlText.append("</table>\n");
|
||||
}
|
||||
htmlText.append("<p></p>");
|
||||
catalogue.append("</ol>");
|
||||
}
|
||||
catalogue.append("</li>");
|
||||
|
||||
String filePath = GlobalDict.templateDir + GlobalDict.TEMPLATE_FILE.get(0);
|
||||
try (FileInputStream inputStream = new FileInputStream(filePath);
|
||||
ByteArrayOutputStream result = new ByteArrayOutputStream()) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int length;
|
||||
while ((length = inputStream.read(buffer)) != -1) {
|
||||
result.write(buffer, 0, length);
|
||||
}
|
||||
String str = result.toString(String.valueOf(StandardCharsets.UTF_8));
|
||||
|
||||
str = str.replace("${data}", htmlText).replace("${catalogue}", catalogue);
|
||||
writer.write(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dealWith(String source) {
|
||||
return StringUtils.isNullForHtml(source);
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.doc.export;
|
||||
|
||||
import ai.chat2db.server.domain.api.enums.ExportFileSuffix;
|
||||
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
|
||||
import ai.chat2db.server.domain.api.model.IndexInfo;
|
||||
import ai.chat2db.server.domain.api.model.TableParameter;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.constant.PatternConstant;
|
||||
import ai.chat2db.server.web.api.util.StringUtils;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* ExportMarkdownService
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
public class ExportMarkdownService extends DatabaseExportService {
|
||||
|
||||
public ExportMarkdownService() {
|
||||
exportTypeEnum = ExportTypeEnum.MARKDOWN;
|
||||
suffix = ExportFileSuffix.MARKDOWN.getSuffix();
|
||||
contentType = "text/plain";
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void export(OutputStream outputStream, ExportOptions exportOptions) {
|
||||
Map<String, List<Map.Entry<String, List<TableParameter>>>> allMap = listMap.entrySet()
|
||||
.stream().collect(Collectors.groupingBy(v -> v.getKey().split("---")[0]));
|
||||
try (BufferedWriter fileWriter = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
|
||||
for (Map.Entry<String, List<Map.Entry<String, List<TableParameter>>>> myMap : allMap.entrySet()) {
|
||||
//数据库名
|
||||
String database = myMap.getKey();
|
||||
String title = String.format(PatternConstant.TITLE, "数据库:" + database);
|
||||
fileWriter.write(title);
|
||||
writeLineSeparator(fileWriter, 2);
|
||||
for (Map.Entry<String, List<TableParameter>> parameterMap : myMap.getValue()) {
|
||||
//表名
|
||||
String tableName = parameterMap.getKey().split("---")[1];
|
||||
fileWriter.write(String.format(PatternConstant.CATALOG, tableName));
|
||||
writeLineSeparator(fileWriter, 1);
|
||||
//索引Table
|
||||
if (!indexMap.isEmpty()) {
|
||||
fileWriter.write(PatternConstant.ALL_INDEX_TABLE_HEADER);
|
||||
writeLineSeparator(fileWriter, 1);
|
||||
fileWriter.write(PatternConstant.INDEX_TABLE_SEPARATOR);
|
||||
writeLineSeparator(fileWriter, 1);
|
||||
String name = parameterMap.getKey().split("\\[")[0];
|
||||
List<IndexInfo> indexInfoVOList = indexMap.get(name);
|
||||
for (int j = 0; j < indexInfoVOList.size(); j++) {
|
||||
fileWriter.write(String.format(PatternConstant.INDEX_TABLE_BODY, getIndexValues(indexInfoVOList.get(j))));
|
||||
writeLineSeparator(fileWriter, 1);
|
||||
}
|
||||
writeLineSeparator(fileWriter, 1);
|
||||
}
|
||||
writeLineSeparator(fileWriter, 2);
|
||||
fileWriter.write(PatternConstant.ALL_TABLE_HEADER);
|
||||
writeLineSeparator(fileWriter, 1);
|
||||
fileWriter.write(PatternConstant.TABLE_SEPARATOR);
|
||||
writeLineSeparator(fileWriter, 1);
|
||||
//字段Table
|
||||
List<TableParameter> exportList = parameterMap.getValue();
|
||||
for (TableParameter tableParameter : exportList) {
|
||||
fileWriter.write(String.format(PatternConstant.TABLE_BODY, getColumnValues(tableParameter)));
|
||||
writeLineSeparator(fileWriter, 1);
|
||||
}
|
||||
writeLineSeparator(fileWriter, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeLineSeparator(BufferedWriter fileWriter, int number) throws IOException {
|
||||
for (int i = 0; i < number; i++) {
|
||||
fileWriter.write(System.lineSeparator());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dealWith(String source) {
|
||||
return StringUtils.isNullForHtml(source);
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.doc.export;
|
||||
|
||||
import ai.chat2db.server.domain.api.enums.ExportFileSuffix;
|
||||
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
|
||||
import ai.chat2db.server.domain.api.model.IndexInfo;
|
||||
import ai.chat2db.server.domain.api.model.TableParameter;
|
||||
import ai.chat2db.server.tools.common.util.I18nUtils;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.constant.CommonConstant;
|
||||
import com.itextpdf.text.Document;
|
||||
import com.itextpdf.text.Font;
|
||||
import com.itextpdf.text.Paragraph;
|
||||
import com.itextpdf.text.pdf.BaseFont;
|
||||
import com.itextpdf.text.pdf.PdfPCell;
|
||||
import com.itextpdf.text.pdf.PdfPTable;
|
||||
import com.itextpdf.text.pdf.PdfWriter;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* ExportPdfService
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
public class ExportPdfService extends DatabaseExportService {
|
||||
|
||||
public ExportPdfService() {
|
||||
exportTypeEnum = ExportTypeEnum.PDF;
|
||||
suffix = ExportFileSuffix.PDF.getSuffix();
|
||||
contentType = "application/pdf";
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void export(OutputStream outputStream, ExportOptions exportOptions) {
|
||||
boolean isExportIndex = exportOptions.getIsExportIndex();
|
||||
Map<String, List<Map.Entry<String, List<TableParameter>>>> allMap = listMap.entrySet()
|
||||
.stream().collect(Collectors.groupingBy(v -> v.getKey().split("---")[0]));
|
||||
Document document = new Document();
|
||||
PdfWriter pdfWriter = PdfWriter.getInstance(document, outputStream);
|
||||
pdfWriter.setStrictImageSequence(true);
|
||||
// 字体设置
|
||||
BaseFont baseFont =BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
|
||||
// 创建字体对象
|
||||
Font font = new Font(baseFont, 10, Font.NORMAL);
|
||||
Font headFont = new Font(baseFont, 12, Font.NORMAL);
|
||||
Font titleFont = new Font(baseFont, 14, Font.BOLD);
|
||||
document.open();
|
||||
//遍历数据
|
||||
for (Map.Entry<String, List<Map.Entry<String, List<TableParameter>>>> myMap : allMap.entrySet()) {
|
||||
//数据库名
|
||||
String database = myMap.getKey();
|
||||
String title = I18nUtils.getMessage("main.databaseText") + database;
|
||||
Paragraph p = new Paragraph(title, titleFont);
|
||||
document.add(p);
|
||||
for (Map.Entry<String, List<TableParameter>> parameterMap : myMap.getValue()) {
|
||||
//表名
|
||||
String tableName = parameterMap.getKey().split("---")[1];
|
||||
Paragraph tableParagraph = new Paragraph(tableName, font);
|
||||
document.add(tableParagraph);
|
||||
//索引Table
|
||||
if (isExportIndex && !indexMap.isEmpty()) {
|
||||
PdfPTable table = new PdfPTable(CommonConstant.INDEX_HEAD_NAMES.length);
|
||||
process(table, CommonConstant.INDEX_HEAD_NAMES, font);
|
||||
String name = parameterMap.getKey().split("\\[")[0];
|
||||
List<IndexInfo> indexInfoVOList = indexMap.get(name);
|
||||
for (IndexInfo indexInfo : indexInfoVOList) {
|
||||
process(table, getIndexValues(indexInfo), font);
|
||||
}
|
||||
table.setPaddingTop(5);
|
||||
document.add(table);
|
||||
}
|
||||
document.add(new Paragraph());
|
||||
//字段Table
|
||||
List<TableParameter> exportList = parameterMap.getValue();
|
||||
PdfPTable table = new PdfPTable(CommonConstant.COLUMN_HEAD_NAMES.length);
|
||||
//标题、内容
|
||||
process(table, CommonConstant.COLUMN_HEAD_NAMES, headFont);
|
||||
for (TableParameter tableParameter : exportList) {
|
||||
process(table, getColumnValues(tableParameter), font);
|
||||
}
|
||||
// 设置表格上方的空白间距,即向下移动的效果
|
||||
table.setSpacingBefore(10f);
|
||||
table.setSpacingAfter(20f);
|
||||
//居左对齐
|
||||
table.setHorizontalAlignment(PdfPTable.ALIGN_LEFT);
|
||||
document.add(table);
|
||||
//分页
|
||||
//document.newPage();
|
||||
}
|
||||
}
|
||||
document.close();
|
||||
}
|
||||
|
||||
//设置表格内容
|
||||
public static <T> void process(PdfPTable table, T[] line, Font font) {
|
||||
for (T s : line) {
|
||||
if (Objects.isNull(s)) {
|
||||
return;
|
||||
}
|
||||
PdfPCell cell = new PdfPCell(new Paragraph(s.toString(), font));
|
||||
cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);
|
||||
cell.setVerticalAlignment(PdfPCell.ALIGN_CENTER);
|
||||
cell.setPaddingTop(5);
|
||||
cell.setPaddingBottom(5);
|
||||
table.addCell(cell);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.doc.export;
|
||||
|
||||
import ai.chat2db.server.domain.api.enums.ExportFileSuffix;
|
||||
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
|
||||
import ai.chat2db.server.domain.api.model.IndexInfo;
|
||||
import ai.chat2db.server.domain.api.model.TableParameter;
|
||||
import ai.chat2db.server.tools.common.config.GlobalDict;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.constant.CommonConstant;
|
||||
import ai.chat2db.server.web.api.util.AddToTopic;
|
||||
import com.deepoove.poi.XWPFTemplate;
|
||||
import com.deepoove.poi.data.Includes;
|
||||
import com.deepoove.poi.data.RowRenderData;
|
||||
import com.deepoove.poi.data.Rows;
|
||||
import com.deepoove.poi.data.Tables;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* WordSuperActionListener
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
public class ExportWordSuperService extends DatabaseExportService {
|
||||
|
||||
public ExportWordSuperService() {
|
||||
exportTypeEnum = ExportTypeEnum.WORD;
|
||||
suffix = ExportFileSuffix.WORD.getSuffix();
|
||||
contentType = "application/msword";
|
||||
}
|
||||
|
||||
/**
|
||||
* Word导出
|
||||
**/
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void export(OutputStream outputStream, ExportOptions exportOptions) {
|
||||
boolean isExportIndex = exportOptions.getIsExportIndex();
|
||||
String filePath = GlobalDict.templateDir + GlobalDict.TEMPLATE_FILE.get(1);
|
||||
String subFile = GlobalDict.templateDir + GlobalDict.TEMPLATE_FILE.get(2);
|
||||
File importWordFile = new File(filePath);
|
||||
Map<String, List<Map.Entry<String, List<TableParameter>>>> allMap = listMap.entrySet()
|
||||
.stream().collect(Collectors.groupingBy(v -> v.getKey().split("---")[0]));
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
Map<String, Object> myDataMap = new HashMap<>(2);
|
||||
//索引表头
|
||||
RowRenderData indexHeaderRow = Rows.of(CommonConstant.INDEX_HEAD_NAMES).center().textBold().textColor("000000").bgColor("bfbfbf").create();
|
||||
//字段表头
|
||||
RowRenderData tableHeaderRow = Rows.of(CommonConstant.COLUMN_HEAD_NAMES).center().textBold().textColor("000000").bgColor("bfbfbf").create();
|
||||
for (Map.Entry<String, List<Map.Entry<String, List<TableParameter>>>> myMap : allMap.entrySet()) {
|
||||
//数据库名
|
||||
String database = myMap.getKey();
|
||||
int i = 1;
|
||||
for (Map.Entry<String, List<TableParameter>> parameterMap : myMap.getValue()) {
|
||||
//初始化容量 3/0.75 + 1
|
||||
Map<String, Object> tableData = new HashMap<>(8);
|
||||
//索引Table
|
||||
if (isExportIndex) {
|
||||
String name = parameterMap.getKey().split("\\[")[0];
|
||||
List<IndexInfo> indexInfoVOList = indexMap.get(name);
|
||||
List<RowRenderData> rowList = getIndexValues(indexInfoVOList, indexHeaderRow);
|
||||
tableData.put("indexTable", Tables.create(rowList.toArray(new RowRenderData[0])));
|
||||
}
|
||||
if (i == 1) {
|
||||
Map<String, String> map = new HashMap<>(2);
|
||||
map.put("dataBase", database);
|
||||
tableData.put("ifDatabase", map);
|
||||
}
|
||||
//表名
|
||||
String tableName = parameterMap.getKey().split("---")[1];
|
||||
tableData.put("number", i);
|
||||
tableData.put("name", tableName);
|
||||
List<TableParameter> tableParameterList = parameterMap.getValue();
|
||||
List<RowRenderData> rowList = getColumnValues(tableParameterList, tableHeaderRow);
|
||||
tableData.put("table", Tables.create(rowList.toArray(new RowRenderData[0])));
|
||||
i++;
|
||||
list.add(tableData);
|
||||
}
|
||||
}
|
||||
myDataMap.put("mydata", Includes.ofLocal(subFile).setRenderModel(list).create());
|
||||
/*根据模板生成文档*/
|
||||
XWPFTemplate template = XWPFTemplate.compile(importWordFile).render(myDataMap);
|
||||
AddToTopic.generateTOC(template.getXWPFDocument(), outputStream);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public List<RowRenderData> getColumnValues(List<TableParameter> list, RowRenderData tableHeaderRow) {
|
||||
List<RowRenderData> rowRenderDataList = new ArrayList<>();
|
||||
rowRenderDataList.add(tableHeaderRow);
|
||||
for (TableParameter tableParameter : list) {
|
||||
String[] values = Arrays.stream(getColumnValues(tableParameter)).toArray(String[]::new);
|
||||
rowRenderDataList.add(Rows.of(values).center().create());
|
||||
}
|
||||
return rowRenderDataList;
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
public List<RowRenderData> getIndexValues(List<IndexInfo> list, RowRenderData tableHeaderRow) {
|
||||
List<RowRenderData> rowRenderDataList = new ArrayList<>();
|
||||
rowRenderDataList.add(tableHeaderRow);
|
||||
if (list.isEmpty()) {
|
||||
String[] values = Arrays.stream(getIndexValues(new IndexInfo())).toArray(String[]::new);
|
||||
rowRenderDataList.add(Rows.of(values).center().create());
|
||||
return rowRenderDataList;
|
||||
}
|
||||
for (IndexInfo indexInfo : list) {
|
||||
String[] values = Arrays.stream(getIndexValues(indexInfo)).toArray(String[]::new);
|
||||
rowRenderDataList.add(Rows.of(values).center().create());
|
||||
}
|
||||
return rowRenderDataList;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.doc.merge;
|
||||
|
||||
import com.alibaba.excel.metadata.Head;
|
||||
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
|
||||
/**
|
||||
* MyMergeExcel
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
public class MyMergeExcel extends AbstractMergeStrategy {
|
||||
|
||||
public static final String NAME = "isTableNameBlank";
|
||||
|
||||
@Override
|
||||
protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
|
||||
if (NAME.equals(cell.getStringCellValue())) {
|
||||
CellRangeAddress region = new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex(), 0, 7);
|
||||
sheet.addMergedRegion(region);
|
||||
Row row = sheet.getRow(cell.getRowIndex());
|
||||
cell = row.getCell(0);
|
||||
Workbook workbook = sheet.getWorkbook();
|
||||
// 生成一个样式
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
// 设置这些样式
|
||||
style.setBorderBottom(BorderStyle.THIN);
|
||||
style.setBorderLeft(BorderStyle.THIN);
|
||||
style.setBorderRight(BorderStyle.THIN);
|
||||
style.setBorderTop(BorderStyle.THIN);
|
||||
style.setAlignment(HorizontalAlignment.CENTER);
|
||||
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
//设置填充方案
|
||||
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
||||
//设置自定义填充颜色
|
||||
style.setFillForegroundColor(IndexedColors.GREEN.getIndex());
|
||||
// 生成一个字体
|
||||
Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
font.setFontHeightInPoints((short) 14);
|
||||
// 把字体应用到当前的样式
|
||||
style.setFont(font);
|
||||
cell.setCellStyle(style);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.doc.style;
|
||||
|
||||
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
|
||||
import com.alibaba.excel.write.metadata.style.WriteFont;
|
||||
import org.apache.poi.ss.usermodel.BorderStyle;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||
import org.apache.poi.ss.usermodel.VerticalAlignment;
|
||||
|
||||
/**
|
||||
* CustomExcelStyle
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
public class CustomExcelStyle {
|
||||
public static WriteCellStyle getContentWriteCellStyle() {
|
||||
//内容样式策略
|
||||
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
|
||||
//垂直居中,水平居中
|
||||
contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
|
||||
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
|
||||
contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
|
||||
contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
|
||||
contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
|
||||
contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
|
||||
//设置 自动换行
|
||||
contentWriteCellStyle.setWrapped(true);
|
||||
// 字体策略
|
||||
WriteFont contentWriteFont = new WriteFont();
|
||||
// 字体大小
|
||||
contentWriteFont.setFontHeightInPoints((short) 11);
|
||||
contentWriteFont.setFontName("宋体");
|
||||
contentWriteFont.setColor(IndexedColors.BLACK.getIndex());
|
||||
contentWriteCellStyle.setWriteFont(contentWriteFont);
|
||||
return contentWriteCellStyle;
|
||||
}
|
||||
|
||||
public static WriteCellStyle getHeadStyle() {
|
||||
//头策略使用默认 设置字体大小
|
||||
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
|
||||
|
||||
headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
|
||||
headWriteCellStyle.setBorderLeft(BorderStyle.THIN);
|
||||
headWriteCellStyle.setBorderTop(BorderStyle.THIN);
|
||||
headWriteCellStyle.setBorderRight(BorderStyle.THIN);
|
||||
headWriteCellStyle.setBorderBottom(BorderStyle.THIN);
|
||||
|
||||
headWriteCellStyle.setWrapped(false);
|
||||
|
||||
WriteFont headWriteFont = new WriteFont();
|
||||
headWriteFont.setFontHeightInPoints((short) 14);
|
||||
headWriteFont.setBold(true);
|
||||
headWriteFont.setFontName("宋体");
|
||||
headWriteCellStyle.setWriteFont(headWriteFont);
|
||||
headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
|
||||
return headWriteCellStyle;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.factory;
|
||||
|
||||
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
|
||||
import ai.chat2db.server.web.api.controller.rdb.doc.export.*;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* ExportServiceFactory
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
public class ExportServiceFactory {
|
||||
|
||||
/**
|
||||
* Export实现类缓存池
|
||||
*/
|
||||
private static final Map<String, Class<?>> REPORT_POOL = new ConcurrentHashMap<>(8);
|
||||
|
||||
static {
|
||||
REPORT_POOL.put(ExportTypeEnum.EXCEL.name(), ExportExcelService.class);
|
||||
REPORT_POOL.put(ExportTypeEnum.WORD.name(), ExportWordSuperService.class);
|
||||
REPORT_POOL.put(ExportTypeEnum.MARKDOWN.name(), ExportMarkdownService.class);
|
||||
REPORT_POOL.put(ExportTypeEnum.HTML.name(), ExportHtmlService.class);
|
||||
REPORT_POOL.put(ExportTypeEnum.PDF.name(), ExportPdfService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对应接口
|
||||
*
|
||||
* @param type 报表类型
|
||||
* @return Class
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static Class<?> get(String type) {
|
||||
Class<?> dataResult = REPORT_POOL.get(type);
|
||||
if (dataResult == null) {
|
||||
throw new ClassNotFoundException("no ExportUI was found");
|
||||
} else {
|
||||
return dataResult;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package ai.chat2db.server.web.api.util;
|
||||
|
||||
import org.apache.poi.xwpf.usermodel.XWPFDocument;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFRun;
|
||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSimpleField;
|
||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* AddToTopic
|
||||
*
|
||||
* @author lzy
|
||||
**/
|
||||
public class AddToTopic {
|
||||
|
||||
public static void generateTOC(XWPFDocument document, OutputStream out) throws IOException {
|
||||
String findText = "目录哈哈";
|
||||
String replaceText = "";
|
||||
for (XWPFParagraph p : document.getParagraphs()) {
|
||||
for (XWPFRun r : p.getRuns()) {
|
||||
int pos = r.getTextPosition();
|
||||
String text = r.getText(pos);
|
||||
if (text != null && text.contains(findText)) {
|
||||
text = text.replace(findText, replaceText);
|
||||
r.setText(text, 0);
|
||||
addField(p);
|
||||
// addField(p, "TOC \\h");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
document.write(out);
|
||||
}
|
||||
|
||||
private static void addField(XWPFParagraph paragraph) {
|
||||
CTSimpleField ctSimpleField = paragraph.getCTP().addNewFldSimple();
|
||||
ctSimpleField.setInstr("TOC \\o \"1-3\" \\h \\z \\u");
|
||||
ctSimpleField.setDirty(STOnOff.TRUE);
|
||||
ctSimpleField.addNewR().addNewT().setStringValue("<>");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package ai.chat2db.server.web.api.util;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 添加字符串工具类,为了兼容JB的各种产品,尽量不要用第三方工具包
|
||||
*
|
||||
* @author lzy
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class StringUtils {
|
||||
|
||||
private static final String EMPTY_STR = "null";
|
||||
|
||||
/**
|
||||
* 首字母处理方法
|
||||
*/
|
||||
private static final BiFunction<String, Function<Integer, Integer>, String> FIRST_CHAR_HANDLER_FUN = (str, firstCharFun) -> {
|
||||
int strLen;
|
||||
if (str == null || (strLen = str.length()) == 0) {
|
||||
return str;
|
||||
}
|
||||
|
||||
final int firstCodepoint = str.codePointAt(0);
|
||||
final int newCodePoint = firstCharFun.apply(firstCodepoint);
|
||||
if (firstCodepoint == newCodePoint) {
|
||||
// already capitalized
|
||||
return str;
|
||||
}
|
||||
|
||||
// cannot be longer than the char array
|
||||
final int[] newCodePoints = new int[strLen];
|
||||
int outOffset = 0;
|
||||
// copy the first codepoint
|
||||
newCodePoints[outOffset++] = newCodePoint;
|
||||
for (int inOffset = Character.charCount(firstCodepoint); inOffset < strLen; ) {
|
||||
final int codepoint = str.codePointAt(inOffset);
|
||||
// copy the remaining ones
|
||||
newCodePoints[outOffset++] = codepoint;
|
||||
inOffset += Character.charCount(codepoint);
|
||||
}
|
||||
return new String(newCodePoints, 0, outOffset);
|
||||
};
|
||||
|
||||
public static String isNullOrEmpty(String str) {
|
||||
if (StringUtils.isEmpty(str) || EMPTY_STR.equals(str)) {
|
||||
return "";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public static String isNull(String str) {
|
||||
if (StringUtils.isEmpty(str) || EMPTY_STR.equals(str)) {
|
||||
return "--";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public static String isNullForHtml(String str) {
|
||||
if (StringUtils.isEmpty(str) || EMPTY_STR.equals(str)) {
|
||||
return "<br>";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是空字符串
|
||||
*
|
||||
* @param cs 字符串
|
||||
* @return 是否为空
|
||||
*/
|
||||
public static boolean isEmpty(final CharSequence cs) {
|
||||
return cs == null || cs.length() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 首字母大写方法
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return 首字母大写结果
|
||||
*/
|
||||
public static String capitalize(final String str) {
|
||||
return FIRST_CHAR_HANDLER_FUN.apply(str, Character::toTitleCase);
|
||||
}
|
||||
|
||||
/**
|
||||
* 首字母小写方法
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return 首字母小写结果
|
||||
*/
|
||||
public static String uncapitalize(final String str) {
|
||||
return FIRST_CHAR_HANDLER_FUN.apply(str, Character::toLowerCase);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user