Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到KB级别,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便。
我建议大家使用1.1.2-beta4版本,也就是官方给的最新版,maven仓库查到的最新版比这个版本略低,最显著的区别就是excel中数字的输出,最新版本到处excel会将数字专为数字格式,而maven库给出的最新版则会输出成文本格式。其他变化需要各位自己去发现。

官方文档地址

  1. https://github.com/alibaba/easyexcel

添加依赖

  1. <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
  2. <dependency>
  3. <groupId>com.alibaba</groupId>
  4. <artifactId>easyexcel</artifactId>
  5. <version>1.1.2-beta4</version>
  6. </dependency>

使用方法

1.表头带注解方法

这种方法首先需要一个继承BaseRowModel的模型,在Hap框架dto的角色就相当于这个模型,示例如下

  1. package ect.report.dto;
  2. import com.alibaba.excel.annotation.ExcelProperty;
  3. import com.alibaba.excel.metadata.BaseRowModel;
  4. import java.math.BigDecimal;
  5. import java.util.Date;
  6. /**
  7. * @ClassName: TraBilDetail
  8. * @Description: 内贸开票明细模型
  9. * @Author: bo.quan@hand-china.com (全博)
  10. * @Date: 2019-03-29 09:52
  11. * @Version 1.0
  12. */
  13. public class TraBilDetail extends BaseRowModel {
  14. //开票申请号
  15. @ExcelProperty(value = {"开票申请号"}, index = 0)
  16. private String applyNum;
  17. //批次号
  18. @ExcelProperty(value = {"批次号"}, index = 1)
  19. private String batchNum;
  20. //制单部门
  21. @ExcelProperty(value = {"制单部门"}, index = 2)
  22. private String name;
  23. //客户名称
  24. @ExcelProperty(value = {"客户名称"}, index = 3)
  25. private String custName;
  26. //业务类型
  27. @ExcelProperty(value = {"业务类型"}, index = 4)
  28. private String busiType;
  29. //批次开票金额
  30. @ExcelProperty(value = {"批次开票金额"}, index = 5)
  31. private BigDecimal sumPrice;
  32. //批次开票重量
  33. @ExcelProperty(value = {"批次开票重量"}, index = 6)
  34. private BigDecimal invoWeight;
  35. //批次财务入总金额
  36. @ExcelProperty(value = {"批次财务入总金额"}, index = 7)
  37. private BigDecimal findInAmoundSum;
  38. //邮寄信息
  39. @ExcelProperty(value = {"邮寄信息"}, index = 8)
  40. private String mailAddress;
  41. private String contactName;
  42. private String telephone;
  43. /*=========================== 查询字段================================*/
  44. private Long comId;
  45. private Date applyDateStart;
  46. private Date applyDateEnd;
  47. /*=====================================*/
  48. getset方法省略
  49. /*=====================================*/
  50. }

然后Service.java,~ServiceImpl.java都要去掉继承系统框架的base接口。mapper层写好查询出需要展示的数据,dto的注解中字段对应的显示。上述例子写的是单层头结构,如果想要写成双层或者多层,可以想一下示例一样添加。

  1. package ect.report.dto;
  2. import com.alibaba.excel.annotation.ExcelProperty;
  3. import com.alibaba.excel.metadata.BaseRowModel;
  4. import java.math.BigDecimal;
  5. /**
  6. * @ClassName: BatchSupply
  7. * @Description: 在库货源表
  8. * @Author: bo.quan@hand-china.com (全博)
  9. * @Date: 2019-04-02 09:00
  10. * @Version 1.0
  11. */
  12. public class BatchSupply extends BaseRowModel {
  13. //公司名称
  14. @ExcelProperty(value = {"在库货源","姓名"} ,index = 0)
  15. private String comName;
  16. //仓库名称
  17. @ExcelProperty(value = {"在库货源","仓库名称"} ,index = 1)
  18. private String subinvName;
  19. //产地
  20. @ExcelProperty(value = {"在库货源","产地"} ,index = 2)
  21. private String terrName;
  22. //等级
  23. @ExcelProperty(value = {"在库货源","等级"} ,index = 3)
  24. private String ctLevel;
  25. //长度
  26. @ExcelProperty(value = {"在库货源","长度"} ,index = 4)
  27. private String length;
  28. //长度均值
  29. @ExcelProperty(value = {"在库货源","长度均值"} ,index = 5)
  30. private String lengthMean;
  31. //马值
  32. @ExcelProperty(value = {"在库货源","马值"} ,index = 6)
  33. private String micron;
  34. //马值均值
  35. @ExcelProperty(value = {"在库货源","马值均值"} ,index = 7)
  36. private String micronMean;
  37. //强力
  38. @ExcelProperty(value = {"在库货源","强力"} ,index = 8)
  39. private String strong;
  40. //强力均值
  41. @ExcelProperty(value = {"在库货源","强力均值"} ,index = 9)
  42. private String strongMean;
  43. //提单号
  44. @ExcelProperty(value = {"在库货源","提单号"} ,index = 10)
  45. private String blNum;
  46. //入库单号
  47. @ExcelProperty(value = {"在库货源","入库单号"} ,index = 11)
  48. private String entryNum;
  49. //箱号
  50. @ExcelProperty(value = {"在库货源","箱号"} ,index = 12)
  51. private String batchNum;
  52. //入库件数
  53. @ExcelProperty(value = {"在库货源","入库件数"} ,index = 13)
  54. private BigDecimal batchQty;
  55. //销售合同号
  56. @ExcelProperty(value = {"在库货源","销售合同号"} ,index = 14)
  57. private String conNum;
  58. //出库单号
  59. @ExcelProperty(value = {"在库货源","出库单号"} ,index = 15)
  60. private String outNum;
  61. //出库件数
  62. @ExcelProperty(value = {"在库货源","出库件数"} ,index = 16)
  63. private BigDecimal outBatchQty;
  64. //在库箱数
  65. @ExcelProperty(value = {"在库货源","在库箱数"} ,index = 17)
  66. private String outStatus;
  67. //在库件数
  68. @ExcelProperty(value = {"在库货源","在库件数"} ,index = 18)
  69. private BigDecimal wareQty;
  70. //采购合同单价
  71. @ExcelProperty(value = {"在库货源","采购合同单价"} ,index = 19)
  72. private BigDecimal conPrice;
  73. //财务入库成本
  74. @ExcelProperty(value = {"在库货源","财务入库成本"} ,index = 20)
  75. private BigDecimal finaPrice;
  76. //初始财务入库成本(USD)
  77. @ExcelProperty(value = {"在库货源","财务入库成本"} ,index = 21)
  78. private BigDecimal firstUsdPrice;
  79. //初始财务入库成本(CNY)
  80. @ExcelProperty(value = {"在库货源","初始财务入库成本"} ,index = 22)
  81. private BigDecimal firstRmbPrice;
  82. private String lengthMax;
  83. private String lengthMin;
  84. private String micronMax;
  85. private String micronMin;
  86. private String strongMax;
  87. private String strongMin;
  88. private String terrCode;
  89. private String comCode;
  90. private String subinvId;
  91. private String isBatchFlag;
  92. private Long specId;
  93. /*=====================================*/
  94. getset方法省略
  95. /*=====================================*/
  96. }

然后在Service层组装业务逻辑并且输出excel文档。

  1. package ect.report.service.impl;
  2. import com.alibaba.excel.ExcelWriter;
  3. import com.alibaba.excel.metadata.Sheet;
  4. import com.alibaba.excel.support.ExcelTypeEnum;
  5. import ect.inv.util.StringUtils;
  6. import ect.report.dto.TraBilDetail;
  7. import ect.report.mapper.TraBilDetailMapper;
  8. import ect.report.utils.EasyEacelUtils;
  9. import jodd.util.StringUtil;
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.stereotype.Service;
  12. import ect.report.service.ITraBilDetailService;
  13. import org.springframework.transaction.annotation.Transactional;
  14. import javax.servlet.ServletOutputStream;
  15. import javax.servlet.http.HttpServletRequest;
  16. import javax.servlet.http.HttpServletResponse;
  17. import java.io.IOException;
  18. import java.util.List;
  19. /**
  20. * @ClassName: TraBilDetailServiceImpl
  21. * @Description: 内贸开票明细实现层
  22. * @Author: bo.quan@hand-china.com (全博)
  23. * @Date: 2019-03-29 09:53
  24. * @Version 1.0
  25. */
  26. @Service
  27. @Transactional(rollbackFor = Exception.class)
  28. public class TraBilDetailServiceImpl implements ITraBilDetailService{
  29. @Autowired
  30. private TraBilDetailMapper traBilDetailMapper;
  31. @Override
  32. public void export(HttpServletRequest request, TraBilDetail traBilDetail, HttpServletResponse httpServletResponse) throws IOException {
  33. String fileName="内贸开票明细表";
  34. //组装输出流
  35. ServletOutputStream outputStream = EasyEacelUtils.createOutputStream(request, httpServletResponse, fileName);
  36. try {
  37. ExcelWriter writer = new ExcelWriter(outputStream, ExcelTypeEnum.XLSX);
  38. Sheet sheet1 = new Sheet(1, 0, TraBilDetail.class);
  39. //自适应宽度
  40. sheet1.setAutoWidth(Boolean.TRUE);
  41. //添加数据
  42. writer.write(traBilDetailMapper.selectTraBilDetail(traBilDetail), sheet1);
  43. writer.finish();
  44. } catch (Exception e) {
  45. e.printStackTrace();
  46. } finally {
  47. try {
  48. outputStream.close();
  49. } catch (IOException e) {
  50. e.printStackTrace();
  51. }
  52. }
  53. }
  54. }

这样一个非常简单的excel导出就完成了,这种方法适合于常规的excle导出。如果sql查询的数据还不能瞒住需求,需要Java中继续添加业务逻辑的话,可以继续组装成一个返回List的方法。示例如下

  1. package ect.report.service.impl;
  2. import com.alibaba.excel.ExcelWriter;
  3. import com.alibaba.excel.metadata.Sheet;
  4. import com.alibaba.excel.support.ExcelTypeEnum;
  5. import ect.co.mapper.ConContractMapper;
  6. import ect.fs.mapper.FsPayAppExpMapper;
  7. import ect.fs.mapper.FsPayAppGooMapper;
  8. import ect.inv.mapper.WarehousingMapper;
  9. import ect.report.dto.ConSheet;
  10. import ect.report.mapper.ConSheetMapper;
  11. import ect.report.service.IConSheetService;
  12. import ect.report.utils.EasyEacelUtils;
  13. import org.springframework.beans.factory.annotation.Autowired;
  14. import org.springframework.stereotype.Service;
  15. import org.springframework.transaction.annotation.Transactional;
  16. import javax.servlet.ServletOutputStream;
  17. import javax.servlet.http.HttpServletRequest;
  18. import javax.servlet.http.HttpServletResponse;
  19. import java.io.IOException;
  20. import java.math.BigDecimal;
  21. import java.util.HashSet;
  22. import java.util.LinkedList;
  23. import java.util.List;
  24. @Service
  25. @Transactional(rollbackFor = Exception.class)
  26. public class ConSheetServiceImpl implements IConSheetService{
  27. @Autowired
  28. private ConSheetMapper sheetMapper;
  29. @Autowired
  30. private FsPayAppGooMapper fsPayAppGooMapper;
  31. @Autowired
  32. private FsPayAppExpMapper fsPayAppExpMapper;
  33. @Autowired
  34. private ConContractMapper conContractMapper;
  35. @Autowired
  36. private WarehousingMapper warehousingMapper;
  37. private static final String REMORK_FIX = "转仓单重量:";
  38. @Override
  39. public void export(HttpServletRequest request, ConSheet conSheet, HttpServletResponse httpServletResponse) throws IOException {
  40. String fileName="联营业务表";
  41. List<ConSheet> dataList = loadReportData(conSheet);
  42. ServletOutputStream outputStream = EasyEacelUtils.createOutputStream(request, httpServletResponse, fileName);
  43. try {
  44. ExcelWriter writer = new ExcelWriter(outputStream, ExcelTypeEnum.XLSX);
  45. Sheet sheet1 = new Sheet(1, 0, ConSheet.class);
  46. //Table table = new Table(2);
  47. sheet1.setAutoWidth(Boolean.TRUE);
  48. writer.write(dataList, sheet1);
  49. writer.finish();
  50. } catch (Exception e) {
  51. e.printStackTrace();
  52. } finally {
  53. try {
  54. outputStream.close();
  55. } catch (IOException e) {
  56. e.printStackTrace();
  57. }
  58. }
  59. }
  60. /**
  61. * 将sql中查询出的数据添加合计逻辑
  62. * @param conSheet
  63. * @return
  64. */
  65. private List<ConSheet> loadReportData(ConSheet conSheet) {
  66. //汇总信息
  67. ConSheet conSheetSum = new ConSheet();
  68. BigDecimal sumEntryWeight = BigDecimal.ZERO;
  69. BigDecimal sumAppAmt = BigDecimal.ZERO;
  70. BigDecimal sumOutBatchWeight = BigDecimal.ZERO;
  71. BigDecimal sumNoOutBatchWeight = BigDecimal.ZERO;
  72. BigDecimal sumtBatchWeight = BigDecimal.ZERO;
  73. BigDecimal sumInvenWeight = BigDecimal.ZERO;
  74. BigDecimal sumEntryAmt = BigDecimal.ZERO;
  75. BigDecimal sumSumExp = BigDecimal.ZERO;
  76. BigDecimal sumInterest = BigDecimal.ZERO;
  77. BigDecimal sumRiskDegree = BigDecimal.ZERO;
  78. BigDecimal sumCheckAmt = BigDecimal.ZERO;
  79. BigDecimal sumInvoWeight = BigDecimal.ZERO;
  80. BigDecimal sumToBatchWeight = BigDecimal.ZERO;
  81. List<ConSheet> uniConExportList = sheetMapper.selectUniCon(conSheet);
  82. for (ConSheet uniConExport : uniConExportList) {
  83. Long conId = uniConExport.getConId();
  84. // 查询 我司支付
  85. BigDecimal amtSum = fsPayAppGooMapper.selectUniConAmtByConId(conId.floatValue());
  86. uniConExport.setAppAmt(amtSum);
  87. // 费用
  88. BigDecimal sumExp = fsPayAppExpMapper.selectSumExpByConId(conId.floatValue());
  89. uniConExport.setSumExp(sumExp);
  90. // 未销数量
  91. BigDecimal noOutBatchWeight = conContractMapper.selectQtyNotSoldByConId(conId.floatValue());
  92. uniConExport.setNoOutBatchWeight(noOutBatchWeight);
  93. // 求已销数量
  94. BigDecimal outBatchWeight = conContractMapper.selectQtySoldByConId(conId.floatValue());
  95. uniConExport.setOutBatchWeight(outBatchWeight);
  96. // 风险度
  97. BigDecimal invenWeight = uniConExport.getInvenWeight();
  98. if (invenWeight != null && invenWeight.compareTo(BigDecimal.ZERO) != 0) {
  99. // 我司支付
  100. BigDecimal appAmt = uniConExport.getAppAmt();
  101. //回笼资金
  102. BigDecimal entryAmt = uniConExport.getEntryAmt();
  103. // 费用
  104. BigDecimal exp = uniConExport.getSumExp();
  105. // 利息
  106. BigDecimal interest = uniConExport.getInterest();
  107. // 排除空
  108. appAmt = appAmt == null ? BigDecimal.ZERO : appAmt;
  109. entryAmt = entryAmt == null ? BigDecimal.ZERO : entryAmt;
  110. exp = exp == null ? BigDecimal.ZERO : exp;
  111. interest = interest == null ? BigDecimal.ZERO : interest;
  112. BigDecimal sum = appAmt.subtract(entryAmt).add(exp).add(interest);
  113. uniConExport.setRiskDegree(sum.divide(invenWeight, 2, BigDecimal.ROUND_HALF_UP));
  114. }
  115. // 备注
  116. BigDecimal toBatchWeight = warehousingMapper.selectToBatchWeightByConId(conId.floatValue());
  117. toBatchWeight = toBatchWeight == null ? BigDecimal.ZERO : toBatchWeight;
  118. uniConExport.setRemark(REMORK_FIX + toBatchWeight.toString());
  119. sumToBatchWeight = sumToBatchWeight.add(toBatchWeight);
  120. sumEntryWeight = sumEntryWeight.add(uniConExport.getEntryWeight());
  121. sumAppAmt = sumAppAmt.add(uniConExport.getAppAmt()==null?BigDecimal.ZERO:uniConExport.getAppAmt());
  122. sumOutBatchWeight = sumOutBatchWeight.add(uniConExport.getOutBatchWeight()==null?BigDecimal.ZERO:uniConExport.getOutBatchWeight());
  123. sumNoOutBatchWeight = sumNoOutBatchWeight.add(uniConExport.getNoOutBatchWeight()==null?BigDecimal.ZERO:uniConExport.getNoOutBatchWeight());
  124. sumtBatchWeight = sumtBatchWeight.add(uniConExport.getBatchWeight());
  125. sumInvenWeight = sumInvenWeight.add(uniConExport.getInvenWeight());
  126. sumEntryAmt = sumEntryAmt.add(uniConExport.getEntryAmt());
  127. sumSumExp = sumSumExp.add(uniConExport.getSumExp() == null ? BigDecimal.ZERO:uniConExport.getSumExp());
  128. sumInterest = sumInterest.add(uniConExport.getInterest()==null?BigDecimal.ZERO:uniConExport.getInterest());
  129. sumRiskDegree = sumRiskDegree.add(uniConExport.getRiskDegree()==null?BigDecimal.ZERO:uniConExport.getRiskDegree());
  130. sumCheckAmt = sumCheckAmt.add(uniConExport.getCheckAmt()==null?BigDecimal.ZERO:uniConExport.getCheckAmt());
  131. sumInvoWeight = sumInvoWeight.add(uniConExport.getInvoWeight());
  132. }
  133. List<ConSheet> sheetList = groupByParty(uniConExportList);
  134. //汇总信息
  135. //conSheetSum.setEntryWeight(sheetList.stream().map(ConSheet::getEntryWeight).reduce(BigDecimal.ZERO, BigDecimal::add));
  136. conSheetSum.setEntryWeight(sumEntryWeight);
  137. conSheetSum.setAppAmt(sumAppAmt);
  138. conSheetSum.setOutBatchWeight(sumOutBatchWeight);
  139. conSheetSum.setNoOutBatchWeight(sumNoOutBatchWeight);
  140. conSheetSum.setBatchWeight(sumtBatchWeight);
  141. conSheetSum.setInvenWeight(sumInvenWeight);
  142. conSheetSum.setEntryAmt(sumEntryAmt);
  143. conSheetSum.setSumExp(sumSumExp);
  144. conSheetSum.setInterest(sumInterest);
  145. conSheetSum.setRiskDegree(sumRiskDegree);
  146. conSheetSum.setCheckAmt(sumCheckAmt);
  147. conSheetSum.setInvoWeight(sumInvoWeight);
  148. conSheetSum.setRemark(REMORK_FIX +sumToBatchWeight);
  149. conSheetSum.setPartyName("合计: ");
  150. List<ConSheet> conSheets = groupByParty(uniConExportList);
  151. conSheets.add(conSheetSum);
  152. return conSheets;
  153. }
  154. /**
  155. * 根据部门进行分组
  156. * @param uniConExportList
  157. * @return
  158. */
  159. private List<ConSheet> groupByParty(List<ConSheet> uniConExportList) {
  160. LinkedList<ConSheet> finalList = new LinkedList<>();
  161. HashSet<String> partySet = new HashSet<>();
  162. // LinkedHashMap<String,Integer> partyMap = new LinkedHashMap<>();
  163. for(ConSheet uniConExport : uniConExportList){
  164. if (partySet.add(uniConExport.getPartyName())) {
  165. finalList.addLast(uniConExport);
  166. }else {
  167. int i =0;
  168. boolean findFlag = false;
  169. // 插入
  170. for(ConSheet export : finalList ){
  171. if(export.getPartyName().equals(uniConExport.getPartyName())){
  172. findFlag = true;
  173. }else if(findFlag){
  174. break;
  175. }
  176. i++;
  177. }
  178. finalList.add(i,uniConExport);
  179. }
  180. }
  181. return finalList;
  182. }
  183. }

官方示例dome

  1. OutputStream out = new FileOutputStream("/Users/jipengfei/2007.xlsx");
  2. ExcelWriter writer = EasyExcelFactory.getWriterWithTemp(inputStream,out,ExcelTypeEnum.XLSX,true);
  3. //写第一个sheet, sheet1 数据全是List<String> 无模型映射关系
  4. Sheet sheet1 = new Sheet(1, 3);
  5. sheet1.setSheetName("第一个sheet");
  6. //设置列宽 设置每列的宽度
  7. Map columnWidth = new HashMap();
  8. columnWidth.put(0,10000);columnWidth.put(1,40000);columnWidth.put(2,10000);columnWidth.put(3,10000);
  9. sheet1.setColumnWidthMap(columnWidth);
  10. sheet1.setHead(createTestListStringHead());
  11. //or 设置自适应宽度
  12. //sheet1.setAutoWidth(Boolean.TRUE);
  13. writer.write1(createTestListObject(), sheet1);
  14. //写第二个sheet sheet2 模型上打有表头的注解,合并单元格
  15. Sheet sheet2 = new Sheet(2, 3, JavaModel1.class, "第二个sheet", null);
  16. sheet2.setTableStyle(createTableStyle());
  17. writer.write(createTestListJavaMode(), sheet2);
  18. //写第三个sheet包含多个table情况
  19. Sheet sheet3 = new Sheet(3, 0);
  20. sheet3.setSheetName("第三个sheet");
  21. Table table1 = new Table(1);
  22. table1.setHead(createTestListStringHead());
  23. writer.write1(createTestListObject(), sheet3, table1);
  24. //写sheet2 模型上打有表头的注解
  25. Table table2 = new Table(2);
  26. table2.setTableStyle(createTableStyle());
  27. table2.setClazz(JavaModel1.class);
  28. writer.write(createTestListJavaMode(), sheet3, table2);
  29. //关闭资源
  30. writer.finish();
  31. out.close();

2.表头不带注解的方法

这种dto的写法跟上述的dto写法基本相同,就是去掉了注解。也可以不用dto,直接用List

  1. package ect.report.service.impl;
  2. import ch.qos.logback.classic.Logger;
  3. import com.alibaba.excel.ExcelWriter;
  4. import com.alibaba.excel.metadata.Sheet;
  5. import com.alibaba.excel.support.ExcelTypeEnum;
  6. import com.hand.hap.core.IRequest;
  7. import com.hand.hap.fnd.dto.Company;
  8. import com.hand.hap.fnd.service.ICompanyService;
  9. import ect.fs.dto.DailyRate;
  10. import ect.fs.dto.FsComAttr;
  11. import ect.fs.service.IDailyRateService;
  12. import ect.fs.service.IFsComAttrService;
  13. import ect.report.dto.InvomeCostDetail;
  14. import ect.report.mapper.InvomeCostDetailMapper;
  15. import ect.report.utils.EasyEacelUtils;
  16. import org.slf4j.LoggerFactory;
  17. import org.springframework.beans.factory.annotation.Autowired;
  18. import org.springframework.stereotype.Service;
  19. import ect.report.service.IInvomeCostDetailService;
  20. import org.springframework.transaction.annotation.Transactional;
  21. import javax.servlet.ServletOutputStream;
  22. import javax.servlet.http.HttpServletRequest;
  23. import javax.servlet.http.HttpServletResponse;
  24. import java.io.IOException;
  25. import java.math.BigDecimal;
  26. import java.text.SimpleDateFormat;
  27. import java.util.*;
  28. /**
  29. * @ClassName: InvomeCostDetailServiceImpl
  30. * @Description:收入成本表实现类
  31. * @Author: bo.quan@hand-china.com (全博)
  32. * @Date: 2019-03-26 16:14
  33. * @Version 1.0
  34. */
  35. @Service
  36. @Transactional(rollbackFor = Exception.class)
  37. public class InvomeCostDetailServiceImpl implements IInvomeCostDetailService {
  38. private Logger logger = (Logger) LoggerFactory.getLogger(InvomeCostDetailServiceImpl.class);
  39. @Autowired
  40. private InvomeCostDetailMapper invomeCostDetailMapper;
  41. @Autowired
  42. private IDailyRateService dailyRateService;
  43. @Autowired
  44. private ICompanyService companyService;
  45. @Autowired
  46. private IFsComAttrService fsComAttrService;
  47. //自营人民币销售合同
  48. private final static String RMB_SO_CON_NUM = "SESRMBSAL";
  49. //自营美元销售合同
  50. private final static String USD_SO_CON_NUM = "SESUSBSAL";
  51. public List<InvomeCostDetail> loadReportData(IRequest requestContext, InvomeCostDetail detail) {
  52. List<Company> companyList = companyService.selectComIdByRole(requestContext.getRoleId());
  53. String currencyCode = null;
  54. if (!companyList.isEmpty()) {
  55. Long comId = companyList.get(0).getCompanyId();
  56. if (comId != null) {
  57. FsComAttr fsComAttr = fsComAttrService.selectByComId(comId.floatValue());
  58. currencyCode = fsComAttr.getCurrencyCode();
  59. }
  60. }
  61. final String comCurrencyCode = currencyCode;
  62. DailyRate dailyRate = new DailyRate();
  63. dailyRate.setConversionDate(detail.getPostDateEnd());
  64. dailyRate.setFromCurrency(DailyRate.USD);
  65. dailyRate.setToCurrency(DailyRate.CNY);
  66. dailyRate.setConversionType(DailyRate.AVERAGE);
  67. List<DailyRate> dailyRates = dailyRateService.getRateByNew(dailyRate);
  68. if (dailyRates.size()>0){
  69. dailyRate=dailyRates.get(0);
  70. }
  71. final BigDecimal rate = dailyRate.getRate();
  72. List<InvomeCostDetail> incomeCostDetails = invomeCostDetailMapper.selectIncomDetail(detail);
  73. incomeCostDetails.stream().forEach(incomeCostDetail -> {
  74. if (RMB_SO_CON_NUM.equals(incomeCostDetail.getDetType())) {
  75. //发票金额(不含税)
  76. incomeCostDetail.setInvoApplyAmount(incomeCostDetail.getInvoApplyAmountRmb());
  77. //incomeCostDetail.setPoCostAmount(incomeCostDetail.getPoCostAmountRmb());
  78. }
  79. if (USD_SO_CON_NUM.equals(incomeCostDetail.getDetType())) {
  80. //发票金额(不含税)
  81. incomeCostDetail.setInvoApplyAmount(incomeCostDetail.getInvoApplyAmountUsd());
  82. // incomeCostDetail.setPoCostAmount(incomeCostDetail.getPoCostAmountUsd());
  83. }
  84. //出库金额(元)
  85. if (incomeCostDetail.getInvoOutAmountCn() == null) {
  86. incomeCostDetail.setInvoOutAmountCn(incomeCostDetail.getInvoOutAmount());
  87. }
  88. //出库汇率
  89. if (incomeCostDetail.getInvoOutAmountCn() != null && incomeCostDetail.getInvoOutAmount() != null && incomeCostDetail.getInvoOutAmount().compareTo(BigDecimal.ZERO) != 0) {
  90. incomeCostDetail.setRate(incomeCostDetail.getInvoOutAmountCn().divide(incomeCostDetail.getInvoOutAmount(), 4, BigDecimal.ROUND_HALF_UP));
  91. }
  92. BigDecimal finaRate = incomeCostDetail.getRate() == null ? BigDecimal.ONE : incomeCostDetail.getRate();
  93. if ("进口".equals(incomeCostDetail.getBusiType())) {
  94. incomeCostDetail.setInvoApplyAmountYuan(incomeCostDetail.getInvoApplyAmount());
  95. } else if ("转口".equals(incomeCostDetail.getBusiType())) {
  96. if (finaRate.compareTo(BigDecimal.ONE) == 0) {
  97. finaRate = rate;
  98. }
  99. incomeCostDetail.setInvoApplyAmountYuan(incomeCostDetail.getInvoApplyAmount().multiply(finaRate));
  100. }
  101. //财务出库单价(人民币/吨) =出库金额(元)/财务出库重量
  102. if (incomeCostDetail.getInvoOutAmountCn() != null && incomeCostDetail.getInvoOutWeight() != null && incomeCostDetail.getInvoOutWeight().compareTo(BigDecimal.ZERO) != 0) {
  103. incomeCostDetail.setInvoOutPriceCn(incomeCostDetail.getInvoOutAmountCn().divide(incomeCostDetail.getInvoOutWeight(), 10, BigDecimal.ROUND_HALF_UP));
  104. }
  105. ///财务出库单价
  106. if (incomeCostDetail.getInvoOutAmount() != null && incomeCostDetail.getInvoOutWeight() != null && incomeCostDetail.getInvoOutWeight().compareTo(BigDecimal.ZERO) != 0) {
  107. incomeCostDetail.setInvoOutPrice(incomeCostDetail.getInvoOutAmount().divide(incomeCostDetail.getInvoOutWeight(), 10, BigDecimal.ROUND_HALF_UP));
  108. }
  109. //对应初始采购成本金额
  110. incomeCostDetail.setPoCostAmount(incomeCostDetail.getPoCostAmountUsd());
  111. //对应初始采购成本金额(元)
  112. incomeCostDetail.setPoCostAmountCn(incomeCostDetail.getPoCostAmountRmb());
  113. //现货毛利(合并)原币 发票金额(不含税)-对应初始采购成本金额(原币)
  114. if (incomeCostDetail.getInvoApplyAmount() != null && incomeCostDetail.getPoCostAmount() != null) {
  115. if (DailyRate.USD.equals(incomeCostDetail.getInCurrencyCode()) && DailyRate.CNY.equals(incomeCostDetail.getOutCurrencyCode())) {
  116. incomeCostDetail.setIncomeMeshAmount((incomeCostDetail.getInvoApplyAmount().divide(rate, 10, BigDecimal.ROUND_HALF_UP)).subtract(incomeCostDetail.getPoCostAmount()));
  117. } else {
  118. incomeCostDetail.setIncomeMeshAmount(incomeCostDetail.getInvoApplyAmount().subtract(incomeCostDetail.getPoCostAmount()));
  119. }
  120. }
  121. //现货毛利(合并)人民币 发票金额(不含税)-对应初始采购成本金额(元)
  122. BigDecimal applyAmount = BigDecimal.ZERO;
  123. if ("进口".equals(incomeCostDetail.getBusiType())) {
  124. applyAmount = incomeCostDetail.getInvoApplyAmount();
  125. } else {
  126. applyAmount = incomeCostDetail.getSumRmb();
  127. }
  128. if (applyAmount != null && incomeCostDetail.getPoCostAmountCn() != null) {
  129. if (incomeCostDetail.getInvoApplyAmountYuan() != null && incomeCostDetail.getPoCostAmountCn() != null) {
  130. incomeCostDetail.setIncomeMeshAmountCn(incomeCostDetail.getInvoApplyAmountYuan().subtract(incomeCostDetail.getPoCostAmountCn()));
  131. }
  132. }
  133. if ("转口".equals(incomeCostDetail.getBusiType()) && ("中纺棉花(香港)有限公司".equals(incomeCostDetail.getComName()))) {
  134. logger.info("2111");
  135. }
  136. //现货毛利(合并)人民币-财务口径 发票金额(不含税)-对应初始采购成本金额(元)
  137. if ("进口".equals(incomeCostDetail.getBusiType()) || ("转口".equals(incomeCostDetail.getBusiType()) && (!"1445".equals(incomeCostDetail.getComCode())))) {
  138. if (incomeCostDetail.getIncomeMeshAmountCn() != null) {
  139. incomeCostDetail.setIncomeMeshAmountCnFina(incomeCostDetail.getIncomeMeshAmountCn());
  140. }
  141. } else if ("转口".equals(incomeCostDetail.getBusiType()) && ("1445".equals(incomeCostDetail.getComCode()))) {
  142. if (incomeCostDetail.getIncomeMeshAmountCn() != null && rate!=null) {
  143. incomeCostDetail.setIncomeMeshAmountCnFina(incomeCostDetail.getIncomeMeshAmount().multiply(rate));
  144. }
  145. }
  146. //对应初始采购成本单价
  147. if (incomeCostDetail.getPoCostAmount() != null && incomeCostDetail.getPoCostBatchWeight() != null) {
  148. incomeCostDetail.setPoCostPricce(incomeCostDetail.getPoCostAmount().divide(incomeCostDetail.getPoCostBatchWeight(), 10, BigDecimal.ROUND_HALF_UP));
  149. }
  150. //对应初始采购成本单价(元/吨)
  151. if (incomeCostDetail.getPoCostAmountCn() != null && incomeCostDetail.getPoCostBatchWeight() != null) {
  152. incomeCostDetail.setPoCostPricceCn(incomeCostDetail.getPoCostAmountCn().divide(incomeCostDetail.getPoCostBatchWeight(), 10, BigDecimal.ROUND_HALF_UP));
  153. }
  154. //对应初始财务入库汇率
  155. if (incomeCostDetail.getPoCostAmountCn() != null && incomeCostDetail.getPoCostAmount() != null && BigDecimal.ZERO.compareTo(incomeCostDetail.getPoCostAmount()) != 0) {
  156. //对应初始采购成本金额/对应初始采购成本金额
  157. incomeCostDetail.setPoRate(incomeCostDetail.getPoCostAmountCn().divide(incomeCostDetail.getPoCostAmount(), 10, BigDecimal.ROUND_HALF_UP));
  158. }
  159. //现货毛利(独立)原币 发票金额(不含税)-财务出库金额(原币)
  160. if (incomeCostDetail.getInvoApplyAmount() != null && incomeCostDetail.getInvoOutAmount() != null) {
  161. if (DailyRate.USD.equals(incomeCostDetail.getInCurrencyCode()) && DailyRate.CNY.equals(incomeCostDetail.getOutCurrencyCode())) {
  162. incomeCostDetail.setIncomeSelfAmount(incomeCostDetail.getInvoApplyAmount().divide(rate, 10, BigDecimal.ROUND_HALF_UP).subtract(incomeCostDetail.getInvoOutAmount()));
  163. } else {
  164. incomeCostDetail.setIncomeSelfAmount(incomeCostDetail.getInvoApplyAmount().subtract(incomeCostDetail.getInvoOutAmount()));
  165. }
  166. }
  167. //现货毛利(独立)人民币 发票金额(不含税)-财务出库金额(元)
  168. if (applyAmount != null && incomeCostDetail.getInvoOutAmountCn() != null) {
  169. if (DailyRate.USD.equals(comCurrencyCode)) {
  170. incomeCostDetail.setIncomeSelfAmountCn(applyAmount.multiply(rate).subtract(incomeCostDetail.getInvoOutAmountCn()));
  171. } else {
  172. incomeCostDetail.setIncomeSelfAmountCn(applyAmount.subtract(incomeCostDetail.getInvoOutAmountCn()));
  173. }
  174. }
  175. //现货毛利(独立)人民币-财务口径 发票金额(不含税)-财务出库金额(元)
  176. if ("进口".equals(incomeCostDetail.getBusiType()) || ("转口".equals(incomeCostDetail.getBusiType()) && (!"1445".equals(incomeCostDetail.getComCode())))) {
  177. if (incomeCostDetail.getIncomeSelfAmountCn() != null) {
  178. incomeCostDetail.setIncomeSelfAmountCnFina(incomeCostDetail.getIncomeSelfAmountCn());
  179. }
  180. } else if ("转口".equals(incomeCostDetail.getBusiType()) && ("1445".equals(incomeCostDetail.getComCode()))) {
  181. if (incomeCostDetail.getIncomeSelfAmount() != null && rate!=null) {
  182. incomeCostDetail.setIncomeSelfAmountCnFina(incomeCostDetail.getIncomeSelfAmountCn().multiply(rate));
  183. }
  184. }
  185. if ("转口".equals(incomeCostDetail.getBusiType()) && incomeCostDetail.getRate() != null && incomeCostDetail.getRate().compareTo(BigDecimal.ONE) == 0) {
  186. incomeCostDetail.setInvoOutAmountCn(incomeCostDetail.getInvoOutAmountCn().multiply(incomeCostDetail.getRate()));
  187. }
  188. });
  189. return incomeCostDetails;
  190. }
  191. @Override
  192. public void export(IRequest requestContext, HttpServletRequest request, InvomeCostDetail costDetail, HttpServletResponse httpServletResponse) throws IOException {
  193. String fileName = "收入成本毛利表";
  194. ServletOutputStream outputStream = EasyEacelUtils.createOutputStream(request, httpServletResponse, fileName);
  195. try {
  196. ExcelWriter writer = new ExcelWriter(outputStream, ExcelTypeEnum.XLSX);
  197. Sheet sheet1 = new Sheet(1, 0, InvomeCostDetail.class);
  198. Map columnWidth = new HashMap();
  199. columnWidth.put(0, 5000);
  200. columnWidth.put(1, 5000);
  201. columnWidth.put(2, 5000);
  202. columnWidth.put(3, 5000);
  203. columnWidth.put(4, 5000);
  204. columnWidth.put(5, 5000);
  205. columnWidth.put(6, 5000);
  206. columnWidth.put(7, 7000);
  207. columnWidth.put(8, 7000);
  208. columnWidth.put(9, 7000);
  209. columnWidth.put(10, 7000);
  210. columnWidth.put(11, 9000);
  211. columnWidth.put(12, 7000);
  212. columnWidth.put(13, 7000);
  213. columnWidth.put(14, 9000);
  214. columnWidth.put(15, 9000);
  215. columnWidth.put(16, 10000);
  216. columnWidth.put(17, 9000);
  217. columnWidth.put(18, 7000);
  218. columnWidth.put(19, 7000);
  219. columnWidth.put(20, 7000);
  220. columnWidth.put(21, 7000);
  221. columnWidth.put(22, 7000);
  222. sheet1.setColumnWidthMap(columnWidth);
  223. sheet1.setHead(createListHeading(costDetail.getPostDateStart() == null ? null : new SimpleDateFormat("yyyy-MM-dd").format(costDetail.getPostDateStart()),
  224. costDetail.getPostDateEnd() == null ? null : new SimpleDateFormat("yyyy-MM-dd").format(costDetail.getPostDateEnd())));
  225. writer.write(loadReportData(requestContext, costDetail), sheet1);
  226. writer.finish();
  227. } catch (Exception e) {
  228. e.printStackTrace();
  229. } finally {
  230. try {
  231. outputStream.close();
  232. } catch (IOException e) {
  233. e.printStackTrace();
  234. }
  235. }
  236. }
  237. private List<List<String>> createListHeading(String startDate, String endDate) {
  238. List<List<String>> head = new ArrayList<List<String>>();
  239. List<String> headCoulumn1 = new ArrayList<String>();
  240. List<String> headCoulumn2 = new ArrayList<String>();
  241. List<String> headCoulumn3 = new ArrayList<String>();
  242. List<String> headCoulumn4 = new ArrayList<String>();
  243. List<String> headCoulumn5 = new ArrayList<String>();
  244. List<String> headCoulumn6 = new ArrayList<String>();
  245. List<String> headCoulumn7 = new ArrayList<String>();
  246. List<String> headCoulumn8 = new ArrayList<String>();
  247. List<String> headCoulumn9 = new ArrayList<String>();
  248. List<String> headCoulumn10 = new ArrayList<String>();
  249. List<String> headCoulumn11 = new ArrayList<String>();
  250. List<String> headCoulumn12 = new ArrayList<String>();
  251. List<String> headCoulumn13 = new ArrayList<String>();
  252. List<String> headCoulumn14 = new ArrayList<String>();
  253. List<String> headCoulumn15 = new ArrayList<String>();
  254. List<String> headCoulumn16 = new ArrayList<String>();
  255. List<String> headCoulumn17 = new ArrayList<String>();
  256. List<String> headCoulumn18 = new ArrayList<String>();
  257. List<String> headCoulumn19 = new ArrayList<String>();
  258. List<String> headCoulumn20 = new ArrayList<String>();
  259. List<String> headCoulumn21 = new ArrayList<String>();
  260. List<String> headCoulumn22 = new ArrayList<String>();
  261. List<String> headCoulumn23 = new ArrayList<String>();
  262. List<String> headCoulumn24 = new ArrayList<String>();
  263. List<String> headCoulumn25 = new ArrayList<String>();
  264. List<String> headCoulumn26 = new ArrayList<String>();
  265. headCoulumn1.add("收入成本毛利表");
  266. headCoulumn1.add("开票起始日");
  267. headCoulumn1.add("公司名称");
  268. headCoulumn2.add("收入成本毛利表");
  269. headCoulumn2.add(startDate);
  270. headCoulumn2.add("销售客户");
  271. headCoulumn3.add("收入成本毛利表");
  272. headCoulumn3.add("");
  273. headCoulumn3.add("合同号");
  274. headCoulumn4.add("收入成本毛利表");
  275. headCoulumn4.add("");
  276. headCoulumn4.add("开票申请号");
  277. headCoulumn5.add("收入成本毛利表");
  278. headCoulumn5.add("");
  279. headCoulumn5.add("过账日期");
  280. headCoulumn6.add("收入成本毛利表");
  281. headCoulumn6.add("");
  282. headCoulumn6.add("业务类型");
  283. headCoulumn7.add("收入成本毛利表");
  284. headCoulumn7.add("开票截止日");
  285. headCoulumn7.add("发票数量");
  286. headCoulumn8.add("收入成本毛利表");
  287. headCoulumn8.add(endDate);
  288. headCoulumn8.add("发票金额(不含税)");
  289. headCoulumn9.add("收入成本毛利表");
  290. headCoulumn9.add(endDate);
  291. headCoulumn9.add("发票金额(不含税-人民币)");
  292. headCoulumn10.add("收入成本毛利表");
  293. headCoulumn10.add("");
  294. headCoulumn10.add("财务出库数量");
  295. headCoulumn11.add("收入成本毛利表");
  296. headCoulumn11.add("");
  297. headCoulumn11.add("财务出库单价(原币)");
  298. headCoulumn12.add("收入成本毛利表");
  299. headCoulumn12.add("");
  300. headCoulumn12.add("财务出库金额(原币)");
  301. headCoulumn13.add("收入成本毛利表");
  302. headCoulumn13.add("");
  303. headCoulumn13.add("财务出库单价(人民币/吨)");
  304. headCoulumn14.add("收入成本毛利表");
  305. headCoulumn14.add("");
  306. headCoulumn14.add("财务出库金额(元)");
  307. headCoulumn15.add("收入成本毛利表");
  308. headCoulumn15.add("");
  309. headCoulumn15.add("财务出库汇率");
  310. headCoulumn16.add("收入成本毛利表");
  311. headCoulumn16.add("");
  312. headCoulumn16.add("对应初始采购成本单价(原币)");
  313. headCoulumn17.add("收入成本毛利表");
  314. headCoulumn17.add("");
  315. headCoulumn17.add("对应初始采购成本金额(原币)");
  316. headCoulumn18.add("收入成本毛利表");
  317. headCoulumn18.add("");
  318. headCoulumn18.add("对应初始采购成本金额(元)");
  319. headCoulumn19.add("收入成本毛利表");
  320. headCoulumn19.add("");
  321. headCoulumn19.add("对应初始采购成本单价(元/吨)");
  322. headCoulumn20.add("收入成本毛利表");
  323. headCoulumn20.add("");
  324. headCoulumn20.add("对应初始财务入库汇率");
  325. headCoulumn21.add("收入成本毛利表");
  326. headCoulumn21.add("");
  327. headCoulumn21.add("现货毛利(合并)原币");
  328. headCoulumn22.add("收入成本毛利表");
  329. headCoulumn22.add("");
  330. headCoulumn22.add("现货毛利(合并)人民币");
  331. headCoulumn23.add("收入成本毛利表");
  332. headCoulumn23.add("");
  333. headCoulumn23.add("现货毛利(合并)人民币-财务口径");
  334. headCoulumn24.add("收入成本毛利表");
  335. headCoulumn24.add("");
  336. headCoulumn24.add("现货毛利(独立)原币");
  337. headCoulumn25.add("收入成本毛利表");
  338. headCoulumn25.add("");
  339. headCoulumn25.add("现货毛利(独立)人民币");
  340. headCoulumn26.add("收入成本毛利表");
  341. headCoulumn26.add("");
  342. headCoulumn26.add("现货毛利(独立)人民币-财务口径");
  343. head.add(headCoulumn1);
  344. head.add(headCoulumn2);
  345. head.add(headCoulumn3);
  346. head.add(headCoulumn4);
  347. head.add(headCoulumn5);
  348. head.add(headCoulumn6);
  349. head.add(headCoulumn7);
  350. head.add(headCoulumn8);
  351. head.add(headCoulumn9);
  352. head.add(headCoulumn10);
  353. head.add(headCoulumn11);
  354. head.add(headCoulumn12);
  355. head.add(headCoulumn13);
  356. head.add(headCoulumn14);
  357. head.add(headCoulumn15);
  358. head.add(headCoulumn16);
  359. head.add(headCoulumn17);
  360. head.add(headCoulumn18);
  361. head.add(headCoulumn19);
  362. head.add(headCoulumn20);
  363. head.add(headCoulumn21);
  364. head.add(headCoulumn22);
  365. head.add(headCoulumn23);
  366. head.add(headCoulumn24);
  367. head.add(headCoulumn25);
  368. head.add(headCoulumn26);
  369. return head;
  370. }
  371. }

官方示例demo

  1. ExcelWriter writer = EasyExcelFactory.getWriter(out);
  2. //写第一个sheet, sheet1 数据全是List<String> 无模型映射关系
  3. Sheet sheet1 = new Sheet(1, 3);
  4. sheet1.setSheetName("第一个sheet");
  5. //设置列宽 设置每列的宽度
  6. Map columnWidth = new HashMap();
  7. columnWidth.put(0,10000);columnWidth.put(1,40000);columnWidth.put(2,10000);columnWidth.put(3,10000);
  8. sheet1.setColumnWidthMap(columnWidth);
  9. sheet1.setHead(createTestListStringHead());
  10. //or 设置自适应宽度
  11. //sheet1.setAutoWidth(Boolean.TRUE);
  12. writer.write1(createTestListObject(), sheet1);
  13. //写第二个sheet sheet2 模型上打有表头的注解,合并单元格
  14. Sheet sheet2 = new Sheet(2, 3, JavaModel1.class, "第二个sheet", null);
  15. sheet2.setTableStyle(createTableStyle());
  16. writer.write(createTestListJavaMode(), sheet2);
  17. //写第三个sheet包含多个table情况
  18. Sheet sheet3 = new Sheet(3, 0);
  19. sheet3.setSheetName("第三个sheet");
  20. Table table1 = new Table(1);
  21. table1.setHead(createTestListStringHead());
  22. writer.write1(createTestListObject(), sheet3, table1);
  23. //写sheet2 模型上打有表头的注解
  24. Table table2 = new Table(2);
  25. table2.setTableStyle(createTableStyle());
  26. table2.setClazz(JavaModel1.class);
  27. writer.write(createTestListJavaMode(), sheet3, table2);
  28. //关闭资源
  29. writer.finish();
  30. out.close();

总结

以上举例是常用的两种组装数据成excel的方法,第一种适用常规的excel导出,但是表头不能控制,数据也必须跟dto中对应,限制相对大一点。第二种方法灵活行比较大,表头可以自己组装,数据也不用局限dto。在实际运用中就最大的不同就是第二种可以做动态列,但是由于第一种因为其局限性就无法完成动态列的实现,但是它却省了很多功夫来组装数据。两者各有优劣,就看实际的业务需求了。
当然还有同一个sheet中使用table来实现两部分数据块的展示方法等等,这些都可以从官方文档中找个具体的使用方法。这里就不做详细阐述了,本文主要就是讲解数据的组装逻辑方法。