中国算力平台算力登记系统2.0
YZFG
2024-09-30 43dc2996fd2033991539ed35a0429238829a5417
commit | author | age
43dc29 1 package com.odcc.cpzidc.common.utils.poi;
Y 2
3 import java.io.File;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.OutputStream;
8 import java.lang.reflect.Field;
9 import java.lang.reflect.Method;
10 import java.lang.reflect.ParameterizedType;
11 import java.math.BigDecimal;
12 import java.text.DecimalFormat;
13 import java.time.LocalDate;
14 import java.time.LocalDateTime;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.Comparator;
19 import java.util.Date;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.UUID;
25 import java.util.stream.Collectors;
26 import javax.servlet.http.HttpServletResponse;
27 import org.apache.commons.lang3.ArrayUtils;
28 import org.apache.commons.lang3.RegExUtils;
29 import org.apache.commons.lang3.reflect.FieldUtils;
30 import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
31 import org.apache.poi.hssf.usermodel.HSSFPicture;
32 import org.apache.poi.hssf.usermodel.HSSFPictureData;
33 import org.apache.poi.hssf.usermodel.HSSFShape;
34 import org.apache.poi.hssf.usermodel.HSSFSheet;
35 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
36 import org.apache.poi.ooxml.POIXMLDocumentPart;
37 import org.apache.poi.ss.usermodel.BorderStyle;
38 import org.apache.poi.ss.usermodel.Cell;
39 import org.apache.poi.ss.usermodel.CellStyle;
40 import org.apache.poi.ss.usermodel.CellType;
41 import org.apache.poi.ss.usermodel.ClientAnchor;
42 import org.apache.poi.ss.usermodel.DataFormat;
43 import org.apache.poi.ss.usermodel.DataValidation;
44 import org.apache.poi.ss.usermodel.DataValidationConstraint;
45 import org.apache.poi.ss.usermodel.DataValidationHelper;
46 import org.apache.poi.ss.usermodel.DateUtil;
47 import org.apache.poi.ss.usermodel.Drawing;
48 import org.apache.poi.ss.usermodel.FillPatternType;
49 import org.apache.poi.ss.usermodel.Font;
50 import org.apache.poi.ss.usermodel.HorizontalAlignment;
51 import org.apache.poi.ss.usermodel.IndexedColors;
52 import org.apache.poi.ss.usermodel.Name;
53 import org.apache.poi.ss.usermodel.PictureData;
54 import org.apache.poi.ss.usermodel.Row;
55 import org.apache.poi.ss.usermodel.Sheet;
56 import org.apache.poi.ss.usermodel.VerticalAlignment;
57 import org.apache.poi.ss.usermodel.Workbook;
58 import org.apache.poi.ss.usermodel.WorkbookFactory;
59 import org.apache.poi.ss.util.CellRangeAddress;
60 import org.apache.poi.ss.util.CellRangeAddressList;
61 import org.apache.poi.util.IOUtils;
62 import org.apache.poi.xssf.streaming.SXSSFWorkbook;
63 import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
64 import org.apache.poi.xssf.usermodel.XSSFDataValidation;
65 import org.apache.poi.xssf.usermodel.XSSFDrawing;
66 import org.apache.poi.xssf.usermodel.XSSFPicture;
67 import org.apache.poi.xssf.usermodel.XSSFShape;
68 import org.apache.poi.xssf.usermodel.XSSFSheet;
69 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
70 import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
73 import com.odcc.cpzidc.common.annotation.Excel;
74 import com.odcc.cpzidc.common.annotation.Excel.ColumnType;
75 import com.odcc.cpzidc.common.annotation.Excel.Type;
76 import com.odcc.cpzidc.common.annotation.Excels;
77 import com.odcc.cpzidc.common.config.RuoYiConfig;
78 import com.odcc.cpzidc.common.core.domain.AjaxResult;
79 import com.odcc.cpzidc.common.core.text.Convert;
80 import com.odcc.cpzidc.common.exception.UtilException;
81 import com.odcc.cpzidc.common.utils.DateUtils;
82 import com.odcc.cpzidc.common.utils.DictUtils;
83 import com.odcc.cpzidc.common.utils.StringUtils;
84 import com.odcc.cpzidc.common.utils.file.FileTypeUtils;
85 import com.odcc.cpzidc.common.utils.file.FileUtils;
86 import com.odcc.cpzidc.common.utils.file.ImageUtils;
87 import com.odcc.cpzidc.common.utils.reflect.ReflectUtils;
88
89 /**
90  * Excel相关处理
91  * 
92  * @author ruoyi
93  */
94 public class ExcelUtil<T>
95 {
96     private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
97
98     public static final String FORMULA_REGEX_STR = "=|-|\\+|@";
99
100     public static final String[] FORMULA_STR = { "=", "-", "+", "@" };
101
102     /**
103      * 用于dictType属性数据存储,避免重复查缓存
104      */
105     public Map<String, String> sysDictMap = new HashMap<String, String>();
106
107     /**
108      * Excel sheet最大行数,默认65536
109      */
110     public static final int sheetSize = 65536;
111
112     /**
113      * 工作表名称
114      */
115     private String sheetName;
116
117     /**
118      * 导出类型(EXPORT:导出数据;IMPORT:导入模板)
119      */
120     private Type type;
121
122     /**
123      * 工作薄对象
124      */
125     private Workbook wb;
126
127     /**
128      * 工作表对象
129      */
130     private Sheet sheet;
131
132     /**
133      * 样式列表
134      */
135     private Map<String, CellStyle> styles;
136
137     /**
138      * 导入导出数据列表
139      */
140     private List<T> list;
141
142     /**
143      * 注解列表
144      */
145     private List<Object[]> fields;
146
147     /**
148      * 当前行号
149      */
150     private int rownum;
151
152     /**
153      * 标题
154      */
155     private String title;
156
157     /**
158      * 最大高度
159      */
160     private short maxHeight;
161
162     /**
163      * 合并后最后行数
164      */
165     private int subMergedLastRowNum = 0;
166
167     /**
168      * 合并后开始行数
169      */
170     private int subMergedFirstRowNum = 1;
171
172     /**
173      * 对象的子列表方法
174      */
175     private Method subMethod;
176
177     /**
178      * 对象的子列表属性
179      */
180     private List<Field> subFields;
181
182     /**
183      * 统计列表
184      */
185     private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
186
187     /**
188      * 数字格式
189      */
190     private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
191
192     /**
193      * 实体对象
194      */
195     public Class<T> clazz;
196
197     /**
198      * 需要排除列属性
199      */
200     public String[] excludeFields;
201
202     public ExcelUtil(Class<T> clazz)
203     {
204         this.clazz = clazz;
205     }
206
207     /**
208      * 隐藏Excel中列属性
209      *
210      * @param fields 列属性名 示例[单个"name"/多个"id","name"]
211      * @throws Exception
212      */
213     public void hideColumn(String... fields)
214     {
215         this.excludeFields = fields;
216     }
217
218     public void init(List<T> list, String sheetName, String title, Type type)
219     {
220         if (list == null)
221         {
222             list = new ArrayList<T>();
223         }
224         this.list = list;
225         this.sheetName = sheetName;
226         this.type = type;
227         this.title = title;
228         createExcelField();
229         createWorkbook();
230         createTitle();
231         createSubHead();
232     }
233
234     /**
235      * 创建excel第一行标题
236      */
237     public void createTitle()
238     {
239         if (StringUtils.isNotEmpty(title))
240         {
241             subMergedFirstRowNum++;
242             subMergedLastRowNum++;
243             int titleLastCol = this.fields.size() - 1;
244             if (isSubList())
245             {
246                 titleLastCol = titleLastCol + subFields.size() - 1;
247             }
248             Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
249             titleRow.setHeightInPoints(30);
250             Cell titleCell = titleRow.createCell(0);
251             titleCell.setCellStyle(styles.get("title"));
252             titleCell.setCellValue(title);
253             sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol));
254         }
255     }
256
257     /**
258      * 创建对象的子列表名称
259      */
260     public void createSubHead()
261     {
262         if (isSubList())
263         {
264             subMergedFirstRowNum++;
265             subMergedLastRowNum++;
266             Row subRow = sheet.createRow(rownum);
267             int excelNum = 0;
268             for (Object[] objects : fields)
269             {
270                 Excel attr = (Excel) objects[1];
271                 Cell headCell1 = subRow.createCell(excelNum);
272                 headCell1.setCellValue(attr.name());
273                 headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
274                 excelNum++;
275             }
276             int headFirstRow = excelNum - 1;
277             int headLastRow = headFirstRow + subFields.size() - 1;
278             if (headLastRow > headFirstRow)
279             {
280                 sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow));
281             }
282             rownum++;
283         }
284     }
285
286     /**
287      * 对excel表单默认第一个索引名转换成list
288      * 
289      * @param is 输入流
290      * @return 转换后集合
291      */
292     public List<T> importExcel(InputStream is)
293     {
294         List<T> list = null;
295         try
296         {
297             list = importExcel(is, 0);
298         }
299         catch (Exception e)
300         {
301             log.error("导入Excel异常{}", e.getMessage());
302             throw new UtilException(e.getMessage());
303         }
304         finally
305         {
306             IOUtils.closeQuietly(is);
307         }
308         return list;
309     }
310
311     /**
312      * 对excel表单默认第一个索引名转换成list
313      * 
314      * @param is 输入流
315      * @param titleNum 标题占用行数
316      * @return 转换后集合
317      */
318     public List<T> importExcel(InputStream is, int titleNum) throws Exception
319     {
320         return importExcel(StringUtils.EMPTY, is, titleNum);
321     }
322
323     /**
324      * 对excel表单指定表格索引名转换成list
325      * 
326      * @param sheetName 表格索引名
327      * @param titleNum 标题占用行数
328      * @param is 输入流
329      * @return 转换后集合
330      */
331     public List<T> importExcel(String sheetName, InputStream is, int titleNum) throws Exception
332     {
333         this.type = Type.IMPORT;
334         this.wb = WorkbookFactory.create(is);
335         List<T> list = new ArrayList<T>();
336         // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
337         Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
338         if (sheet == null)
339         {
340             throw new IOException("文件sheet不存在");
341         }
342         boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook);
343         Map<String, PictureData> pictures;
344         if (isXSSFWorkbook)
345         {
346             pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb);
347         }
348         else
349         {
350             pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb);
351         }
352         // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1
353         int rows = sheet.getLastRowNum();
354         if (rows > 0)
355         {
356             // 定义一个map用于存放excel列的序号和field.
357             Map<String, Integer> cellMap = new HashMap<String, Integer>();
358             // 获取表头
359             Row heard = sheet.getRow(titleNum);
360             for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++)
361             {
362                 Cell cell = heard.getCell(i);
363                 if (StringUtils.isNotNull(cell))
364                 {
365                     String value = this.getCellValue(heard, i).toString();
366                     cellMap.put(value, i);
367                 }
368                 else
369                 {
370                     cellMap.put(null, i);
371                 }
372             }
373             // 有数据时才处理 得到类的所有field.
374             List<Object[]> fields = this.getFields();
375             Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>();
376             for (Object[] objects : fields)
377             {
378                 Excel attr = (Excel) objects[1];
379                 Integer column = cellMap.get(attr.name());
380                 if (column != null)
381                 {
382                     fieldsMap.put(column, objects);
383                 }
384             }
385             for (int i = titleNum + 1; i <= rows; i++)
386             {
387                 // 从第2行开始取数据,默认第一行是表头.
388                 Row row = sheet.getRow(i);
389                 // 判断当前行是否是空行
390                 if (isRowEmpty(row))
391                 {
392                     continue;
393                 }
394                 T entity = null;
395                 for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet())
396                 {
397                     Object val = this.getCellValue(row, entry.getKey());
398
399                     // 如果不存在实例则新建.
400                     entity = (entity == null ? clazz.newInstance() : entity);
401                     // 从map中得到对应列的field.
402                     Field field = (Field) entry.getValue()[0];
403                     Excel attr = (Excel) entry.getValue()[1];
404                     // 取得类型,并根据对象类型设置值.
405                     Class<?> fieldType = field.getType();
406                     if (String.class == fieldType)
407                     {
408                         String s = Convert.toStr(val);
409                         if (StringUtils.endsWith(s, ".0"))
410                         {
411                             val = StringUtils.substringBefore(s, ".0");
412                         }
413                         else
414                         {
415                             String dateFormat = field.getAnnotation(Excel.class).dateFormat();
416                             if (StringUtils.isNotEmpty(dateFormat))
417                             {
418                                 val = parseDateToStr(dateFormat, val);
419                             }
420                             else
421                             {
422                                 val = Convert.toStr(val);
423                             }
424                         }
425                     }
426                     else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
427                     {
428                         val = Convert.toInt(val);
429                     }
430                     else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
431                     {
432                         val = Convert.toLong(val);
433                     }
434                     else if (Double.TYPE == fieldType || Double.class == fieldType)
435                     {
436                         val = Convert.toDouble(val);
437                     }
438                     else if (Float.TYPE == fieldType || Float.class == fieldType)
439                     {
440                         val = Convert.toFloat(val);
441                     }
442                     else if (BigDecimal.class == fieldType)
443                     {
444                         val = Convert.toBigDecimal(val);
445                     }
446                     else if (Date.class == fieldType)
447                     {
448                         if (val instanceof String)
449                         {
450                             val = DateUtils.parseDate(val);
451                         }
452                         else if (val instanceof Double)
453                         {
454                             val = DateUtil.getJavaDate((Double) val);
455                         }
456                     }
457                     else if (Boolean.TYPE == fieldType || Boolean.class == fieldType)
458                     {
459                         val = Convert.toBool(val, false);
460                     }
461                     if (StringUtils.isNotNull(fieldType))
462                     {
463                         String propertyName = field.getName();
464                         if (StringUtils.isNotEmpty(attr.targetAttr()))
465                         {
466                             propertyName = field.getName() + "." + attr.targetAttr();
467                         }
468                         if (StringUtils.isNotEmpty(attr.readConverterExp()))
469                         {
470                             val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
471                         }
472                         else if (StringUtils.isNotEmpty(attr.dictType()))
473                         {
474                             if (!sysDictMap.containsKey(attr.dictType() + val))
475                             {
476                                 String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
477                                 sysDictMap.put(attr.dictType() + val, dictValue);
478                             }
479                             val = sysDictMap.get(attr.dictType() + val);
480                         }
481                         else if (!attr.handler().equals(ExcelHandlerAdapter.class))
482                         {
483                             val = dataFormatHandlerAdapter(val, attr, null);
484                         }
485                         else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures))
486                         {
487                             PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey());
488                             if (image == null)
489                             {
490                                 val = "";
491                             }
492                             else
493                             {
494                                 byte[] data = image.getData();
495                                 val = FileUtils.writeImportBytes(data);
496                             }
497                         }
498                         ReflectUtils.invokeSetter(entity, propertyName, val);
499                     }
500                 }
501                 list.add(entity);
502             }
503         }
504         return list;
505     }
506
507     /**
508      * 对list数据源将其里面的数据导入到excel表单
509      * 
510      * @param list 导出数据集合
511      * @param sheetName 工作表的名称
512      * @return 结果
513      */
514     public AjaxResult exportExcel(List<T> list, String sheetName)
515     {
516         return exportExcel(list, sheetName, StringUtils.EMPTY);
517     }
518
519     /**
520      * 对list数据源将其里面的数据导入到excel表单
521      * 
522      * @param list 导出数据集合
523      * @param sheetName 工作表的名称
524      * @param title 标题
525      * @return 结果
526      */
527     public AjaxResult exportExcel(List<T> list, String sheetName, String title)
528     {
529         this.init(list, sheetName, title, Type.EXPORT);
530         return exportExcel();
531     }
532
533     /**
534      * 对list数据源将其里面的数据导入到excel表单
535      * 
536      * @param response 返回数据
537      * @param list 导出数据集合
538      * @param sheetName 工作表的名称
539      * @return 结果
540      */
541     public void exportExcel(HttpServletResponse response, List<T> list, String sheetName)
542     {
543         exportExcel(response, list, sheetName, StringUtils.EMPTY);
544     }
545
546     /**
547      * 对list数据源将其里面的数据导入到excel表单
548      * 
549      * @param response 返回数据
550      * @param list 导出数据集合
551      * @param sheetName 工作表的名称
552      * @param title 标题
553      * @return 结果
554      */
555     public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title)
556     {
557         response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
558         response.setCharacterEncoding("utf-8");
559         this.init(list, sheetName, title, Type.EXPORT);
560         exportExcel(response);
561     }
562
563     /**
564      * 对list数据源将其里面的数据导入到excel表单
565      * 
566      * @param sheetName 工作表的名称
567      * @return 结果
568      */
569     public AjaxResult importTemplateExcel(String sheetName)
570     {
571         return importTemplateExcel(sheetName, StringUtils.EMPTY);
572     }
573
574     /**
575      * 对list数据源将其里面的数据导入到excel表单
576      * 
577      * @param sheetName 工作表的名称
578      * @param title 标题
579      * @return 结果
580      */
581     public AjaxResult importTemplateExcel(String sheetName, String title)
582     {
583         this.init(null, sheetName, title, Type.IMPORT);
584         return exportExcel();
585     }
586
587     /**
588      * 对list数据源将其里面的数据导入到excel表单
589      * 
590      * @param sheetName 工作表的名称
591      * @return 结果
592      */
593     public void importTemplateExcel(HttpServletResponse response, String sheetName)
594     {
595         importTemplateExcel(response, sheetName, StringUtils.EMPTY);
596     }
597
598     /**
599      * 对list数据源将其里面的数据导入到excel表单
600      * 
601      * @param sheetName 工作表的名称
602      * @param title 标题
603      * @return 结果
604      */
605     public void importTemplateExcel(HttpServletResponse response, String sheetName, String title)
606     {
607         response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
608         response.setCharacterEncoding("utf-8");
609         this.init(null, sheetName, title, Type.IMPORT);
610         exportExcel(response);
611     }
612
613     /**
614      * 对list数据源将其里面的数据导入到excel表单
615      * 
616      * @return 结果
617      */
618     public void exportExcel(HttpServletResponse response)
619     {
620         try
621         {
622             writeSheet();
623             wb.write(response.getOutputStream());
624         }
625         catch (Exception e)
626         {
627             log.error("导出Excel异常{}", e.getMessage());
628         }
629         finally
630         {
631             IOUtils.closeQuietly(wb);
632         }
633     }
634
635     /**
636      * 对list数据源将其里面的数据导入到excel表单
637      * 
638      * @return 结果
639      */
640     public AjaxResult exportExcel()
641     {
642         OutputStream out = null;
643         try
644         {
645             writeSheet();
646             String filename = encodingFilename(sheetName);
647             out = new FileOutputStream(getAbsoluteFile(filename));
648             wb.write(out);
649             return AjaxResult.success(filename);
650         }
651         catch (Exception e)
652         {
653             log.error("导出Excel异常{}", e.getMessage());
654             throw new UtilException("导出Excel失败,请联系网站管理员!");
655         }
656         finally
657         {
658             IOUtils.closeQuietly(wb);
659             IOUtils.closeQuietly(out);
660         }
661     }
662
663     /**
664      * 创建写入数据到Sheet
665      */
666     public void writeSheet()
667     {
668         // 取出一共有多少个sheet.
669         int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize));
670         for (int index = 0; index < sheetNo; index++)
671         {
672             createSheet(sheetNo, index);
673
674             // 产生一行
675             Row row = sheet.createRow(rownum);
676             int column = 0;
677             // 写入各个字段的列头名称
678             for (Object[] os : fields)
679             {
680                 Field field = (Field) os[0];
681                 Excel excel = (Excel) os[1];
682                 if (Collection.class.isAssignableFrom(field.getType()))
683                 {
684                     for (Field subField : subFields)
685                     {
686                         Excel subExcel = subField.getAnnotation(Excel.class);
687                         this.createHeadCell(subExcel, row, column++);
688                     }
689                 }
690                 else
691                 {
692                     this.createHeadCell(excel, row, column++);
693                 }
694             }
695             if (Type.EXPORT.equals(type))
696             {
697                 fillExcelData(index, row);
698                 addStatisticsRow();
699             }
700         }
701     }
702
703     /**
704      * 填充excel数据
705      * 
706      * @param index 序号
707      * @param row 单元格行
708      */
709     @SuppressWarnings("unchecked")
710     public void fillExcelData(int index, Row row)
711     {
712         int startNo = index * sheetSize;
713         int endNo = Math.min(startNo + sheetSize, list.size());
714         int rowNo = (1 + rownum) - startNo;
715         for (int i = startNo; i < endNo; i++)
716         {
717             rowNo = isSubList() ? (i > 1 ? rowNo + 1 : rowNo + i) : i + 1 + rownum - startNo;
718             row = sheet.createRow(rowNo);
719             // 得到导出对象.
720             T vo = (T) list.get(i);
721             Collection<?> subList = null;
722             if (isSubList())
723             {
724                 if (isSubListValue(vo))
725                 {
726                     subList = getListCellValue(vo);
727                     subMergedLastRowNum = subMergedLastRowNum + subList.size();
728                 }
729                 else
730                 {
731                     subMergedFirstRowNum++;
732                     subMergedLastRowNum++;
733                 }
734             }
735             int column = 0;
736             for (Object[] os : fields)
737             {
738                 Field field = (Field) os[0];
739                 Excel excel = (Excel) os[1];
740                 if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList))
741                 {
742                     boolean subFirst = false;
743                     for (Object obj : subList)
744                     {
745                         if (subFirst)
746                         {
747                             rowNo++;
748                             row = sheet.createRow(rowNo);
749                         }
750                         List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class);
751                         int subIndex = 0;
752                         for (Field subField : subFields)
753                         {
754                             if (subField.isAnnotationPresent(Excel.class))
755                             {
756                                 subField.setAccessible(true);
757                                 Excel attr = subField.getAnnotation(Excel.class);
758                                 this.addCell(attr, row, (T) obj, subField, column + subIndex);
759                             }
760                             subIndex++;
761                         }
762                         subFirst = true;
763                     }
764                     this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size();
765                 }
766                 else
767                 {
768                     this.addCell(excel, row, vo, field, column++);
769                 }
770             }
771         }
772     }
773
774     /**
775      * 创建表格样式
776      * 
777      * @param wb 工作薄对象
778      * @return 样式列表
779      */
780     private Map<String, CellStyle> createStyles(Workbook wb)
781     {
782         // 写入各条记录,每条记录对应excel表中的一行
783         Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
784         CellStyle style = wb.createCellStyle();
785         style.setAlignment(HorizontalAlignment.CENTER);
786         style.setVerticalAlignment(VerticalAlignment.CENTER);
787         Font titleFont = wb.createFont();
788         titleFont.setFontName("Arial");
789         titleFont.setFontHeightInPoints((short) 16);
790         titleFont.setBold(true);
791         style.setFont(titleFont);
792         DataFormat dataFormat = wb.createDataFormat();
793         style.setDataFormat(dataFormat.getFormat("@"));
794         styles.put("title", style);
795
796         style = wb.createCellStyle();
797         style.setAlignment(HorizontalAlignment.CENTER);
798         style.setVerticalAlignment(VerticalAlignment.CENTER);
799         style.setBorderRight(BorderStyle.THIN);
800         style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
801         style.setBorderLeft(BorderStyle.THIN);
802         style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
803         style.setBorderTop(BorderStyle.THIN);
804         style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
805         style.setBorderBottom(BorderStyle.THIN);
806         style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
807         Font dataFont = wb.createFont();
808         dataFont.setFontName("Arial");
809         dataFont.setFontHeightInPoints((short) 10);
810         style.setFont(dataFont);
811         styles.put("data", style);
812
813         style = wb.createCellStyle();
814         style.setAlignment(HorizontalAlignment.CENTER);
815         style.setVerticalAlignment(VerticalAlignment.CENTER);
816         Font totalFont = wb.createFont();
817         totalFont.setFontName("Arial");
818         totalFont.setFontHeightInPoints((short) 10);
819         style.setFont(totalFont);
820         styles.put("total", style);
821
822         styles.putAll(annotationHeaderStyles(wb, styles));
823
824         styles.putAll(annotationDataStyles(wb));
825
826         return styles;
827     }
828
829     /**
830      * 根据Excel注解创建表格头样式
831      * 
832      * @param wb 工作薄对象
833      * @return 自定义样式列表
834      */
835     private Map<String, CellStyle> annotationHeaderStyles(Workbook wb, Map<String, CellStyle> styles)
836     {
837         Map<String, CellStyle> headerStyles = new HashMap<String, CellStyle>();
838         for (Object[] os : fields)
839         {
840             Excel excel = (Excel) os[1];
841             String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor());
842             if (!headerStyles.containsKey(key))
843             {
844                 CellStyle style = wb.createCellStyle();
845                 style.cloneStyleFrom(styles.get("data"));
846                 style.setAlignment(HorizontalAlignment.CENTER);
847                 style.setVerticalAlignment(VerticalAlignment.CENTER);
848                 style.setFillForegroundColor(excel.headerBackgroundColor().index);
849                 style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
850                 Font headerFont = wb.createFont();
851                 headerFont.setFontName("Arial");
852                 headerFont.setFontHeightInPoints((short) 10);
853                 headerFont.setBold(true);
854                 headerFont.setColor(excel.headerColor().index);
855                 style.setFont(headerFont);
856                 // 设置表格头单元格文本形式
857                 DataFormat dataFormat = wb.createDataFormat();
858                 style.setDataFormat(dataFormat.getFormat("@"));
859                 headerStyles.put(key, style);
860             }
861         }
862         return headerStyles;
863     }
864
865     /**
866      * 根据Excel注解创建表格列样式
867      * 
868      * @param wb 工作薄对象
869      * @return 自定义样式列表
870      */
871     private Map<String, CellStyle> annotationDataStyles(Workbook wb)
872     {
873         Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
874         for (Object[] os : fields)
875         {
876             Field field = (Field) os[0];
877             Excel excel = (Excel) os[1];
878             if (Collection.class.isAssignableFrom(field.getType()))
879             {
880                 ParameterizedType pt = (ParameterizedType) field.getGenericType();
881                 Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
882                 List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
883                 for (Field subField : subFields)
884                 {
885                     Excel subExcel = subField.getAnnotation(Excel.class);
886                     annotationDataStyles(styles, subField, subExcel);
887                 }
888             }
889             else
890             {
891                 annotationDataStyles(styles, field, excel);
892             }
893         }
894         return styles;
895     }
896
897     /**
898      * 根据Excel注解创建表格列样式
899      * 
900      * @param styles 自定义样式列表
901      * @param field  属性列信息
902      * @param excel  注解信息
903      */
904     public void annotationDataStyles(Map<String, CellStyle> styles, Field field, Excel excel)
905     {
906         String key = StringUtils.format("data_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType());
907         if (!styles.containsKey(key))
908         {
909             CellStyle style = wb.createCellStyle();
910             style.setAlignment(excel.align());
911             style.setVerticalAlignment(VerticalAlignment.CENTER);
912             style.setBorderRight(BorderStyle.THIN);
913             style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
914             style.setBorderLeft(BorderStyle.THIN);
915             style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
916             style.setBorderTop(BorderStyle.THIN);
917             style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
918             style.setBorderBottom(BorderStyle.THIN);
919             style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
920             style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
921             style.setFillForegroundColor(excel.backgroundColor().getIndex());
922             Font dataFont = wb.createFont();
923             dataFont.setFontName("Arial");
924             dataFont.setFontHeightInPoints((short) 10);
925             dataFont.setColor(excel.color().index);
926             style.setFont(dataFont);
927             if (ColumnType.TEXT == excel.cellType())
928             {
929                 DataFormat dataFormat = wb.createDataFormat();
930                 style.setDataFormat(dataFormat.getFormat("@"));
931             }
932             styles.put(key, style);
933         }
934     }
935
936     /**
937      * 创建单元格
938      */
939     public Cell createHeadCell(Excel attr, Row row, int column)
940     {
941         // 创建列
942         Cell cell = row.createCell(column);
943         // 写入列信息
944         cell.setCellValue(attr.name());
945         setDataValidation(attr, row, column);
946         cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
947         if (isSubList())
948         {
949             // 填充默认样式,防止合并单元格样式失效
950             sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType())));
951             if (attr.needMerge())
952             {
953                 sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column));
954             }
955         }
956         return cell;
957     }
958
959     /**
960      * 设置单元格信息
961      * 
962      * @param value 单元格值
963      * @param attr 注解相关
964      * @param cell 单元格信息
965      */
966     public void setCellVo(Object value, Excel attr, Cell cell)
967     {
968         if (ColumnType.STRING == attr.cellType() || ColumnType.TEXT == attr.cellType())
969         {
970             String cellValue = Convert.toStr(value);
971             // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。
972             if (StringUtils.startsWithAny(cellValue, FORMULA_STR))
973             {
974                 cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0");
975             }
976             if (value instanceof Collection && StringUtils.equals("[]", cellValue))
977             {
978                 cellValue = StringUtils.EMPTY;
979             }
980             cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix());
981         }
982         else if (ColumnType.NUMERIC == attr.cellType())
983         {
984             if (StringUtils.isNotNull(value))
985             {
986                 cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value));
987             }
988         }
989         else if (ColumnType.IMAGE == attr.cellType())
990         {
991             ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1);
992             String imagePath = Convert.toStr(value);
993             if (StringUtils.isNotEmpty(imagePath))
994             {
995                 byte[] data = ImageUtils.getImage(imagePath);
996                 getDrawingPatriarch(cell.getSheet()).createPicture(anchor,
997                         cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
998             }
999         }
1000     }
1001
1002     /**
1003      * 获取画布
1004      */
1005     public static Drawing<?> getDrawingPatriarch(Sheet sheet)
1006     {
1007         if (sheet.getDrawingPatriarch() == null)
1008         {
1009             sheet.createDrawingPatriarch();
1010         }
1011         return sheet.getDrawingPatriarch();
1012     }
1013
1014     /**
1015      * 获取图片类型,设置图片插入类型
1016      */
1017     public int getImageType(byte[] value)
1018     {
1019         String type = FileTypeUtils.getFileExtendName(value);
1020         if ("JPG".equalsIgnoreCase(type))
1021         {
1022             return Workbook.PICTURE_TYPE_JPEG;
1023         }
1024         else if ("PNG".equalsIgnoreCase(type))
1025         {
1026             return Workbook.PICTURE_TYPE_PNG;
1027         }
1028         return Workbook.PICTURE_TYPE_JPEG;
1029     }
1030
1031     /**
1032      * 创建表格样式
1033      */
1034     public void setDataValidation(Excel attr, Row row, int column)
1035     {
1036         if (attr.name().indexOf("注:") >= 0)
1037         {
1038             sheet.setColumnWidth(column, 6000);
1039         }
1040         else
1041         {
1042             // 设置列宽
1043             sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
1044         }
1045         if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0 || attr.comboReadDict())
1046         {
1047             String[] comboArray = attr.combo();
1048             if (attr.comboReadDict())
1049             {
1050                 if (!sysDictMap.containsKey("combo_" + attr.dictType()))
1051                 {
1052                     String labels = DictUtils.getDictLabels(attr.dictType());
1053                     sysDictMap.put("combo_" + attr.dictType(), labels);
1054                 }
1055                 String val = sysDictMap.get("combo_" + attr.dictType());
1056                 comboArray = StringUtils.split(val, DictUtils.SEPARATOR);
1057             }
1058             if (comboArray.length > 15 || StringUtils.join(comboArray).length() > 255)
1059             {
1060                 // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到
1061                 setXSSFValidationWithHidden(sheet, comboArray, attr.prompt(), 1, 100, column, column);
1062             }
1063             else
1064             {
1065                 // 提示信息或只能选择不能输入的列内容.
1066                 setPromptOrValidation(sheet, comboArray, attr.prompt(), 1, 100, column, column);
1067             }
1068         }
1069     }
1070
1071     /**
1072      * 添加单元格
1073      */
1074     public Cell addCell(Excel attr, Row row, T vo, Field field, int column)
1075     {
1076         Cell cell = null;
1077         try
1078         {
1079             // 设置行高
1080             row.setHeight(maxHeight);
1081             // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
1082             if (attr.isExport())
1083             {
1084                 // 创建cell
1085                 cell = row.createCell(column);
1086                 if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge())
1087                 {
1088                     CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column);
1089                     sheet.addMergedRegion(cellAddress);
1090                 }
1091                 cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType())));
1092
1093                 // 用于读取对象中的属性
1094                 Object value = getTargetValue(vo, field, attr);
1095                 String dateFormat = attr.dateFormat();
1096                 String readConverterExp = attr.readConverterExp();
1097                 String separator = attr.separator();
1098                 String dictType = attr.dictType();
1099                 if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value))
1100                 {
1101                     cell.setCellValue(parseDateToStr(dateFormat, value));
1102                 }
1103                 else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value))
1104                 {
1105                     cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator));
1106                 }
1107                 else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value))
1108                 {
1109                     if (!sysDictMap.containsKey(dictType + value))
1110                     {
1111                         String lable = convertDictByExp(Convert.toStr(value), dictType, separator);
1112                         sysDictMap.put(dictType + value, lable);
1113                     }
1114                     cell.setCellValue(sysDictMap.get(dictType + value));
1115                 }
1116                 else if (value instanceof BigDecimal && -1 != attr.scale())
1117                 {
1118                     cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue());
1119                 }
1120                 else if (!attr.handler().equals(ExcelHandlerAdapter.class))
1121                 {
1122                     cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell));
1123                 }
1124                 else
1125                 {
1126                     // 设置列类型
1127                     setCellVo(value, attr, cell);
1128                 }
1129                 addStatisticsData(column, Convert.toStr(value), attr);
1130             }
1131         }
1132         catch (Exception e)
1133         {
1134             log.error("导出Excel失败{}", e);
1135         }
1136         return cell;
1137     }
1138
1139     /**
1140      * 设置 POI XSSFSheet 单元格提示或选择框
1141      * 
1142      * @param sheet 表单
1143      * @param textlist 下拉框显示的内容
1144      * @param promptContent 提示内容
1145      * @param firstRow 开始行
1146      * @param endRow 结束行
1147      * @param firstCol 开始列
1148      * @param endCol 结束列
1149      */
1150     public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow,
1151             int firstCol, int endCol)
1152     {
1153         DataValidationHelper helper = sheet.getDataValidationHelper();
1154         DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1");
1155         CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
1156         DataValidation dataValidation = helper.createValidation(constraint, regions);
1157         if (StringUtils.isNotEmpty(promptContent))
1158         {
1159             // 如果设置了提示信息则鼠标放上去提示
1160             dataValidation.createPromptBox("", promptContent);
1161             dataValidation.setShowPromptBox(true);
1162         }
1163         // 处理Excel兼容性问题
1164         if (dataValidation instanceof XSSFDataValidation)
1165         {
1166             dataValidation.setSuppressDropDownArrow(true);
1167             dataValidation.setShowErrorBox(true);
1168         }
1169         else
1170         {
1171             dataValidation.setSuppressDropDownArrow(false);
1172         }
1173         sheet.addValidationData(dataValidation);
1174     }
1175
1176     /**
1177      * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框).
1178      * 
1179      * @param sheet 要设置的sheet.
1180      * @param textlist 下拉框显示的内容
1181      * @param promptContent 提示内容
1182      * @param firstRow 开始行
1183      * @param endRow 结束行
1184      * @param firstCol 开始列
1185      * @param endCol 结束列
1186      */
1187     public void setXSSFValidationWithHidden(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol)
1188     {
1189         String hideSheetName = "combo_" + firstCol + "_" + endCol;
1190         Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据
1191         for (int i = 0; i < textlist.length; i++)
1192         {
1193             hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]);
1194         }
1195         // 创建名称,可被其他单元格引用
1196         Name name = wb.createName();
1197         name.setNameName(hideSheetName + "_data");
1198         name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length);
1199         DataValidationHelper helper = sheet.getDataValidationHelper();
1200         // 加载下拉列表内容
1201         DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data");
1202         // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
1203         CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
1204         // 数据有效性对象
1205         DataValidation dataValidation = helper.createValidation(constraint, regions);
1206         if (StringUtils.isNotEmpty(promptContent))
1207         {
1208             // 如果设置了提示信息则鼠标放上去提示
1209             dataValidation.createPromptBox("", promptContent);
1210             dataValidation.setShowPromptBox(true);
1211         }
1212         // 处理Excel兼容性问题
1213         if (dataValidation instanceof XSSFDataValidation)
1214         {
1215             dataValidation.setSuppressDropDownArrow(true);
1216             dataValidation.setShowErrorBox(true);
1217         }
1218         else
1219         {
1220             dataValidation.setSuppressDropDownArrow(false);
1221         }
1222
1223         sheet.addValidationData(dataValidation);
1224         // 设置hiddenSheet隐藏
1225         wb.setSheetHidden(wb.getSheetIndex(hideSheet), true);
1226     }
1227
1228     /**
1229      * 解析导出值 0=男,1=女,2=未知
1230      * 
1231      * @param propertyValue 参数值
1232      * @param converterExp 翻译注解
1233      * @param separator 分隔符
1234      * @return 解析后值
1235      */
1236     public static String convertByExp(String propertyValue, String converterExp, String separator)
1237     {
1238         StringBuilder propertyString = new StringBuilder();
1239         String[] convertSource = converterExp.split(",");
1240         for (String item : convertSource)
1241         {
1242             String[] itemArray = item.split("=");
1243             if (StringUtils.containsAny(propertyValue, separator))
1244             {
1245                 for (String value : propertyValue.split(separator))
1246                 {
1247                     if (itemArray[0].equals(value))
1248                     {
1249                         propertyString.append(itemArray[1] + separator);
1250                         break;
1251                     }
1252                 }
1253             }
1254             else
1255             {
1256                 if (itemArray[0].equals(propertyValue))
1257                 {
1258                     return itemArray[1];
1259                 }
1260             }
1261         }
1262         return StringUtils.stripEnd(propertyString.toString(), separator);
1263     }
1264
1265     /**
1266      * 反向解析值 男=0,女=1,未知=2
1267      * 
1268      * @param propertyValue 参数值
1269      * @param converterExp 翻译注解
1270      * @param separator 分隔符
1271      * @return 解析后值
1272      */
1273     public static String reverseByExp(String propertyValue, String converterExp, String separator)
1274     {
1275         StringBuilder propertyString = new StringBuilder();
1276         String[] convertSource = converterExp.split(",");
1277         for (String item : convertSource)
1278         {
1279             String[] itemArray = item.split("=");
1280             if (StringUtils.containsAny(propertyValue, separator))
1281             {
1282                 for (String value : propertyValue.split(separator))
1283                 {
1284                     if (itemArray[1].equals(value))
1285                     {
1286                         propertyString.append(itemArray[0] + separator);
1287                         break;
1288                     }
1289                 }
1290             }
1291             else
1292             {
1293                 if (itemArray[1].equals(propertyValue))
1294                 {
1295                     return itemArray[0];
1296                 }
1297             }
1298         }
1299         return StringUtils.stripEnd(propertyString.toString(), separator);
1300     }
1301
1302     /**
1303      * 解析字典值
1304      * 
1305      * @param dictValue 字典值
1306      * @param dictType 字典类型
1307      * @param separator 分隔符
1308      * @return 字典标签
1309      */
1310     public static String convertDictByExp(String dictValue, String dictType, String separator)
1311     {
1312         return DictUtils.getDictLabel(dictType, dictValue, separator);
1313     }
1314
1315     /**
1316      * 反向解析值字典值
1317      * 
1318      * @param dictLabel 字典标签
1319      * @param dictType 字典类型
1320      * @param separator 分隔符
1321      * @return 字典值
1322      */
1323     public static String reverseDictByExp(String dictLabel, String dictType, String separator)
1324     {
1325         return DictUtils.getDictValue(dictType, dictLabel, separator);
1326     }
1327
1328     /**
1329      * 数据处理器
1330      * 
1331      * @param value 数据值
1332      * @param excel 数据注解
1333      * @return
1334      */
1335     public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell)
1336     {
1337         try
1338         {
1339             Object instance = excel.handler().newInstance();
1340             Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class });
1341             value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb);
1342         }
1343         catch (Exception e)
1344         {
1345             log.error("不能格式化数据 " + excel.handler(), e.getMessage());
1346         }
1347         return Convert.toStr(value);
1348     }
1349
1350     /**
1351      * 合计统计信息
1352      */
1353     private void addStatisticsData(Integer index, String text, Excel entity)
1354     {
1355         if (entity != null && entity.isStatistics())
1356         {
1357             Double temp = 0D;
1358             if (!statistics.containsKey(index))
1359             {
1360                 statistics.put(index, temp);
1361             }
1362             try
1363             {
1364                 temp = Double.valueOf(text);
1365             }
1366             catch (NumberFormatException e)
1367             {
1368             }
1369             statistics.put(index, statistics.get(index) + temp);
1370         }
1371     }
1372
1373     /**
1374      * 创建统计行
1375      */
1376     public void addStatisticsRow()
1377     {
1378         if (statistics.size() > 0)
1379         {
1380             Row row = sheet.createRow(sheet.getLastRowNum() + 1);
1381             Set<Integer> keys = statistics.keySet();
1382             Cell cell = row.createCell(0);
1383             cell.setCellStyle(styles.get("total"));
1384             cell.setCellValue("合计");
1385
1386             for (Integer key : keys)
1387             {
1388                 cell = row.createCell(key);
1389                 cell.setCellStyle(styles.get("total"));
1390                 cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key)));
1391             }
1392             statistics.clear();
1393         }
1394     }
1395
1396     /**
1397      * 编码文件名
1398      */
1399     public String encodingFilename(String filename)
1400     {
1401         filename = UUID.randomUUID() + "_" + filename + ".xlsx";
1402         return filename;
1403     }
1404
1405     /**
1406      * 获取下载路径
1407      * 
1408      * @param filename 文件名称
1409      */
1410     public String getAbsoluteFile(String filename)
1411     {
1412         String downloadPath = RuoYiConfig.getDownloadPath() + filename;
1413         File desc = new File(downloadPath);
1414         if (!desc.getParentFile().exists())
1415         {
1416             desc.getParentFile().mkdirs();
1417         }
1418         return downloadPath;
1419     }
1420
1421     /**
1422      * 获取bean中的属性值
1423      * 
1424      * @param vo 实体对象
1425      * @param field 字段
1426      * @param excel 注解
1427      * @return 最终的属性值
1428      * @throws Exception
1429      */
1430     private Object getTargetValue(T vo, Field field, Excel excel) throws Exception
1431     {
1432         Object o = field.get(vo);
1433         if (StringUtils.isNotEmpty(excel.targetAttr()))
1434         {
1435             String target = excel.targetAttr();
1436             if (target.contains("."))
1437             {
1438                 String[] targets = target.split("[.]");
1439                 for (String name : targets)
1440                 {
1441                     o = getValue(o, name);
1442                 }
1443             }
1444             else
1445             {
1446                 o = getValue(o, target);
1447             }
1448         }
1449         return o;
1450     }
1451
1452     /**
1453      * 以类的属性的get方法方法形式获取值
1454      * 
1455      * @param o
1456      * @param name
1457      * @return value
1458      * @throws Exception
1459      */
1460     private Object getValue(Object o, String name) throws Exception
1461     {
1462         if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name))
1463         {
1464             Class<?> clazz = o.getClass();
1465             Field field = clazz.getDeclaredField(name);
1466             field.setAccessible(true);
1467             o = field.get(o);
1468         }
1469         return o;
1470     }
1471
1472     /**
1473      * 得到所有定义字段
1474      */
1475     private void createExcelField()
1476     {
1477         this.fields = getFields();
1478         this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
1479         this.maxHeight = getRowHeight();
1480     }
1481
1482     /**
1483      * 获取字段注解信息
1484      */
1485     public List<Object[]> getFields()
1486     {
1487         List<Object[]> fields = new ArrayList<Object[]>();
1488         List<Field> tempFields = new ArrayList<>();
1489         tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
1490         tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
1491         for (Field field : tempFields)
1492         {
1493             if (!ArrayUtils.contains(this.excludeFields, field.getName()))
1494             {
1495                 // 单注解
1496                 if (field.isAnnotationPresent(Excel.class))
1497                 {
1498                     Excel attr = field.getAnnotation(Excel.class);
1499                     if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
1500                     {
1501                         field.setAccessible(true);
1502                         fields.add(new Object[] { field, attr });
1503                     }
1504                     if (Collection.class.isAssignableFrom(field.getType()))
1505                     {
1506                         subMethod = getSubMethod(field.getName(), clazz);
1507                         ParameterizedType pt = (ParameterizedType) field.getGenericType();
1508                         Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
1509                         this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
1510                     }
1511                 }
1512
1513                 // 多注解
1514                 if (field.isAnnotationPresent(Excels.class))
1515                 {
1516                     Excels attrs = field.getAnnotation(Excels.class);
1517                     Excel[] excels = attrs.value();
1518                     for (Excel attr : excels)
1519                     {
1520                         if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr())
1521                                 && (attr != null && (attr.type() == Type.ALL || attr.type() == type)))
1522                         {
1523                             field.setAccessible(true);
1524                             fields.add(new Object[] { field, attr });
1525                         }
1526                     }
1527                 }
1528             }
1529         }
1530         return fields;
1531     }
1532
1533     /**
1534      * 根据注解获取最大行高
1535      */
1536     public short getRowHeight()
1537     {
1538         double maxHeight = 0;
1539         for (Object[] os : this.fields)
1540         {
1541             Excel excel = (Excel) os[1];
1542             maxHeight = Math.max(maxHeight, excel.height());
1543         }
1544         return (short) (maxHeight * 20);
1545     }
1546
1547     /**
1548      * 创建一个工作簿
1549      */
1550     public void createWorkbook()
1551     {
1552         this.wb = new SXSSFWorkbook(500);
1553         this.sheet = wb.createSheet();
1554         wb.setSheetName(0, sheetName);
1555         this.styles = createStyles(wb);
1556     }
1557
1558     /**
1559      * 创建工作表
1560      * 
1561      * @param sheetNo sheet数量
1562      * @param index 序号
1563      */
1564     public void createSheet(int sheetNo, int index)
1565     {
1566         // 设置工作表的名称.
1567         if (sheetNo > 1 && index > 0)
1568         {
1569             this.sheet = wb.createSheet();
1570             this.createTitle();
1571             wb.setSheetName(index, sheetName + index);
1572         }
1573     }
1574
1575     /**
1576      * 获取单元格值
1577      * 
1578      * @param row 获取的行
1579      * @param column 获取单元格列号
1580      * @return 单元格值
1581      */
1582     public Object getCellValue(Row row, int column)
1583     {
1584         if (row == null)
1585         {
1586             return row;
1587         }
1588         Object val = "";
1589         try
1590         {
1591             Cell cell = row.getCell(column);
1592             if (StringUtils.isNotNull(cell))
1593             {
1594                 if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA)
1595                 {
1596                     val = cell.getNumericCellValue();
1597                     if (DateUtil.isCellDateFormatted(cell))
1598                     {
1599                         val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
1600                     }
1601                     else
1602                     {
1603                         if ((Double) val % 1 != 0)
1604                         {
1605                             val = new BigDecimal(val.toString());
1606                         }
1607                         else
1608                         {
1609                             val = new DecimalFormat("0").format(val);
1610                         }
1611                     }
1612                 }
1613                 else if (cell.getCellType() == CellType.STRING)
1614                 {
1615                     val = cell.getStringCellValue();
1616                 }
1617                 else if (cell.getCellType() == CellType.BOOLEAN)
1618                 {
1619                     val = cell.getBooleanCellValue();
1620                 }
1621                 else if (cell.getCellType() == CellType.ERROR)
1622                 {
1623                     val = cell.getErrorCellValue();
1624                 }
1625
1626             }
1627         }
1628         catch (Exception e)
1629         {
1630             return val;
1631         }
1632         return val;
1633     }
1634
1635     /**
1636      * 判断是否是空行
1637      * 
1638      * @param row 判断的行
1639      * @return
1640      */
1641     private boolean isRowEmpty(Row row)
1642     {
1643         if (row == null)
1644         {
1645             return true;
1646         }
1647         for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++)
1648         {
1649             Cell cell = row.getCell(i);
1650             if (cell != null && cell.getCellType() != CellType.BLANK)
1651             {
1652                 return false;
1653             }
1654         }
1655         return true;
1656     }
1657
1658     /**
1659      * 获取Excel2003图片
1660      *
1661      * @param sheet 当前sheet对象
1662      * @param workbook 工作簿对象
1663      * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData
1664      */
1665     public static Map<String, PictureData> getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook)
1666     {
1667         Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();
1668         List<HSSFPictureData> pictures = workbook.getAllPictures();
1669         if (!pictures.isEmpty())
1670         {
1671             for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren())
1672             {
1673                 HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor();
1674                 if (shape instanceof HSSFPicture)
1675                 {
1676                     HSSFPicture pic = (HSSFPicture) shape;
1677                     int pictureIndex = pic.getPictureIndex() - 1;
1678                     HSSFPictureData picData = pictures.get(pictureIndex);
1679                     String picIndex = anchor.getRow1() + "_" + anchor.getCol1();
1680                     sheetIndexPicMap.put(picIndex, picData);
1681                 }
1682             }
1683             return sheetIndexPicMap;
1684         }
1685         else
1686         {
1687             return sheetIndexPicMap;
1688         }
1689     }
1690
1691     /**
1692      * 获取Excel2007图片
1693      *
1694      * @param sheet 当前sheet对象
1695      * @param workbook 工作簿对象
1696      * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData
1697      */
1698     public static Map<String, PictureData> getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook)
1699     {
1700         Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();
1701         for (POIXMLDocumentPart dr : sheet.getRelations())
1702         {
1703             if (dr instanceof XSSFDrawing)
1704             {
1705                 XSSFDrawing drawing = (XSSFDrawing) dr;
1706                 List<XSSFShape> shapes = drawing.getShapes();
1707                 for (XSSFShape shape : shapes)
1708                 {
1709                     if (shape instanceof XSSFPicture)
1710                     {
1711                         XSSFPicture pic = (XSSFPicture) shape;
1712                         XSSFClientAnchor anchor = pic.getPreferredSize();
1713                         CTMarker ctMarker = anchor.getFrom();
1714                         String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol();
1715                         sheetIndexPicMap.put(picIndex, pic.getPictureData());
1716                     }
1717                 }
1718             }
1719         }
1720         return sheetIndexPicMap;
1721     }
1722
1723     /**
1724      * 格式化不同类型的日期对象
1725      * 
1726      * @param dateFormat 日期格式
1727      * @param val 被格式化的日期对象
1728      * @return 格式化后的日期字符
1729      */
1730     public String parseDateToStr(String dateFormat, Object val)
1731     {
1732         if (val == null)
1733         {
1734             return "";
1735         }
1736         String str;
1737         if (val instanceof Date)
1738         {
1739             str = DateUtils.parseDateToStr(dateFormat, (Date) val);
1740         }
1741         else if (val instanceof LocalDateTime)
1742         {
1743             str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val));
1744         }
1745         else if (val instanceof LocalDate)
1746         {
1747             str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val));
1748         }
1749         else
1750         {
1751             str = val.toString();
1752         }
1753         return str;
1754     }
1755
1756     /**
1757      * 是否有对象的子列表
1758      */
1759     public boolean isSubList()
1760     {
1761         return StringUtils.isNotNull(subFields) && subFields.size() > 0;
1762     }
1763
1764     /**
1765      * 是否有对象的子列表,集合不为空
1766      */
1767     public boolean isSubListValue(T vo)
1768     {
1769         return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0;
1770     }
1771
1772     /**
1773      * 获取集合的值
1774      */
1775     public Collection<?> getListCellValue(Object obj)
1776     {
1777         Object value;
1778         try
1779         {
1780             value = subMethod.invoke(obj, new Object[] {});
1781         }
1782         catch (Exception e)
1783         {
1784             return new ArrayList<Object>();
1785         }
1786         return (Collection<?>) value;
1787     }
1788
1789     /**
1790      * 获取对象的子列表方法
1791      * 
1792      * @param name 名称
1793      * @param pojoClass 类对象
1794      * @return 子列表方法
1795      */
1796     public Method getSubMethod(String name, Class<?> pojoClass)
1797     {
1798         StringBuffer getMethodName = new StringBuffer("get");
1799         getMethodName.append(name.substring(0, 1).toUpperCase());
1800         getMethodName.append(name.substring(1));
1801         Method method = null;
1802         try
1803         {
1804             method = pojoClass.getMethod(getMethodName.toString(), new Class[] {});
1805         }
1806         catch (Exception e)
1807         {
1808             log.error("获取对象异常{}", e.getMessage());
1809         }
1810         return method;
1811     }
1812 }