diff --git a/.idea/libraries/Maven__cglib_cglib_3_1.xml b/.idea/libraries/Maven__cglib_cglib_3_1.xml new file mode 100644 index 000000000..bd3cddd6b --- /dev/null +++ b/.idea/libraries/Maven__cglib_cglib_3_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_alibaba_easyexcel_2_2_7.xml b/.idea/libraries/Maven__com_alibaba_easyexcel_2_2_7.xml new file mode 100644 index 000000000..a74963b4c --- /dev/null +++ b/.idea/libraries/Maven__com_alibaba_easyexcel_2_2_7.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_github_virtuald_curvesapi_1_04.xml b/.idea/libraries/Maven__com_github_virtuald_curvesapi_1_04.xml new file mode 100644 index 000000000..2db743118 --- /dev/null +++ b/.idea/libraries/Maven__com_github_virtuald_curvesapi_1_04.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_commons_commons_collections4_4_1.xml b/.idea/libraries/Maven__org_apache_commons_commons_collections4_4_1.xml new file mode 100644 index 000000000..1e0b53d1f --- /dev/null +++ b/.idea/libraries/Maven__org_apache_commons_commons_collections4_4_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_commons_commons_compress_1_21.xml b/.idea/libraries/Maven__org_apache_commons_commons_compress_1_21.xml new file mode 100644 index 000000000..49cd123ce --- /dev/null +++ b/.idea/libraries/Maven__org_apache_commons_commons_compress_1_21.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_poi_poi_3_17.xml b/.idea/libraries/Maven__org_apache_poi_poi_3_17.xml new file mode 100644 index 000000000..1a446fbf1 --- /dev/null +++ b/.idea/libraries/Maven__org_apache_poi_poi_3_17.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_poi_poi_ooxml_3_17.xml b/.idea/libraries/Maven__org_apache_poi_poi_ooxml_3_17.xml new file mode 100644 index 000000000..d50f385ea --- /dev/null +++ b/.idea/libraries/Maven__org_apache_poi_poi_ooxml_3_17.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_poi_poi_ooxml_schemas_3_17.xml b/.idea/libraries/Maven__org_apache_poi_poi_ooxml_schemas_3_17.xml new file mode 100644 index 000000000..d214d8b81 --- /dev/null +++ b/.idea/libraries/Maven__org_apache_poi_poi_ooxml_schemas_3_17.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_xmlbeans_xmlbeans_2_6_0.xml b/.idea/libraries/Maven__org_apache_xmlbeans_xmlbeans_2_6_0.xml new file mode 100644 index 000000000..cb0f76d6c --- /dev/null +++ b/.idea/libraries/Maven__org_apache_xmlbeans_xmlbeans_2_6_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_ehcache_ehcache_3_9_9.xml b/.idea/libraries/Maven__org_ehcache_ehcache_3_9_9.xml new file mode 100644 index 000000000..9ccdf70d9 --- /dev/null +++ b/.idea/libraries/Maven__org_ehcache_ehcache_3_9_9.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_ow2_asm_asm_4_2.xml b/.idea/libraries/Maven__org_ow2_asm_asm_4_2.xml new file mode 100644 index 000000000..b5e374897 --- /dev/null +++ b/.idea/libraries/Maven__org_ow2_asm_asm_4_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/emis-biz-web/emis-biz-web.iml b/emis-biz-web/emis-biz-web.iml index c5cd89ef1..5b3cf1c18 100644 --- a/emis-biz-web/emis-biz-web.iml +++ b/emis-biz-web/emis-biz-web.iml @@ -112,15 +112,6 @@ - - - - - - - - - @@ -160,7 +151,6 @@ - @@ -266,5 +256,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/emis-biz-web/src/main/java/com/xdadan/erp/web/emis/EmisSettleBillController.java b/emis-biz-web/src/main/java/com/xdadan/erp/web/emis/EmisSettleBillController.java index 73c8c0ba6..45a593497 100644 --- a/emis-biz-web/src/main/java/com/xdadan/erp/web/emis/EmisSettleBillController.java +++ b/emis-biz-web/src/main/java/com/xdadan/erp/web/emis/EmisSettleBillController.java @@ -11,13 +11,16 @@ package com.xdadan.erp.web.emis; +import java.io.IOException; import java.util.List; import javax.servlet.http.HttpServletResponse; import com.xdadan.erp.emis.domain.EmisSettleSubBill; +import com.xdadan.erp.emis.domain.EmisSettleSubBillFeeitem; import com.xdadan.erp.emis.domain.exception.EmisBizError; import com.xdadan.erp.emis.domain.vo.scan.ScanResult; import com.xdadan.erp.emis.service.EmisBaseService; +import com.xdadan.erp.emis.service.billexport.ExcelUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.util.CollectionUtils; @@ -189,6 +192,27 @@ public class EmisSettleBillController extends BaseController } + @Log(title = "导出账单", businessType = BusinessType.EXPORT) + @GetMapping(value = "/exportBill") + public void exportBill(HttpServletResponse response,String settleBillNo) + { +// return AjaxResult.success(); + + EmisSettleBill bill=emisBaseService.getSettleBillBySettleBillNo(settleBillNo); + List list = emisBaseService.selectSubBillListBySettleBillNo(settleBillNo); + + ExcelUtil util = new ExcelUtil(EmisSettleSubBill.class); +// util.exportExcel(response, list, "账单明细数据"); + try { + ExcelUtils.exportToTemplate(response, "tpl/settle_bill_tpl.xls", bill.getSettleBillName()+".xls", null, + null, null, + null); + } catch (IOException e) { + e.printStackTrace(); + } + + } + /** * 修改结算总账单 diff --git a/emis-biz/emis-biz.iml b/emis-biz/emis-biz.iml index a33e6214c..162fa7f70 100644 --- a/emis-biz/emis-biz.iml +++ b/emis-biz/emis-biz.iml @@ -65,15 +65,6 @@ - - - - - - - - - @@ -85,7 +76,6 @@ - @@ -115,7 +105,6 @@ - @@ -137,6 +126,7 @@ + @@ -225,5 +215,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/emis-biz/pom.xml b/emis-biz/pom.xml index 66d62d4ce..739d48f56 100644 --- a/emis-biz/pom.xml +++ b/emis-biz/pom.xml @@ -105,6 +105,26 @@ 1.1.14 + + + + com.alibaba + easyexcel + 2.2.7 + + + + + org.apache.poi + poi + 3.17 + + + org.apache.poi + poi-ooxml + 3.17 + + diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/service/EmisBaseService.java b/emis-biz/src/main/java/com/xdadan/erp/emis/service/EmisBaseService.java index 3e82011f7..81b295332 100644 --- a/emis-biz/src/main/java/com/xdadan/erp/emis/service/EmisBaseService.java +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/service/EmisBaseService.java @@ -1305,6 +1305,11 @@ public class EmisBaseService { return emisSettleSubBillMapper.setBillErrorBySite(emisSettleSubBill); } + + public List selectSubBillListBySettleBillNo(String settleBillNo){ + return emisSettleSubBillMapper.selectSubBillListBySettleBillNo(settleBillNo); + } + /** * 根据账单号获取总账单 * @param settleBillNo diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/DynamicDataMapping.java b/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/DynamicDataMapping.java new file mode 100644 index 000000000..71c5cea11 --- /dev/null +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/DynamicDataMapping.java @@ -0,0 +1,50 @@ +package com.xdadan.erp.emis.service.billexport; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +@Data +public class DynamicDataMapping { + + private String dataId; + private List> dataList; + + + /** + * @description: 组装只有一个list类型的动态数据 + * @param dataId + * @param dataList + */ + public static List createOneDataList(String dataId, List> dataList) { + if (dataList == null) + return null; + return Collections.singletonList(getDynamicDataMapping(dataId,dataList)); + } + + /** + * @description: 组装只有多个list类型的动态数据 + * @param transMap + */ + public static List createMorDataList(Map>> transMap) { + if (transMap == null) + return null; + List list = new ArrayList<>(); + transMap.forEach((dataId,dataList)->{ + list.add(getDynamicDataMapping(dataId,dataList)); + }); + return list; + } + + public static DynamicDataMapping getDynamicDataMapping(String dataId, List> dataList){ + DynamicDataMapping dynamicData = new DynamicDataMapping(); + dynamicData.dataId = dataId; + dynamicData.dataList = dataList; + return dynamicData; + } + +} + diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExcelFillCellMergePrevColUtils.java b/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExcelFillCellMergePrevColUtils.java new file mode 100644 index 000000000..ec86291eb --- /dev/null +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExcelFillCellMergePrevColUtils.java @@ -0,0 +1,70 @@ +package com.xdadan.erp.emis.service.billexport; + +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 列合并工具类 + * + * @author DaiHaijiao + */ +public class ExcelFillCellMergePrevColUtils implements CellWriteHandler { + private static final String KEY = "%s-%s"; + //所有的合并信息都存在了这个map里面 + Map mergeInfo = new HashMap<>(); + + public ExcelFillCellMergePrevColUtils() { + } + + @Override + public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) { + + } + + @Override + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) { + + } + + @Override + public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer integer, Boolean aBoolean) { + + } + + @Override + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List list, Cell cell, Head head, Integer integer, Boolean aBoolean) { + //当前行 + int curRowIndex = cell.getRowIndex(); + //当前列 + int curColIndex = cell.getColumnIndex(); + + Integer num = mergeInfo.get(String.format(KEY, curRowIndex, curColIndex)); + if (null != num) { + // 合并最后一行 ,列 + this.mergeWithPrevCol(writeSheetHolder, cell, curRowIndex, curColIndex, num); + } + } + + public void mergeWithPrevCol(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex, int num) { + Sheet sheet = writeSheetHolder.getSheet(); + CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex, curRowIndex, curColIndex, curColIndex + num); + sheet.addMergedRegion(cellRangeAddress); + } + + //num从第几列开始增加多少列,(6,2,7)代表的意思就是第6行的第2列至第2+7也就是9列开始合并 + public void add(int curRowIndex, int curColIndex, int num) { + mergeInfo.put(String.format(KEY, curRowIndex, curColIndex), num); + } + +} \ No newline at end of file diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExcelFillCellMergeStrategyUtils.java b/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExcelFillCellMergeStrategyUtils.java new file mode 100644 index 000000000..7480ed0b6 --- /dev/null +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExcelFillCellMergeStrategyUtils.java @@ -0,0 +1,110 @@ +package com.xdadan.erp.emis.service.billexport; + + +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; + +import java.util.List; + +/** + * 行合并工具类 + * + * @author DaiHaijiao + */ +public class ExcelFillCellMergeStrategyUtils implements CellWriteHandler { + + /** + * 合并字段的下标 + */ + private int[] mergeColumnIndex; + /** + * 合并几行 + */ + private int mergeRowIndex; + + public ExcelFillCellMergeStrategyUtils(int mergeRowIndex, int[] mergeColumnIndex) { + this.mergeRowIndex = mergeRowIndex; + this.mergeColumnIndex = mergeColumnIndex; + } + + @Override + public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Head head, Integer integer, Integer integer1, Boolean aBoolean) { + + } + + @Override + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer integer, Boolean aBoolean) { + + + } + + + @Override + public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + CellData cellData, Cell cell, Head head, Integer integer, Boolean aBoolean) { + + } + + @Override + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List list, Cell cell, Head head, Integer integer, Boolean aBoolean) { + //当前行 + int curRowIndex = cell.getRowIndex(); + //当前列 + int curColIndex = cell.getColumnIndex(); + + if (curRowIndex > mergeRowIndex) { + for (int i = 0; i < mergeColumnIndex.length; i++) { + if (curColIndex == mergeColumnIndex[i]) { + this.mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex); + break; + } + } + } + } + + private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) { + //获取当前行的当前列的数据和上一行的当前列列数据,通过上一行数据是否相同进行合并 + //获取当前行的第一列 + Cell firstNowCell = cell.getSheet().getRow(curRowIndex).getCell(curColIndex); + Object curData = firstNowCell.getCellTypeEnum() == CellType.STRING ? firstNowCell.getStringCellValue() : firstNowCell.getNumericCellValue(); + Row preRow = cell.getSheet().getRow(curRowIndex - 1); + if (preRow == null) { + // 当获取不到上一行数据时,使用缓存sheet中数据 + preRow = writeSheetHolder.getCachedSheet().getRow(curRowIndex - 1); + } + Cell preCell = preRow.getCell(curColIndex); + Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue(); + // 比较当前行的第一列的单元格与上一行是否相同,相同合并当前单元格与上一行 + if (curData.equals(preData)) { + Sheet sheet = writeSheetHolder.getSheet(); + List mergeRegions = sheet.getMergedRegions(); + boolean isMerged = false; + for (int i = 0; i < mergeRegions.size() && !isMerged; i++) { + CellRangeAddress cellRangeAddr = mergeRegions.get(i); + // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元 + if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) { + sheet.removeMergedRegion(i); + cellRangeAddr.setLastRow(curRowIndex); + sheet.addMergedRegion(cellRangeAddr); + isMerged = true; + } + } + // 若上一个单元格未被合并,则新增合并单元 + if (!isMerged) { + CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex); + sheet.addMergedRegion(cellRangeAddress); + } + } + } +} \ No newline at end of file diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExcelTemplateProc.java b/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExcelTemplateProc.java new file mode 100644 index 000000000..f6f8afad3 --- /dev/null +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExcelTemplateProc.java @@ -0,0 +1,251 @@ +package com.xdadan.erp.emis.service.billexport; + +import com.xdadan.erp.emis.service.billexport.DynamicDataMapping; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.ss.usermodel.CellCopyPolicy; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.util.ResourceUtils; + + +import java.io.*; +import java.util.List; +import java.util.Map; + +/** + * @Description 根据模版导出Excel程序 + */ +public class ExcelTemplateProc { + + /** + * @param templateFileName + * @param exportFilePathAndName + * @param staticDataMap + * @param dynamicDataMappingList + * @return void + * @description: 根据模版导出Excel入口 + * @author susu + * @date 2024/2/20 + */ + public static void doExportExcelByTemplateProc(String templateFileName, String exportFilePathAndName, + Map staticDataMap, + List dynamicDataMappingList) throws IOException { + /** + * 1. 从resources下加载模板并替换 + * 使用 ResourceUtils 加载文件 + */ + File file = ResourceUtils.getFile("classpath:"+templateFileName); + InputStream inputStream = new FileInputStream(file); + + Workbook workbook = dealFirstSheetByTemplate(inputStream, staticDataMap, dynamicDataMappingList); + // 2. 保存到本地 + saveExportFile(workbook, exportFilePathAndName); + } + + /** + * @param workbook + * @param excelFilePath + * @return void + * @description: 保存导出的Excel文件到服务器 + * @author susu + * @date 2024/2/20 + */ + public static void saveExportFile(Workbook workbook, String excelFilePath) throws IOException { + FileOutputStream outputStream = new FileOutputStream(excelFilePath); + executeWorkBookWrite(workbook, outputStream); + } + + /** + * @param workbook + * @param outputStream + * @return void + * @description: 数据输出 + * @author susu + * @date 2024/2/20 + */ + public static void executeWorkBookWrite(Workbook workbook, OutputStream outputStream) throws IOException { + workbook.write(outputStream); + outputStream.flush(); + outputStream.close(); + workbook.close(); + } + + /** + * @param inputStream + * @param staticDataMap + * @param dynamicDataMappingList + * @return org.apache.poi.ss.usermodel.Workbook + * @description: 处理只有一个sheet页的模版 + * @author susu + * @date 2024/2/20 + */ + public static Workbook dealFirstSheetByTemplate(InputStream inputStream, + Map staticDataMap, + List dynamicDataMappingList) throws IOException { + XSSFWorkbook workbook = new XSSFWorkbook(inputStream); + XSSFSheet sheet = workbook.getSheetAt(0); + // 按模板处理sheet页 + dealSheetDataByTemplate(sheet, staticDataMap, dynamicDataMappingList); + return workbook; + } + + /** + * @param sheet + * @param staticDataMap + * @param dynamicDataMappingList + * @return void + * @description: 按模板处理sheet页里的数据 + * @author susu + * @date 2024/2/19 + */ + private static void dealSheetDataByTemplate(XSSFSheet sheet, Map staticDataMap, List dynamicDataMappingList) { + // 循环sheet里每一行 + for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i++) { + XSSFRow row = sheet.getRow(i); + DynamicDataMapping dynamicDataMapping = getDynamicRowDataByMatch(row, dynamicDataMappingList); + if (dynamicDataMapping != null) { + i = getTemplateLastRowIndexAfterDealTemplate(sheet, i, dynamicDataMapping); + } else { + dealTemplateDataRow(row, null, staticDataMap); + } + } + } + + /** + * @param row + * @param dataMap + * @param dataPrefix + * @return void + * @description: 循环处理模版中每行的数据 + * @author susu + * @date 2024/2/20 + */ + private static void dealTemplateDataRow(XSSFRow row, String dataPrefix, Map dataMap) { + if (dataMap == null) { + return; + } + for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) { + XSSFCell cell = row.getCell(i); + fillInTemplateCellDataValue(cell, dataPrefix, dataMap); + } + } + + /** + * @param cell + * @param dataPrefix + * @param dataMap + * @return void + * @description: 填充模版里单元格的值 + * @author susu + * @date 2024/2/20 + */ + private static void fillInTemplateCellDataValue(XSSFCell cell, String dataPrefix, Map dataMap) { + if (cell == null) { + return; + } + String cellValue = cell.getStringCellValue();//获取模版里设置的数据 + if (StringUtils.isEmpty(cellValue)) { + return; + } + boolean flag = false; + dataPrefix = StringUtils.isEmpty(dataPrefix) ? "" : (dataPrefix + "."); + for (Map.Entry entry : dataMap.entrySet()) { + // 循环所有,因为可能一行有多个占位符 + String cellTemplateStr = "{{" + dataPrefix + entry.getKey() + "}}"; + if (cellValue.contains(cellTemplateStr)) { + // 替换模版中单元格的数据 + cellValue = cellValue.replace(cellTemplateStr, entry.getValue() == null ? "" : entry.getValue().toString()); + flag = true; + } + } + if (flag) { + cell.setCellValue(cellValue); + } + } + + /** + * @param row + * @param dynamicDataMappingList + * @return com.liu.susu.excel.template.poi.common.DynamicDataMapping + * @description: 通过模版sheet中的行数据 与 动态数据匹配,获取此行需要填充的动态数据 + * @author susu + * @date 2024/2/21 + */ + private static DynamicDataMapping getDynamicRowDataByMatch(XSSFRow row, List dynamicDataMappingList) { + if (dynamicDataMappingList == null || dynamicDataMappingList.size() < 1) { + return null; + } + for (int j = row.getFirstCellNum(); j < row.getLastCellNum(); j++) { + XSSFCell cell = row.getCell(j); + String value = cell.getStringCellValue(); + if (value != null) { + for (DynamicDataMapping dynamicData : dynamicDataMappingList) { + if (value.startsWith("{{" + dynamicData.getDataId() + ".")) { + return dynamicData; + } + } + } + } + return null; + } + + /** + * @param sheet + * @param rowIndex + * @param dynamicDataMapping + * @return int + * @description: 根据动态数据的条数动态复制模版行,每处理一个类型的list返回最后的行数,进而处理下一个类型的list + * @author susu + * @date 2024/2/20 + */ + private static int getTemplateLastRowIndexAfterDealTemplate(XSSFSheet sheet, int rowIndex, DynamicDataMapping dynamicDataMapping) { + if (dynamicDataMapping == null) { + return rowIndex; + } + int dataRows = dynamicDataMapping.getDataList().size(); + // 需要拷贝的行数(因为模板行本身占1行,所以-1) + int copyRows = dataRows - 1; + if (copyRows > 0) { + /** + * shiftRows: 从动态数据模版行(rowIndex)到最后一行,这些全部行都向下移copyRows行 + * 相当于模版行上面插入n行空行(n=copyRows) + */ + sheet.shiftRows(rowIndex, sheet.getLastRowNum(), copyRows, true, false); + // 拷贝策略 + CellCopyPolicy cellCopyPolicy = makeCellCopyPolicy(); + // 因为从模版行开始向下平移了copyRows行,所以这里 模板行=rowIndex + copyRows, + int templateDataRow = rowIndex + copyRows; + // 因为模版行上新增了空行,所以要把模板所在行的模版 拷贝到上面新增的空行 + for (int i = 0; i < copyRows; i++) { + //templateDataRow-模版行数据 rowIndex + i循环的当前空行 + sheet.copyRows(templateDataRow, templateDataRow, rowIndex + i, cellCopyPolicy); + } + } + // 循环模版行:动态替换模版行(将模版行里的模版替换成动态数据) + for (int j = rowIndex; j < rowIndex + dataRows; j++) { + Map dataMap = dynamicDataMapping.getDataList().get(j - rowIndex); + dealTemplateDataRow(sheet.getRow(j), dynamicDataMapping.getDataId(), dataMap); + } + return rowIndex + copyRows; + } + + /** + * @param + * @return org.apache.poi.ss.usermodel.CellCopyPolicy + * @description: 拷贝策略 + * @author susu + * @date 2024/2/20 + */ + public static CellCopyPolicy makeCellCopyPolicy() { + CellCopyPolicy cellCopyPolicy = new CellCopyPolicy(); + cellCopyPolicy.setCopyCellValue(true); + cellCopyPolicy.setCopyCellStyle(true); + return cellCopyPolicy; + } + + +} + diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExcelUtils.java b/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExcelUtils.java new file mode 100644 index 000000000..bba13454b --- /dev/null +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExcelUtils.java @@ -0,0 +1,157 @@ +package com.xdadan.erp.emis.service.billexport; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.io.ClassPathResource; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.*; + +/** + * excel导出工具类 + * + */ +public class ExcelUtils { + + /** + * 导出数据到指定Excel + * + * @param response HttpServletResponse对象 + * @param excelPath Excel模板地址 + * @param excelFileName 文件名称 + * @param outerMap 头部内容map + * @param innerMapList 表格内容list + * @param excelFillCellMergePrevColUtils 列合并参数 + * @param excelFillCellMergeStrategyUtils 行合并参数 + * @throws IOException 异常错误 + */ + public static void exportToTemplate(HttpServletResponse response, String excelPath, String excelFileName, Map outerMap, List> innerMapList, + ExcelFillCellMergePrevColUtils excelFillCellMergePrevColUtils, ExcelFillCellMergeStrategyUtils excelFillCellMergeStrategyUtils) throws IOException { + InputStream inputStream = new ClassPathResource(excelPath).getInputStream(); + response.setContentType("application/vnd.ms-excel"); + response.setCharacterEncoding("utf-8"); + String fileName = URLEncoder.encode(excelFileName, "UTF-8"); + response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"); + ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) + .withTemplate(inputStream) + .registerWriteHandler(excelFillCellMergePrevColUtils) + .registerWriteHandler(excelFillCellMergeStrategyUtils) + .build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + if (null != innerMapList && innerMapList.size() > 0) { + FillWrapper listWrapper = new FillWrapper("list", innerMapList); + excelWriter.fill(listWrapper, fillConfig, writeSheet); + } + if (null != outerMap && outerMap.size() > 0) { + excelWriter.fill(outerMap, writeSheet); + } + excelWriter.finish(); + } + + /** + * 验证并获取excel单元格内的值 + * + * @param columnData 列值,object类型 + * @param rowIndex 行号 + * @param columnIndex 列号 + * @param fieldName 字段名称 + * @param lengthLimit 限制长度数(null时不做判断) + * @param ifJudgeEmpty 是否需要判空(默认是) + * @return 字符串格式值 + * @throws Exception 逻辑异常 + */ + public static String checkValue(Object columnData, int rowIndex, int columnIndex, String fieldName, Integer lengthLimit, + Boolean ifJudgeEmpty) throws Exception { + String value = getStringValue(columnData); + ifJudgeEmpty = null == ifJudgeEmpty ? true : ifJudgeEmpty; + if (ifJudgeEmpty) { + //需要判空 + if (StringUtils.isEmpty(value)) { + throw new Exception("第" + (rowIndex + 1) + "行,第" + (columnIndex + 1) + "列," + fieldName + "不能为空"); + } + } + if (null != lengthLimit && lengthLimit > 0) { + //需要判断字符长度 + if (StringUtils.isNotEmpty(value)) { + if (value.length() > lengthLimit) { + throw new Exception("第" + (rowIndex + 1) + "行,第" + (columnIndex + 1) + "列," + fieldName + "不能超过" + lengthLimit + "个字符"); + } + } + } + return value; + } + + /** + * String => LocalDate + * 入参str和pattern格式需要对应 + * + * @param str + * @return LocalDate + */ + public static LocalDate str2LocalDate(String str) { + if (StringUtils.isEmpty(str)) { + return null; + } + if (str.indexOf("-") != -1 || str.indexOf("/") != -1) { + String pattern = str.indexOf("/") != -1 ? "yyyy/MM/dd" : "yyyy-MM-dd"; + try { + //测试日期字符串是否符合日期 + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern); + return LocalDate.parse(str, dateTimeFormatter); + } catch (Exception e) { + pattern = str.indexOf("/") != -1 ? "yyyy/M/d" : "yyyy-M-d"; + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern); + return LocalDate.parse(str, dateTimeFormatter); + } + } else { + Calendar calendar = new GregorianCalendar(1900, 0, -1); + Date date = calendar.getTime(); + int amount = Integer.parseInt(str); + if (amount > 0) { + Calendar calendar1 = Calendar.getInstance(); + calendar1.setTime(date); + calendar1.add(Calendar.DAY_OF_YEAR, amount); + date = calendar.getTime(); + } + return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } + } + + /** + * 获取String类型的值 + * + * @param columnData 列值,object类型 + * @return 字符串格式值 + */ + public static String getStringValue(Object columnData) { + if (columnData == null) { + return null; + } else { + String res = columnData.toString().replace("[\\t\\n\\r]", "").trim(); + return res; +// //判断是否是科学计数法 true是科学计数法,false不是科学计数法 +// boolean isMache=SCIENTIFIC_COUNTING_METHOD_PATTERN.matcher(res).matches(); +// if(isMache){ +// BigDecimal resDecimal = new BigDecimal(res); +// return resDecimal.toPlainString(); +// }else { +// return res; +// } + } + } + + public static void main(String[] args) { +// ExcelUtils.exportToTemplate(response, "excel/某某管理导入模板.xlsx", "某某管理导入模板", null, null, null, null); + } +} \ No newline at end of file diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExportExcelByTemplateUtils.java b/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExportExcelByTemplateUtils.java new file mode 100644 index 000000000..21d9a52b8 --- /dev/null +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/ExportExcelByTemplateUtils.java @@ -0,0 +1,65 @@ +package com.xdadan.erp.emis.service.billexport; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Description 根据模版导出Excel工具类 + * @Author susu + * @Date 2024/2/19 + */ +public class ExportExcelByTemplateUtils { + + /** + * @description: 根据模版导出Excel入口(单个list数据) + * @param templateFileName + * @param exportFilePathAndName + * @param staticDataMap + * @param dataId + * @param originDataList + * @return void + * @author susu + * @date 2024/2/21 + */ + public static void doExportExcelOneListByTemplate(String templateFileName, String exportFilePathAndName, + Map staticDataMap, + String dataId, + List originDataList) throws Exception{ + + List> exportDataList = MapObjectUtil.objListToMapList(originDataList); + // 只有一个list数据 + List dynamicDataMappingList = DynamicDataMapping.createOneDataList(dataId, exportDataList); + // 导出 + ExcelTemplateProc.doExportExcelByTemplateProc(templateFileName,exportFilePathAndName,staticDataMap,dynamicDataMappingList); + + } + + /** + * @description: 根据模版导出Excel入口(多个list数据) + * @param templateFileName + * @param exportFilePathAndName + * @param staticSource + * @param originDataMapList + * @return void + * @author susu + * @date 2024/2/20 + */ + public static void doExportExcelMoreListByTemplate(String templateFileName, + String exportFilePathAndName, + Map staticSource, + Map> originDataMapList) throws Exception{ + + Map>> transMap = new HashMap<>(); + originDataMapList.forEach((dataId,originDataList)->{ + List> transDataList = MapObjectUtil.objListToMapList(originDataList); + transMap.put(dataId,transDataList); + }); + // 多个list类型数据 + List dynamicDataMappingList = DynamicDataMapping.createMorDataList(transMap); + // 导出 + ExcelTemplateProc.doExportExcelByTemplateProc(templateFileName,exportFilePathAndName,staticSource,dynamicDataMappingList); + + } + +} diff --git a/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/MapObjectUtil.java b/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/MapObjectUtil.java new file mode 100644 index 000000000..6652fb5eb --- /dev/null +++ b/emis-biz/src/main/java/com/xdadan/erp/emis/service/billexport/MapObjectUtil.java @@ -0,0 +1,97 @@ +package com.xdadan.erp.emis.service.billexport; + +import com.alibaba.fastjson.JSONObject; +import org.apache.commons.beanutils.BeanMap; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Description + * @Author susu + * @Date 2024/2/19 + */ +public class MapObjectUtil { + + /** + * @description: 将object的list数据 转换成 map的list(如:List>) + * @param objDataList + * @return java.util.List> + * @author susu + */ + public static List> objListToMapList(List objDataList){ + List> dataList = new ArrayList<>(); + if (objDataList==null || objDataList.size()<1){ + return null; + } + objDataList.forEach(obj->{ + try { + Map map = MapObjectUtil.objectToMap(obj); + dataList.add(map); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + return dataList; + } + + /** + * @description: 将object数据转换成map数据 + * @param obj + * @return java.util.Map + * @author susu + */ + public static Map objectToMap(Object obj) throws IllegalAccessException { + Map map = new HashMap(); + Class cla = obj.getClass(); + Field[] fields = cla.getDeclaredFields(); + for (Field field : fields) { + field.setAccessible(true); + String keyName = field.getName(); + Object value = field.get(obj); + if (value == null) + value = ""; + map.put(keyName, value); + } + return map; + } + + /** + * @description: 使用 JSONObject 将object转换成map + * @param obj + * @return java.util.Map + * @author susu + */ + public static Map objectToMap2(Object obj) { + if (obj == null) + return null; + return JSONObject.parseObject(JSONObject.toJSONString(obj),Map.class); + } + + /** + * @description: 使用BeanMap将object转换成map + * @param obj + * @return java.util.Map + * @author susu + */ + public static Map objectToMap3(Object obj) { + if (obj == null) + return null; + return new BeanMap(obj); + } + + + public static void main(String[] args) { +// DogEntity2 dog = new DogEntity2(); +// dog.setDogId("A-1001"); +// dog.setDogAge(3); +// dog.setDogName("aaa"); +//// Map map = JSONObject.parseObject(JSONObject.toJSONString(dog),Map.class); +// Map map = objectToMap2(dog); +// System.out.println(map); + } + +} diff --git a/emis-biz/src/main/resources/tpl/settle_bill_tpl.xls b/emis-biz/src/main/resources/tpl/settle_bill_tpl.xls new file mode 100644 index 000000000..19e1da38f Binary files /dev/null and b/emis-biz/src/main/resources/tpl/settle_bill_tpl.xls differ