|
|
@@ -1,12 +1,5 @@
|
|
|
package org.springblade.manager.controller;
|
|
|
-
|
|
|
-import com.alibaba.fastjson.JSONObject;
|
|
|
-import com.aspose.cells.LoadFormat;
|
|
|
-import com.aspose.cells.LoadOptions;
|
|
|
-import com.aspose.cells.SaveFormat;
|
|
|
import com.aspose.cells.Workbook;
|
|
|
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
|
|
import com.google.common.collect.Lists;
|
|
|
@@ -20,10 +13,8 @@ import io.swagger.annotations.ApiOperation;
|
|
|
import lombok.AllArgsConstructor;
|
|
|
import lombok.SneakyThrows;
|
|
|
import org.apache.commons.lang.StringUtils;
|
|
|
-import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
|
|
import org.apache.poi.ss.usermodel.*;
|
|
|
import org.apache.poi.ss.util.CellRangeAddress;
|
|
|
-import org.apache.poi.ss.util.WorkbookUtil;
|
|
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
|
|
import org.jsoup.Jsoup;
|
|
|
import org.jsoup.nodes.Document;
|
|
|
@@ -31,8 +22,6 @@ import org.jsoup.nodes.Element;
|
|
|
import org.jsoup.select.Elements;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
-import org.springblade.business.vo.NeiYeLedgerVO1;
|
|
|
-import org.springblade.common.utils.CommonUtil;
|
|
|
import org.springblade.common.utils.SnowFlakeUtil;
|
|
|
import org.springblade.core.boot.ctrl.BladeController;
|
|
|
import org.springblade.core.log.exception.ServiceException;
|
|
|
@@ -47,21 +36,14 @@ import org.springblade.manager.dto.TableSortDTO;
|
|
|
import org.springblade.manager.dto.WbsTreeContractDTO2;
|
|
|
import org.springblade.manager.entity.*;
|
|
|
import org.springblade.manager.feign.ContractClient;
|
|
|
-import org.springblade.manager.service.INodeBaseInfoService;
|
|
|
import org.springblade.manager.service.IWbsParamService;
|
|
|
import org.springblade.manager.service.IWbsTreeContractService;
|
|
|
-import org.springblade.manager.service.IWbsTreePrivateService;
|
|
|
-import org.springblade.manager.service.impl.NodeBaseInfoServiceImpl;
|
|
|
import org.springblade.manager.service.impl.WbsTreeContractServiceImpl;
|
|
|
import org.springblade.manager.utils.FileUtils;
|
|
|
import org.springblade.manager.utils.RandomNumberHolder;
|
|
|
import org.springblade.manager.vo.*;
|
|
|
-import org.springframework.beans.factory.annotation.Autowired;
|
|
|
-import org.springframework.core.io.ByteArrayResource;
|
|
|
import org.springframework.core.io.Resource;
|
|
|
import org.springframework.dao.DataAccessException;
|
|
|
-import org.springframework.http.HttpHeaders;
|
|
|
-import org.springframework.http.MediaType;
|
|
|
import org.springframework.http.ResponseEntity;
|
|
|
import org.springframework.jdbc.core.BeanPropertyRowMapper;
|
|
|
import org.springframework.jdbc.core.JdbcTemplate;
|
|
|
@@ -84,7 +66,20 @@ import java.util.*;
|
|
|
import java.util.regex.Matcher;
|
|
|
import java.util.regex.Pattern;
|
|
|
import java.util.stream.Collectors;
|
|
|
-import org.springframework.http.ContentDisposition;
|
|
|
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+import static com.aspose.cells.HtmlLinkTargetType.BLANK;
|
|
|
+import static com.aspose.cells.LoadDataFilterOptions.FORMULA;
|
|
|
+import static com.aspose.cells.PropertyType.BOOLEAN;
|
|
|
+import static com.aspose.cells.PropertyType.STRING;
|
|
|
+import static java.sql.Types.NUMERIC;
|
|
|
+
|
|
|
|
|
|
@RestController
|
|
|
@AllArgsConstructor
|
|
|
@@ -429,8 +424,187 @@ public class WbsTreeContractController extends BladeController {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ @SneakyThrows
|
|
|
+ @GetMapping("/download-node-excel")
|
|
|
+ @ApiOperationSupport(order = 14)
|
|
|
+ @ApiOperation(value = "客户端-下载节点下所有表单为多sheet excel", notes = "传入节点ID和分类")
|
|
|
+ public void downloadNodeExcel(@RequestParam Long nodeId, @RequestParam Integer classify, HttpServletResponse response) {
|
|
|
+ // 构建Excel文件名
|
|
|
+ StringBuilder excelName = new StringBuilder();
|
|
|
+ // 获取节点下所有表单
|
|
|
+ List<WbsTreeContract> formList = wbsTreeContractServiceImpl.selectAllPkeyIdByNodeId(nodeId, classify);
|
|
|
+ if (ObjectUtil.isEmpty(formList)) {
|
|
|
+ throw new ServiceException("该节点下没有找到对应的表单数据");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取节点及祖先节点信息用于构建文件名
|
|
|
+ WbsTreeContract node = wbsTreeContractServiceImpl.getById(nodeId);
|
|
|
+ List<WbsTreeContract> ancestorsList = wbsTreeContractServiceImpl.getAncestorsList(node.getAncestorsPId());
|
|
|
+ for (WbsTreeContract ancestor : ancestorsList) {
|
|
|
+ if (2 == ancestor.getNodeType()) {
|
|
|
+ excelName.append(ancestor.getNodeName());
|
|
|
+ } else if (4 == ancestor.getNodeType()) {
|
|
|
+ excelName.append("-" + ancestor.getNodeName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ excelName.append("-" + node.getNodeName());
|
|
|
+
|
|
|
+ // 创建主工作簿(用于合并多sheet)
|
|
|
+ XSSFWorkbook mainWorkbook = new XSSFWorkbook();
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 遍历所有表单,生成对应的sheet
|
|
|
+ for (WbsTreeContract form : formList) {
|
|
|
+ String pKeyId = form.getPKeyId()+"";
|
|
|
+ String sheetName = form.getNodeName();
|
|
|
+ // 处理sheet名称中的特殊字符(Excel不允许的字符)
|
|
|
+ sheetName = sheetName.replaceAll("[\\\\/:*?\"<>|]", "_");
|
|
|
+
|
|
|
+ // 1. 获取当前表单的htmlUrl(复用downloadExcel的逻辑)
|
|
|
+ String htmlUrl = "";
|
|
|
+ WbsTreeContract contractTab = iWbsTreeContractService.getBaseMapper()
|
|
|
+ .selectOne(Wrappers.<WbsTreeContract>lambdaQuery().eq(WbsTreeContract::getPKeyId, pKeyId));
|
|
|
+
|
|
|
+ if (ObjectUtil.isEmpty(contractTab)) {
|
|
|
+ // 尝试从试验表获取
|
|
|
+ WbsTreePrivate privateTab = jdbcTemplate.query(
|
|
|
+ "select * from m_wbs_tree_private where p_key_id = ?",
|
|
|
+ new BeanPropertyRowMapper<>(WbsTreePrivate.class),
|
|
|
+ pKeyId)
|
|
|
+ .stream().findAny().orElse(null);
|
|
|
+ if (privateTab != null && privateTab.getHtmlUrl() != null) {
|
|
|
+ htmlUrl = privateTab.getHtmlUrl();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ htmlUrl = contractTab.getHtmlUrl();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 跳过无html信息的表单
|
|
|
+ if (ObjectUtil.isEmpty(htmlUrl)) {
|
|
|
+ logger.warn("表单pKeyId:{} 未获取到html信息,已跳过", pKeyId);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 转换html为单个sheet的工作簿
|
|
|
+ InputStream htmlStream = FileUtils.getInputStreamByUrl(htmlUrl);
|
|
|
+ String htmlContent = IoUtil.readToString(htmlStream);
|
|
|
+ org.apache.poi.ss.usermodel.Workbook singleSheetWorkbook = HtmlTableToExcelConverter.convertHtmlTableToExcel(htmlContent);
|
|
|
|
|
|
+ // 3. 将单个sheet复制到主工作簿
|
|
|
+ if (singleSheetWorkbook.getNumberOfSheets() > 0) {
|
|
|
+ Sheet sourceSheet = singleSheetWorkbook.getSheetAt(0);
|
|
|
+ Sheet targetSheet = mainWorkbook.createSheet(sheetName);
|
|
|
+ copySheetContent(mainWorkbook, sourceSheet, targetSheet);
|
|
|
+ }
|
|
|
|
|
|
+ // 关闭临时工作簿释放资源
|
|
|
+ singleSheetWorkbook.close();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 输出主工作簿到响应
|
|
|
+ if (mainWorkbook.getNumberOfSheets() == 0) {
|
|
|
+ throw new ServiceException("所有表单均无法生成有效Excel内容");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置响应头
|
|
|
+ String encodedFileName = URLEncoder.encode(excelName + ".xlsx", "UTF-8")
|
|
|
+ .replaceAll("\\+", " ")
|
|
|
+ .replaceAll("%2B", "+");
|
|
|
+ response.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName);
|
|
|
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
|
|
+
|
|
|
+ // 写入输出流
|
|
|
+ try (ServletOutputStream outputStream = response.getOutputStream()) {
|
|
|
+ mainWorkbook.write(outputStream);
|
|
|
+ outputStream.flush(); // 强制刷出所有数据
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("下载节点多sheet Excel异常", e);
|
|
|
+ throw new ServiceException("下载失败:" + e.getMessage());
|
|
|
+ } finally {
|
|
|
+ if (mainWorkbook != null) {
|
|
|
+ mainWorkbook.close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 复制sheet内容(包括样式、合并区域、行高列宽)
|
|
|
+ */
|
|
|
+ private void copySheetContent(XSSFWorkbook targetWorkbook, Sheet sourceSheet, Sheet targetSheet) {
|
|
|
+ // 复制合并区域
|
|
|
+ for (int i = 0; i < sourceSheet.getNumMergedRegions(); i++) {
|
|
|
+ CellRangeAddress mergedRegion = sourceSheet.getMergedRegion(i);
|
|
|
+ targetSheet.addMergedRegion(mergedRegion);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 复制列宽
|
|
|
+ for (int col = 0; col <= sourceSheet.getLastRowNum(); col++) {
|
|
|
+ targetSheet.setColumnWidth(col, sourceSheet.getColumnWidth(col));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 复制行数据及样式
|
|
|
+ for (int rowIdx = 0; rowIdx <= sourceSheet.getLastRowNum(); rowIdx++) {
|
|
|
+ Row sourceRow = sourceSheet.getRow(rowIdx);
|
|
|
+ if (sourceRow == null) continue;
|
|
|
+
|
|
|
+ Row targetRow = targetSheet.createRow(rowIdx);
|
|
|
+ targetRow.setHeight(sourceRow.getHeight());
|
|
|
+
|
|
|
+ // 复制单元格
|
|
|
+ for (int cellIdx = 0; cellIdx < sourceRow.getLastCellNum(); cellIdx++) {
|
|
|
+ Cell sourceCell = sourceRow.getCell(cellIdx);
|
|
|
+ if (sourceCell == null) continue;
|
|
|
+
|
|
|
+ Cell targetCell = targetRow.createCell(cellIdx);
|
|
|
+ copyCellContent(targetWorkbook, sourceCell, targetCell);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void copyCellContent(XSSFWorkbook targetWorkbook, Cell sourceCell, Cell targetCell) {
|
|
|
+ // 复制样式
|
|
|
+ CellStyle targetStyle = targetWorkbook.createCellStyle();
|
|
|
+ targetStyle.cloneStyleFrom(sourceCell.getCellStyle());
|
|
|
+ targetCell.setCellStyle(targetStyle);
|
|
|
+
|
|
|
+ // 复制单元格值(完善类型处理)
|
|
|
+ CellType cellType = CellType.forInt(sourceCell.getCellType());
|
|
|
+ // 公式单元格需要先获取计算后的值
|
|
|
+ if (cellType == CellType.FORMULA) {
|
|
|
+ cellType = CellType.forInt(sourceCell.getCachedFormulaResultType());
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (cellType) {
|
|
|
+ case STRING:
|
|
|
+ targetCell.setCellValue(sourceCell.getStringCellValue());
|
|
|
+ break;
|
|
|
+ case NUMERIC:
|
|
|
+ if (DateUtil.isCellDateFormatted(sourceCell)) {
|
|
|
+ targetCell.setCellValue(sourceCell.getDateCellValue());
|
|
|
+ } else {
|
|
|
+ targetCell.setCellValue(sourceCell.getNumericCellValue());
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case BOOLEAN:
|
|
|
+ targetCell.setCellValue(sourceCell.getBooleanCellValue());
|
|
|
+ break;
|
|
|
+ case FORMULA:
|
|
|
+ // 公式单元格同时复制公式和缓存结果
|
|
|
+ targetCell.setCellFormula(sourceCell.getCellFormula());
|
|
|
+ targetCell.setCellValue(sourceCell.getNumericCellValue()); // 补充缓存值
|
|
|
+ break;
|
|
|
+ case BLANK:
|
|
|
+ break;
|
|
|
+ case ERROR:
|
|
|
+ targetCell.setCellErrorValue(sourceCell.getErrorCellValue());
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ // 兜底处理,避免遗漏
|
|
|
+ targetCell.setCellValue(sourceCell.toString());
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
|
|
|
// 计算最大列数以对齐所有行
|