|
@@ -0,0 +1,629 @@
|
|
|
|
|
+package com.hotent.enterpriseDeclare.controller.material;
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
|
|
+import com.hotent.base.annotation.ApiGroup;
|
|
|
|
|
+import com.hotent.base.constants.ApiGroupConsts;
|
|
|
|
|
+import com.hotent.base.util.StringUtil;
|
|
|
|
|
+import com.hotent.base.model.CommonResult;
|
|
|
|
|
+import com.hotent.enterpriseDeclare.manager.CostSurveyFdTemplateUploadDataManager;
|
|
|
|
|
+import com.hotent.enterpriseDeclare.model.CostSurveyFdTemplateUploadData;
|
|
|
|
|
+import com.hotent.project.manager.CostProjectTaskMaterialManager;
|
|
|
|
|
+import com.hotent.project.model.CostProjectTaskMaterial;
|
|
|
|
|
+import com.hotent.surveyinfo.dao.CostSurveyFdTemplateHeadersDao;
|
|
|
|
|
+import com.hotent.surveyinfo.dao.CostSurveyFdTemplateItemsDao;
|
|
|
|
|
+import com.hotent.surveyinfo.manager.CostSurveyFdTemplateManager;
|
|
|
|
|
+import com.hotent.surveyinfo.manager.CostSurveyFdTemplateVersionManager;
|
|
|
|
|
+import com.hotent.surveyinfo.model.CostSurveyFdTemplate;
|
|
|
|
|
+import com.hotent.surveyinfo.model.CostSurveyFdTemplateHeaders;
|
|
|
|
|
+import com.hotent.surveyinfo.model.CostSurveyFdTemplateItems;
|
|
|
|
|
+import com.hotent.surveyinfo.model.CostSurveyFdTemplateVersion;
|
|
|
|
|
+import io.swagger.annotations.Api;
|
|
|
|
|
+import io.swagger.annotations.ApiOperation;
|
|
|
|
|
+import io.swagger.annotations.ApiParam;
|
|
|
|
|
+import org.apache.poi.ss.usermodel.*;
|
|
|
|
|
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
|
|
|
|
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
+import org.springframework.web.bind.annotation.*;
|
|
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
+
|
|
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
|
|
+import java.io.InputStream;
|
|
|
|
|
+import java.net.URLEncoder;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 任务定制-财务表导出 前端控制器
|
|
|
|
|
+ *
|
|
|
|
|
+ * @company 山西清众科技股份有限公司
|
|
|
|
|
+ * @author 超级管理员
|
|
|
|
|
+ * @since 2025-01-27
|
|
|
|
|
+ */
|
|
|
|
|
+@RestController
|
|
|
|
|
+@RequestMapping("/api/fdTemplateExport/v1/")
|
|
|
|
|
+@Api(tags = "任务定制-财务表")
|
|
|
|
|
+@ApiGroup(group = {ApiGroupConsts.GROUP_COST})
|
|
|
|
|
+public class CostProjectTaskFdTemplateExportController {
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CostSurveyFdTemplateManager templateManager;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CostSurveyFdTemplateVersionManager versionManager;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CostSurveyFdTemplateHeadersDao headersDao;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CostSurveyFdTemplateItemsDao itemsDao;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CostSurveyFdTemplateUploadDataManager uploadDataManager;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CostProjectTaskMaterialManager costProjectTaskMaterialManager;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 导出财务表Excel
|
|
|
|
|
+ * @param surveyTemplateId 模板ID
|
|
|
|
|
+ * @param versionId 版本ID(可选,不传则使用当前版本)
|
|
|
|
|
+ * @param response HTTP响应对象
|
|
|
|
|
+ * @throws Exception 异常
|
|
|
|
|
+ */
|
|
|
|
|
+ @GetMapping(value = "/exportExcel")
|
|
|
|
|
+ @ApiOperation(value = "导出财务表Excel", httpMethod = "GET", notes = "根据模板ID导出财务表Excel,支持指定版本,不传版本ID则使用当前版本")
|
|
|
|
|
+ public void exportExcel(
|
|
|
|
|
+ @ApiParam(name = "surveyTemplateId", value = "模板ID", required = true)
|
|
|
|
|
+ @RequestParam(required = true) String surveyTemplateId,
|
|
|
|
|
+ @ApiParam(name = "versionId", value = "版本ID(可选,不传则使用当前版本)", required = false)
|
|
|
|
|
+ @RequestParam(required = false) String versionId,
|
|
|
|
|
+ HttpServletResponse response) throws Exception {
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 获取模板信息
|
|
|
|
|
+ CostSurveyFdTemplate template = templateManager.get(surveyTemplateId);
|
|
|
|
|
+ if (template == null) {
|
|
|
|
|
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未找到指定的财务模板");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 获取版本信息
|
|
|
|
|
+ CostSurveyFdTemplateVersion currentVersion = null;
|
|
|
|
|
+ if (StringUtil.isNotEmpty(versionId)) {
|
|
|
|
|
+ currentVersion = versionManager.get(versionId);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 获取当前启用版本
|
|
|
|
|
+ currentVersion = versionManager.selectCurrentVersion(surveyTemplateId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (currentVersion == null) {
|
|
|
|
|
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未找到指定的模板版本");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 获取表头信息(根据版本)
|
|
|
|
|
+ List<CostSurveyFdTemplateHeaders> headersList = headersDao.selectBySurveyTemplateIdAndVersion(
|
|
|
|
|
+ surveyTemplateId, currentVersion.getId());
|
|
|
|
|
+
|
|
|
|
|
+ // 过滤可见字段
|
|
|
|
|
+ headersList = headersList.stream()
|
|
|
|
|
+ .filter(header -> StringUtil.isEmpty(header.getShowVisible()) || "1".equals(header.getShowVisible()))
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ if (headersList == null || headersList.isEmpty()) {
|
|
|
|
|
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未找到表头信息");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 获取数据项信息(根据版本)
|
|
|
|
|
+ List<CostSurveyFdTemplateItems> itemsList = itemsDao.selectBySurveyTemplateIdAndVersion(
|
|
|
|
|
+ surveyTemplateId, currentVersion.getId());
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 按照orderNum排序表头
|
|
|
|
|
+ headersList.sort(Comparator.comparing(header -> {
|
|
|
|
|
+ String orderNum = header.getOrderNum();
|
|
|
|
|
+ if (StringUtil.isEmpty(orderNum)) {
|
|
|
|
|
+ return Integer.MAX_VALUE;
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ return Integer.parseInt(orderNum);
|
|
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
|
|
+ return Integer.MAX_VALUE;
|
|
|
|
|
+ }
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ // 6. 创建Excel工作簿
|
|
|
|
|
+ Workbook workbook = new XSSFWorkbook();
|
|
|
|
|
+ String sheetName = currentVersion.getSurveyTemplateName() != null ?
|
|
|
|
|
+ currentVersion.getSurveyTemplateName() :
|
|
|
|
|
+ (template.getSurveyTemplateName() != null ? template.getSurveyTemplateName() : "财务表");
|
|
|
|
|
+ Sheet sheet = workbook.createSheet(sheetName);
|
|
|
|
|
+
|
|
|
|
|
+ // 7. 创建表头行
|
|
|
|
|
+ Row headerRow = sheet.createRow(0);
|
|
|
|
|
+ for (int i = 0; i < headersList.size(); i++) {
|
|
|
|
|
+ CostSurveyFdTemplateHeaders header = headersList.get(i);
|
|
|
|
|
+ Cell cell = headerRow.createCell(i);
|
|
|
|
|
+ cell.setCellValue(header.getFieldName() != null ? header.getFieldName() : "");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 8. 根据模板类型处理数据并填充到Excel
|
|
|
|
|
+ String templateType = template.getTemplateType();
|
|
|
|
|
+ if (itemsList != null && !itemsList.isEmpty()) {
|
|
|
|
|
+ // 构建表头ID到索引的映射
|
|
|
|
|
+ Map<String, Integer> headerIndexMap = new HashMap<>();
|
|
|
|
|
+ for (int i = 0; i < headersList.size(); i++) {
|
|
|
|
|
+ headerIndexMap.put(headersList.get(i).getId(), i);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 根据模板类型采用不同的数据处理方式
|
|
|
|
|
+ switch (templateType != null ? templateType : "") {
|
|
|
|
|
+ case "1":
|
|
|
|
|
+ // 单表(单记录):所有数据在一行
|
|
|
|
|
+ exportSingleTable(sheet, itemsList, headerIndexMap);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case "2":
|
|
|
|
|
+ // 固定表:按rowid或orderNum分组,每个分组一行
|
|
|
|
|
+ exportFixedTable(sheet, itemsList, headerIndexMap);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case "3":
|
|
|
|
|
+ // 动态表:按rowid分组,支持树形结构
|
|
|
|
|
+ exportDynamicTable(sheet, itemsList, headerIndexMap);
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ // 未知类型,使用默认处理方式(按rowid分组)
|
|
|
|
|
+ throw new IllegalArgumentException("未知的模板类型:" + templateType);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 9. 自动调整列宽
|
|
|
|
|
+ for (int i = 0; i < headersList.size(); i++) {
|
|
|
|
|
+ sheet.autoSizeColumn(i);
|
|
|
|
|
+ // 设置最小列宽
|
|
|
|
|
+ sheet.setColumnWidth(i, Math.max(sheet.getColumnWidth(i), 3000));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 10. 设置响应头
|
|
|
|
|
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
|
|
|
|
+ response.setCharacterEncoding("utf-8");
|
|
|
|
|
+ String fileName = sheetName + "_" + System.currentTimeMillis() + ".xlsx";
|
|
|
|
|
+ response.setHeader("Content-Disposition", "attachment; filename=" +
|
|
|
|
|
+ URLEncoder.encode(fileName, "UTF-8"));
|
|
|
|
|
+
|
|
|
|
|
+ // 11. 写入响应输出流
|
|
|
|
|
+ workbook.write(response.getOutputStream());
|
|
|
|
|
+ workbook.close();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 导出单表(单记录):所有数据在一行
|
|
|
|
|
+ */
|
|
|
|
|
+ private void exportSingleTable(Sheet sheet, List<CostSurveyFdTemplateItems> itemsList,
|
|
|
|
|
+ Map<String, Integer> headerIndexMap) {
|
|
|
|
|
+ Row dataRow = sheet.createRow(1);
|
|
|
|
|
+ for (CostSurveyFdTemplateItems item : itemsList) {
|
|
|
|
|
+ Integer colIndex = headerIndexMap.get(item.getHeadersId());
|
|
|
|
|
+ if (colIndex != null) {
|
|
|
|
|
+ Cell cell = dataRow.createCell(colIndex);
|
|
|
|
|
+ String value = getItemValue(item);
|
|
|
|
|
+ cell.setCellValue(value);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 导出固定表:按rowid或orderNum分组,每个分组一行
|
|
|
|
|
+ */
|
|
|
|
|
+ private void exportFixedTable(Sheet sheet, List<CostSurveyFdTemplateItems> itemsList,
|
|
|
|
|
+ Map<String, Integer> headerIndexMap) {
|
|
|
|
|
+ // 优先按rowid分组
|
|
|
|
|
+ Map<String, List<CostSurveyFdTemplateItems>> itemsByRowId = itemsList.stream()
|
|
|
|
|
+ .filter(item -> StringUtil.isNotEmpty(item.getRowid()))
|
|
|
|
|
+ .collect(Collectors.groupingBy(CostSurveyFdTemplateItems::getRowid));
|
|
|
|
|
+
|
|
|
|
|
+ if (!itemsByRowId.isEmpty()) {
|
|
|
|
|
+ // 按rowid分组处理
|
|
|
|
|
+ int rowNum = 1;
|
|
|
|
|
+ // 按orderNum排序rowid(如果rowid是数字)
|
|
|
|
|
+ List<String> sortedRowIds = itemsByRowId.keySet().stream()
|
|
|
|
|
+ .sorted((id1, id2) -> {
|
|
|
|
|
+ try {
|
|
|
|
|
+ return Integer.compare(Integer.parseInt(id1), Integer.parseInt(id2));
|
|
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
|
|
+ return id1.compareTo(id2);
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ for (String rowId : sortedRowIds) {
|
|
|
|
|
+ Row dataRow = sheet.createRow(rowNum++);
|
|
|
|
|
+ List<CostSurveyFdTemplateItems> rowItems = itemsByRowId.get(rowId);
|
|
|
|
|
+
|
|
|
|
|
+ // 按orderNum排序该行的数据
|
|
|
|
|
+ rowItems.sort(Comparator.comparing(item -> {
|
|
|
|
|
+ Integer orderNum = item.getOrderNum();
|
|
|
|
|
+ return orderNum != null ? orderNum : Integer.MAX_VALUE;
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ // 填充该行的数据
|
|
|
|
|
+ fillRowData(sheet, dataRow, rowItems, headerIndexMap);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 如果没有rowid,按orderNum分组
|
|
|
|
|
+ Map<Integer, List<CostSurveyFdTemplateItems>> itemsByOrderNum = itemsList.stream()
|
|
|
|
|
+ .filter(item -> item.getOrderNum() != null)
|
|
|
|
|
+ .collect(Collectors.groupingBy(CostSurveyFdTemplateItems::getOrderNum));
|
|
|
|
|
+
|
|
|
|
|
+ if (!itemsByOrderNum.isEmpty()) {
|
|
|
|
|
+ int rowNum = 1;
|
|
|
|
|
+ List<Integer> sortedOrderNums = itemsByOrderNum.keySet().stream()
|
|
|
|
|
+ .sorted()
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ for (Integer orderNum : sortedOrderNums) {
|
|
|
|
|
+ Row dataRow = sheet.createRow(rowNum++);
|
|
|
|
|
+ List<CostSurveyFdTemplateItems> rowItems = itemsByOrderNum.get(orderNum);
|
|
|
|
|
+ fillRowData(sheet, dataRow, rowItems, headerIndexMap);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 如果既没有rowid也没有orderNum,按headersId分组(每个headersId一行)
|
|
|
|
|
+ Map<String, List<CostSurveyFdTemplateItems>> itemsByHeaderId = itemsList.stream()
|
|
|
|
|
+ .filter(item -> StringUtil.isNotEmpty(item.getHeadersId()))
|
|
|
|
|
+ .collect(Collectors.groupingBy(CostSurveyFdTemplateItems::getHeadersId));
|
|
|
|
|
+
|
|
|
|
|
+ int rowNum = 1;
|
|
|
|
|
+ for (Map.Entry<String, List<CostSurveyFdTemplateItems>> entry : itemsByHeaderId.entrySet()) {
|
|
|
|
|
+ Row dataRow = sheet.createRow(rowNum++);
|
|
|
|
|
+ List<CostSurveyFdTemplateItems> rowItems = entry.getValue();
|
|
|
|
|
+
|
|
|
|
|
+ // 填充该行的数据(取第一个数据值)
|
|
|
|
|
+ if (!rowItems.isEmpty()) {
|
|
|
|
|
+ CostSurveyFdTemplateItems item = rowItems.get(0);
|
|
|
|
|
+ Integer colIndex = headerIndexMap.get(item.getHeadersId());
|
|
|
|
|
+ if (colIndex != null) {
|
|
|
|
|
+ Cell cell = dataRow.createCell(colIndex);
|
|
|
|
|
+ String value = getItemValue(item);
|
|
|
|
|
+ cell.setCellValue(value);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 导出动态表:按rowid分组,支持树形结构
|
|
|
|
|
+ */
|
|
|
|
|
+ private void exportDynamicTable(Sheet sheet, List<CostSurveyFdTemplateItems> itemsList,
|
|
|
|
|
+ Map<String, Integer> headerIndexMap) {
|
|
|
|
|
+ Map<String, List<CostSurveyFdTemplateItems>> itemsByRowId = itemsList.stream()
|
|
|
|
|
+ .filter(item -> StringUtil.isNotEmpty(item.getRowid()))
|
|
|
|
|
+ .collect(Collectors.groupingBy(CostSurveyFdTemplateItems::getRowid));
|
|
|
|
|
+
|
|
|
|
|
+ if (!itemsByRowId.isEmpty()) {
|
|
|
|
|
+ int rowNum = 1;
|
|
|
|
|
+ // 按orderNum排序rowid
|
|
|
|
|
+ List<String> sortedRowIds = itemsByRowId.keySet().stream()
|
|
|
|
|
+ .sorted((id1, id2) -> {
|
|
|
|
|
+ try {
|
|
|
|
|
+ return Integer.compare(Integer.parseInt(id1), Integer.parseInt(id2));
|
|
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
|
|
+ return id1.compareTo(id2);
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ for (String rowId : sortedRowIds) {
|
|
|
|
|
+ Row dataRow = sheet.createRow(rowNum++);
|
|
|
|
|
+ List<CostSurveyFdTemplateItems> rowItems = itemsByRowId.get(rowId);
|
|
|
|
|
+
|
|
|
|
|
+ // 按orderNum排序该行的数据
|
|
|
|
|
+ rowItems.sort(Comparator.comparing(item -> {
|
|
|
|
|
+ Integer orderNum = item.getOrderNum();
|
|
|
|
|
+ return orderNum != null ? orderNum : Integer.MAX_VALUE;
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ fillRowData(sheet, dataRow, rowItems, headerIndexMap);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 动态表如果没有rowid,按orderNum分组
|
|
|
|
|
+ Map<Integer, List<CostSurveyFdTemplateItems>> itemsByOrderNum = itemsList.stream()
|
|
|
|
|
+ .filter(item -> item.getOrderNum() != null)
|
|
|
|
|
+ .collect(Collectors.groupingBy(CostSurveyFdTemplateItems::getOrderNum));
|
|
|
|
|
+
|
|
|
|
|
+ int rowNum = 1;
|
|
|
|
|
+ List<Integer> sortedOrderNums = itemsByOrderNum.keySet().stream()
|
|
|
|
|
+ .sorted()
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ for (Integer orderNum : sortedOrderNums) {
|
|
|
|
|
+ Row dataRow = sheet.createRow(rowNum++);
|
|
|
|
|
+ List<CostSurveyFdTemplateItems> rowItems = itemsByOrderNum.get(orderNum);
|
|
|
|
|
+ fillRowData(sheet, dataRow, rowItems, headerIndexMap);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 填充行数据
|
|
|
|
|
+ */
|
|
|
|
|
+ private void fillRowData(Sheet sheet, Row dataRow, List<CostSurveyFdTemplateItems> rowItems,
|
|
|
|
|
+ Map<String, Integer> headerIndexMap) {
|
|
|
|
|
+ for (CostSurveyFdTemplateItems item : rowItems) {
|
|
|
|
|
+ Integer colIndex = headerIndexMap.get(item.getHeadersId());
|
|
|
|
|
+ if (colIndex != null) {
|
|
|
|
|
+ Cell cell = dataRow.createCell(colIndex);
|
|
|
|
|
+ String value = getItemValue(item);
|
|
|
|
|
+ cell.setCellValue(value);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取数据项值(优先rvalue,其次jsonStr)
|
|
|
|
|
+ * @param item 数据项对象
|
|
|
|
|
+ * @return 数据值字符串
|
|
|
|
|
+ */
|
|
|
|
|
+ private String getItemValue(CostSurveyFdTemplateItems item) {
|
|
|
|
|
+ if (StringUtil.isNotEmpty(item.getRvalue())) {
|
|
|
|
|
+ return item.getRvalue();
|
|
|
|
|
+ } else if (StringUtil.isNotEmpty(item.getJsonStr())) {
|
|
|
|
|
+ return item.getJsonStr();
|
|
|
|
|
+ }
|
|
|
|
|
+ return "";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 导入财务表Excel数据
|
|
|
|
|
+ * @param file Excel文件
|
|
|
|
|
+ * @param surveyTemplateId 模板ID
|
|
|
|
|
+ * @param periodRecordId 监审期间记录ID(动态表使用,可选)
|
|
|
|
|
+ * @return 导入结果
|
|
|
|
|
+ * @throws Exception 异常
|
|
|
|
|
+ */
|
|
|
|
|
+ @PostMapping(value = "/importExcel")
|
|
|
|
|
+ @ApiOperation(value = "导入财务表Excel数据", httpMethod = "POST", notes = "导入Excel文件并存储数据到数据库")
|
|
|
|
|
+ public CommonResult<String> importExcel(
|
|
|
|
|
+ @ApiParam(name = "file", value = "Excel文件", required = true)
|
|
|
|
|
+ @RequestParam("file") MultipartFile file,
|
|
|
|
|
+ @ApiParam(name = "surveyTemplateId", value = "模板ID", required = true)
|
|
|
|
|
+ @RequestParam(required = true) String surveyTemplateId,
|
|
|
|
|
+ @ApiParam(name = "taskId", value = "任务ID", required = true)
|
|
|
|
|
+ @RequestParam(required = true) String taskId,
|
|
|
|
|
+ @ApiParam(name = "materialId", value = "材料ID", required = true)
|
|
|
|
|
+ @RequestParam(required = true) String materialId,
|
|
|
|
|
+ @ApiParam(name = "periodRecordId", value = "监审期间记录ID(动态表使用)", required = false)
|
|
|
|
|
+ @RequestParam(required = false) String periodRecordId) throws Exception {
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 验证文件
|
|
|
|
|
+ if (file == null || file.isEmpty()) {
|
|
|
|
|
+ return CommonResult.<String>error().message("上传文件不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String fileName = file.getOriginalFilename();
|
|
|
|
|
+ if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) {
|
|
|
|
|
+ return CommonResult.<String>error().message("文件格式不正确,请上传Excel文件(.xlsx或.xls)");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 获取模板信息
|
|
|
|
|
+ CostSurveyFdTemplate template = templateManager.get(surveyTemplateId);
|
|
|
|
|
+ if (template == null) {
|
|
|
|
|
+ return CommonResult.<String>error().message("未找到指定的财务模板");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 获取当前版本(用于获取表头和数据项)
|
|
|
|
|
+ CostSurveyFdTemplateVersion currentVersion = versionManager.selectCurrentVersion(surveyTemplateId);
|
|
|
|
|
+ if (currentVersion == null) {
|
|
|
|
|
+ return CommonResult.<String>error().message("未找到启用的模板版本");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 获取表头信息(根据版本)
|
|
|
|
|
+ List<CostSurveyFdTemplateHeaders> headersList = headersDao.selectBySurveyTemplateIdAndVersion(
|
|
|
|
|
+ surveyTemplateId, currentVersion.getId());
|
|
|
|
|
+
|
|
|
|
|
+ // 过滤可见字段
|
|
|
|
|
+ headersList = headersList.stream()
|
|
|
|
|
+ .filter(header -> StringUtil.isEmpty(header.getShowVisible()) || "1".equals(header.getShowVisible()))
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ if (headersList == null || headersList.isEmpty()) {
|
|
|
|
|
+ return CommonResult.<String>error().message("未找到表头信息");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 构建表头名称到表头信息的映射(包含ID和字段名)
|
|
|
|
|
+ Map<String, CostSurveyFdTemplateHeaders> headerNameToHeaderMap = new HashMap<>();
|
|
|
|
|
+ for (CostSurveyFdTemplateHeaders header : headersList) {
|
|
|
|
|
+ if (StringUtil.isNotEmpty(header.getFieldName())) {
|
|
|
|
|
+ headerNameToHeaderMap.put(header.getFieldName().trim(), header);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 6. 读取Excel文件
|
|
|
|
|
+ Workbook workbook = null;
|
|
|
|
|
+ try (InputStream inputStream = file.getInputStream()) {
|
|
|
|
|
+ if (fileName.endsWith(".xlsx")) {
|
|
|
|
|
+ workbook = new XSSFWorkbook(inputStream);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ workbook = new HSSFWorkbook(inputStream);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Sheet sheet = workbook.getSheetAt(0);
|
|
|
|
|
+ if (sheet == null || sheet.getPhysicalNumberOfRows() < 2) {
|
|
|
|
|
+ return CommonResult.<String>error().message("Excel文件没有数据或格式不正确");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 7. 读取表头行(第一行)
|
|
|
|
|
+ Row headerRow = sheet.getRow(0);
|
|
|
|
|
+ if (headerRow == null) {
|
|
|
|
|
+ return CommonResult.<String>error().message("Excel文件第一行为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 构建Excel列索引到表头信息的映射
|
|
|
|
|
+ Map<Integer, CostSurveyFdTemplateHeaders> columnIndexToHeaderMap = new HashMap<>();
|
|
|
|
|
+ for (int i = 0; i < headerRow.getLastCellNum(); i++) {
|
|
|
|
|
+ Cell cell = headerRow.getCell(i);
|
|
|
|
|
+ if (cell != null) {
|
|
|
|
|
+ String cellValue = getCellStringValue(cell);
|
|
|
|
|
+ if (StringUtil.isNotEmpty(cellValue)) {
|
|
|
|
|
+ CostSurveyFdTemplateHeaders header = headerNameToHeaderMap.get(cellValue.trim());
|
|
|
|
|
+ if (header != null) {
|
|
|
|
|
+ columnIndexToHeaderMap.put(i, header);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (columnIndexToHeaderMap.isEmpty()) {
|
|
|
|
|
+ return CommonResult.<String>error().message("Excel表头与模板表头不匹配");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 8. 读取数据行并转换为实体对象
|
|
|
|
|
+ String templateType = template.getTemplateType();
|
|
|
|
|
+ List<CostSurveyFdTemplateUploadData> dataList = new ArrayList<>();
|
|
|
|
|
+ int dataRowCount = 0;
|
|
|
|
|
+ String currentRowId = null;
|
|
|
|
|
+
|
|
|
|
|
+ for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
|
|
|
|
|
+ Row dataRow = sheet.getRow(rowIndex);
|
|
|
|
|
+ if (dataRow == null) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 判断是否为空行
|
|
|
|
|
+ boolean isEmptyRow = true;
|
|
|
|
|
+ for (int colIndex = 0; colIndex < dataRow.getLastCellNum(); colIndex++) {
|
|
|
|
|
+ Cell cell = dataRow.getCell(colIndex);
|
|
|
|
|
+ if (cell != null && StringUtil.isNotEmpty(getCellStringValue(cell))) {
|
|
|
|
|
+ isEmptyRow = false;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isEmptyRow) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 根据模板类型处理数据
|
|
|
|
|
+ if ("1".equals(templateType)) {
|
|
|
|
|
+ // 单表:所有数据在一行
|
|
|
|
|
+ currentRowId = "row_" + System.currentTimeMillis();
|
|
|
|
|
+ for (Map.Entry<Integer, CostSurveyFdTemplateHeaders> entry : columnIndexToHeaderMap.entrySet()) {
|
|
|
|
|
+ int colIndex = entry.getKey();
|
|
|
|
|
+ CostSurveyFdTemplateHeaders header = entry.getValue();
|
|
|
|
|
+ Cell cell = dataRow.getCell(colIndex);
|
|
|
|
|
+ String cellValue = getCellStringValue(cell);
|
|
|
|
|
+
|
|
|
|
|
+ if (StringUtil.isNotEmpty(cellValue)) {
|
|
|
|
|
+ CostSurveyFdTemplateUploadData uploadData = createUploadData(
|
|
|
|
|
+ surveyTemplateId,taskId,materialId, currentRowId,
|
|
|
|
|
+ header, cellValue, periodRecordId);
|
|
|
|
|
+ dataList.add(uploadData);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ dataRowCount++;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 固定表和动态表:每行数据对应一个rowid
|
|
|
|
|
+ currentRowId = "row_" + rowIndex;
|
|
|
|
|
+ for (Map.Entry<Integer, CostSurveyFdTemplateHeaders> entry : columnIndexToHeaderMap.entrySet()) {
|
|
|
|
|
+ int colIndex = entry.getKey();
|
|
|
|
|
+ CostSurveyFdTemplateHeaders header = entry.getValue();
|
|
|
|
|
+ Cell cell = dataRow.getCell(colIndex);
|
|
|
|
|
+ String cellValue = getCellStringValue(cell);
|
|
|
|
|
+
|
|
|
|
|
+ if (StringUtil.isNotEmpty(cellValue)) {
|
|
|
|
|
+ CostSurveyFdTemplateUploadData uploadData = createUploadData(
|
|
|
|
|
+ surveyTemplateId, taskId,materialId, currentRowId,
|
|
|
|
|
+ header, cellValue, periodRecordId);
|
|
|
|
|
+ dataList.add(uploadData);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ dataRowCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (dataList.isEmpty()) {
|
|
|
|
|
+ return CommonResult.<String>error().message("Excel文件中没有有效数据");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 8. 保存数据
|
|
|
|
|
+ uploadDataManager.saveData(dataList);
|
|
|
|
|
+ CostProjectTaskMaterial manager = costProjectTaskMaterialManager.getById(materialId);
|
|
|
|
|
+ manager.setId(materialId);
|
|
|
|
|
+ manager.setIsUpload("1");
|
|
|
|
|
+ costProjectTaskMaterialManager.updateById(manager);
|
|
|
|
|
+ return CommonResult.<String>ok().message("导入成功,共导入 " + dataRowCount + " 行数据");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ return CommonResult.<String>error().message("导入失败:" + e.getMessage());
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ if (workbook != null) {
|
|
|
|
|
+ workbook.close();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建上传数据对象
|
|
|
|
|
+ */
|
|
|
|
|
+ private CostSurveyFdTemplateUploadData createUploadData(String surveyTemplateId,
|
|
|
|
|
+ String taskId, String materialId,
|
|
|
|
|
+ String rowid, CostSurveyFdTemplateHeaders header,
|
|
|
|
|
+ String value, String periodRecordId) {
|
|
|
|
|
+ CostSurveyFdTemplateUploadData uploadData = new CostSurveyFdTemplateUploadData();
|
|
|
|
|
+ uploadData.setSurveyTemplateId(surveyTemplateId);
|
|
|
|
|
+ uploadData.setMaterialId(materialId);
|
|
|
|
|
+ uploadData.setTaskId(taskId);
|
|
|
|
|
+ uploadData.setRowid(rowid);
|
|
|
|
|
+ String rkey = StringUtil.isNotEmpty(header.getFieldEname()) ?
|
|
|
|
|
+ header.getFieldEname() : header.getFieldName();
|
|
|
|
|
+ uploadData.setRkey(rkey);
|
|
|
|
|
+ uploadData.setRvalue(value);
|
|
|
|
|
+ if (StringUtil.isNotEmpty(periodRecordId)) {
|
|
|
|
|
+ uploadData.setPeriodRecordId(periodRecordId);
|
|
|
|
|
+ }
|
|
|
|
|
+ uploadData.setIsDeleted("0");
|
|
|
|
|
+ return uploadData;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取单元格的字符串值
|
|
|
|
|
+ */
|
|
|
|
|
+ private String getCellStringValue(Cell cell) {
|
|
|
|
|
+ if (cell == null) {
|
|
|
|
|
+ return "";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ switch (cell.getCellType()) {
|
|
|
|
|
+ case STRING:
|
|
|
|
|
+ return cell.getStringCellValue();
|
|
|
|
|
+ case NUMERIC:
|
|
|
|
|
+ if (DateUtil.isCellDateFormatted(cell)) {
|
|
|
|
|
+ return cell.getDateCellValue().toString();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 处理数字,避免科学计数法
|
|
|
|
|
+ double numericValue = cell.getNumericCellValue();
|
|
|
|
|
+ if (numericValue == (long) numericValue) {
|
|
|
|
|
+ return String.valueOf((long) numericValue);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return String.valueOf(numericValue);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ case BOOLEAN:
|
|
|
|
|
+ return String.valueOf(cell.getBooleanCellValue());
|
|
|
|
|
+ case FORMULA:
|
|
|
|
|
+ try {
|
|
|
|
|
+ return cell.getStringCellValue();
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ return String.valueOf(cell.getNumericCellValue());
|
|
|
|
|
+ } catch (Exception ex) {
|
|
|
|
|
+ return "";
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ default:
|
|
|
|
|
+ return "";
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|