7 天以前 11214e3074266a23fe61e8eebbce647fdb7305ef
src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
@@ -10,6 +10,7 @@
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
@@ -23,7 +24,7 @@
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
@@ -428,7 +429,7 @@
                    if (String.class == fieldType)
                    {
                        String s = Convert.toStr(val);
                        if (StringUtils.endsWith(s, ".0"))
                        if (s.matches("^\\d+\\.0$"))
                        {
                            val = StringUtils.substringBefore(s, ".0");
                        }
@@ -476,6 +477,22 @@
                            val = DateUtil.getJavaDate((Double) val);
                        }
                    }
                    else if (LocalDate.class == fieldType)
                    {
                        if (val instanceof String)
                        {
                            Date date = DateUtils.parseDate(val);
                            val = StringUtils.isNull(date) ? null : DateUtils.toLocalDate(date);
                        }
                        else if (val instanceof Date)
                        {
                            val = DateUtils.toLocalDate((Date) val);
                        }
                        else if (val instanceof Double)
                        {
                            val = DateUtils.toLocalDate(DateUtil.getJavaDate((Double) val));
                        }
                    }
                    else if (Boolean.TYPE == fieldType || Boolean.class == fieldType)
                    {
                        val = Convert.toBool(val, false);
@@ -510,6 +527,185 @@
                            List<PictureData> images = pictures.get(row.getRowNum() + "_" + entry.getKey());
                            for (PictureData picture : images)
                            {
                                byte[] data = picture.getData();
                                String fileName = FileUtils.writeImportBytes(data);
                                propertyString.append(fileName).append(SEPARATOR);
                            }
                            val = StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
                        }
                        ReflectUtils.invokeSetter(entity, propertyName, val);
                    }
                }
                list.add(entity);
            }
        }
        return list;
    }
    /**
     * 扩展:读取Excel中多个指定Sheet的数据
     * @param sheetNameList 要读取的Sheet名称列表(null则读取所有Sheet)
     * @param is 输入流
     * @param titleNum 标题占用行数
     * @return Map<Sheet名称, 对应Sheet的数据列表>
     */
    public Map<String, List<T>> importExcelMultiSheet(List<String> sheetNameList, InputStream is, int titleNum) {
        Map<String, List<T>> resultMap = new HashMap<>();
        try {
            this.type = Type.IMPORT;
            this.wb = WorkbookFactory.create(is);
            // 1. 确定要读取的Sheet列表
            List<Sheet> sheetsToRead = new ArrayList<>();
            if (sheetNameList != null && !sheetNameList.isEmpty()) {
                // 读取指定名称的Sheet
                for (String sheetName : sheetNameList) {
                    Sheet sheet = wb.getSheet(sheetName);
                    if (sheet != null) {
                        sheetsToRead.add(sheet);
                    } else {
                        log.warn("指定的Sheet名称不存在:{}", sheetName);
                    }
                }
            } else {
                // 读取所有Sheet
                int sheetCount = wb.getNumberOfSheets();
                for (int i = 0; i < sheetCount; i++) {
                    sheetsToRead.add(wb.getSheetAt(i));
                }
            }
            // 2. 遍历每个Sheet,复用原有导入逻辑
            for (Sheet sheet : sheetsToRead) {
                String sheetName = wb.getSheetName(wb.getSheetIndex(sheet));
                // 复用原有核心导入逻辑(关键:将原有方法拆分为可复用的内部方法)
                List<T> sheetData = importExcelBySheet(sheet, titleNum);
                resultMap.put(sheetName, sheetData);
            }
        } catch (Exception e) {
            log.error("导入多Sheet Excel异常{}", e.getMessage());
            throw new UtilException(e.getMessage());
        } finally {
            IOUtils.closeQuietly(is);
        }
        return resultMap;
    }
    /**
     * 内部复用方法:根据指定Sheet对象读取数据(提取原有importExcel的核心逻辑)
     */
    private List<T> importExcelBySheet(Sheet sheet, int titleNum) throws Exception {
        List<T> list = new ArrayList<T>();
        if (sheet == null) {
            return list;
        }
        boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook);
        Map<String, List<PictureData>> pictures = null;
        if (isXSSFWorkbook) {
            pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb);
        } else {
            pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb);
        }
        int rows = sheet.getLastRowNum();
        if (rows > 0) {
            Map<String, Integer> cellMap = new HashMap<String, Integer>();
            Row heard = sheet.getRow(titleNum);
            for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) {
                Cell cell = heard.getCell(i);
                if (StringUtils.isNotNull(cell)) {
                    String value = this.getCellValue(heard, i).toString();
                    cellMap.put(value, i);
                } else {
                    cellMap.put(null, i);
                }
            }
            List<Object[]> fields = this.getFields();
            Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>();
            for (Object[] objects : fields) {
                Excel attr = (Excel) objects[1];
                Integer column = cellMap.get(attr.name());
                if (column != null) {
                    fieldsMap.put(column, objects);
                }
            }
            for (int i = titleNum + 1; i <= rows; i++) {
                Row row = sheet.getRow(i);
                if (isRowEmpty(row)) {
                    continue;
                }
                T entity = null;
                for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet()) {
                    Object val = this.getCellValue(row, entry.getKey());
                    entity = (entity == null ? clazz.newInstance() : entity);
                    Field field = (Field) entry.getValue()[0];
                    Excel attr = (Excel) entry.getValue()[1];
                    Class<?> fieldType = field.getType();
                    // 以下是原有数据类型转换、字典解析等逻辑(完全复用)
                    if (String.class == fieldType) {
                        String s = Convert.toStr(val);
                        if (s.matches("^\\d+\\.0$")) {
                            val = StringUtils.substringBefore(s, ".0");
                        } else {
                            String dateFormat = field.getAnnotation(Excel.class).dateFormat();
                            if (StringUtils.isNotEmpty(dateFormat)) {
                                val = parseDateToStr(dateFormat, val);
                            } else {
                                val = Convert.toStr(val);
                            }
                        }
                    } else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) {
                        val = Convert.toInt(val);
                    } else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) {
                        val = Convert.toLong(val);
                    } else if (Double.TYPE == fieldType || Double.class == fieldType) {
                        val = Convert.toDouble(val);
                    } else if (Float.TYPE == fieldType || Float.class == fieldType) {
                        val = Convert.toFloat(val);
                    } else if (BigDecimal.class == fieldType) {
                        val = Convert.toBigDecimal(val);
                    } else if (Date.class == fieldType) {
                        if (val instanceof String) {
                            val = DateUtils.parseDate(val);
                        } else if (val instanceof Double) {
                            val = DateUtil.getJavaDate((Double) val);
                        }
                    } else if (LocalDate.class == fieldType) {
                        if (val instanceof String) {
                            Date date = DateUtils.parseDate(val);
                            val = StringUtils.isNull(date) ? null : DateUtils.toLocalDate(date);
                        } else if (val instanceof Date) {
                            val = DateUtils.toLocalDate((Date) val);
                        } else if (val instanceof Double) {
                            val = DateUtils.toLocalDate(DateUtil.getJavaDate((Double) val));
                        }
                    } else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) {
                        val = Convert.toBool(val, false);
                    }
                    if (StringUtils.isNotNull(fieldType)) {
                        String propertyName = field.getName();
                        if (StringUtils.isNotEmpty(attr.targetAttr())) {
                            propertyName = field.getName() + "." + attr.targetAttr();
                        }
                        if (StringUtils.isNotEmpty(attr.readConverterExp())) {
                            val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
                        } else if (StringUtils.isNotEmpty(attr.dictType())) {
                            if (!sysDictMap.containsKey(attr.dictType() + val)) {
                                String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
                                sysDictMap.put(attr.dictType() + val, dictValue);
                            }
                            val = sysDictMap.get(attr.dictType() + val);
                        } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) {
                            val = dataFormatHandlerAdapter(val, attr, null);
                        } else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures)) {
                            StringBuilder propertyString = new StringBuilder();
                            List<PictureData> images = pictures.get(row.getRowNum() + "_" + entry.getKey());
                            for (PictureData picture : images) {
                                byte[] data = picture.getData();
                                String fileName = FileUtils.writeImportBytes(data);
                                propertyString.append(fileName).append(SEPARATOR);
@@ -1873,7 +2069,7 @@
    /**
     * 获取对象的子列表方法
     *
     *
     * @param name 名称
     * @param pojoClass 类对象
     * @return 子列表方法
@@ -1894,4 +2090,247 @@
        }
        return method;
    }
    /**
     * 导出多Sheet Excel模板(静态方法)
     * 支持不同类型的DTO导出到不同的Sheet
     *
     * @param response HTTP响应
     * @param sheetDataMap Map<Sheet名称, SheetData>,SheetData包含数据列表和对应的Class类型
     * @param fileName 文件名
     */
    @SuppressWarnings("unchecked")
    public static void exportExcelMultiSheet(HttpServletResponse response,
            Map<String, SheetData<?>> sheetDataMap, String fileName)
    {
        try (SXSSFWorkbook workbook = new SXSSFWorkbook())
        {
            // 创建样式
            CellStyle headerStyle = createHeaderStyle(workbook);
            CellStyle dataStyle = createDataStyle(workbook);
            // 遍历每个Sheet
            for (Map.Entry<String, SheetData<?>> entry : sheetDataMap.entrySet())
            {
                String sheetName = entry.getKey();
                SheetData<?> sheetData = entry.getValue();
                List<?> dataList = sheetData.getDataList();
                Class<?> clazz = sheetData.getClazz();
                // 创建Sheet
                Sheet sheet = workbook.createSheet(sheetName);
                // 获取字段信息
                List<Object[]> fields = getFieldsByClass(clazz, Type.IMPORT);
                if (fields.isEmpty())
                {
                    continue;
                }
                // 创建表头
                Row headerRow = sheet.createRow(0);
                int colIndex = 0;
                for (Object[] fieldObj : fields)
                {
                    Field field = (Field) fieldObj[0];
                    Excel excel = (Excel) fieldObj[1];
                    Cell cell = headerRow.createCell(colIndex);
                    cell.setCellValue(excel.name());
                    cell.setCellStyle(headerStyle);
                    // 设置列宽
                    sheet.setColumnWidth(colIndex, (int) ((excel.width() + 0.72) * 256));
                    colIndex++;
                }
                // 写入数据
                if (dataList != null && !dataList.isEmpty())
                {
                    int rowIndex = 1;
                    for (Object data : dataList)
                    {
                        Row dataRow = sheet.createRow(rowIndex);
                        colIndex = 0;
                        for (Object[] fieldObj : fields)
                        {
                            Field field = (Field) fieldObj[0];
                            Excel excel = (Excel) fieldObj[1];
                            field.setAccessible(true);
                            Object value = field.get(data);
                            Cell cell = dataRow.createCell(colIndex);
                            setCellValueByType(cell, value, excel, dataStyle, workbook);
                            colIndex++;
                        }
                        rowIndex++;
                    }
                }
            }
            // 输出到响应
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            String encodedFileName = new String(fileName.getBytes("GBK"), "ISO8859_1") + ".xlsx";
            response.addHeader("Content-Disposition", "attachment;filename=" + encodedFileName);
            workbook.write(response.getOutputStream());
        }
        catch (Exception e)
        {
            log.error("导出多Sheet Excel异常: {}", e.getMessage());
            throw new UtilException("导出Excel失败: " + e.getMessage());
        }
    }
    /**
     * Sheet数据封装类
     */
    public static class SheetData<T>
    {
        private List<T> dataList;
        private Class<T> clazz;
        public SheetData(List<T> dataList, Class<T> clazz)
        {
            this.dataList = dataList;
            this.clazz = clazz;
        }
        public List<T> getDataList()
        {
            return dataList;
        }
        public Class<T> getClazz()
        {
            return clazz;
        }
    }
    /**
     * 根据类获取字段列表(静态方法)
     */
    private static List<Object[]> getFieldsByClass(Class<?> clazz, Type type)
    {
        List<Object[]> fields = new ArrayList<>();
        List<Field> tempFields = new ArrayList<>();
        tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
        tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        for (Field field : tempFields)
        {
            if (field.isAnnotationPresent(Excel.class))
            {
                Excel excel = field.getAnnotation(Excel.class);
                if (excel != null && (excel.type() == Type.ALL || excel.type() == type))
                {
                    fields.add(new Object[] { field, excel });
                }
            }
        }
        // 按sort排序
        fields.sort(Comparator.comparing(objects -> ((Excel) objects[1]).sort()));
        return fields;
    }
    /**
     * 创建表头样式
     */
    private static CellStyle createHeaderStyle(Workbook workbook)
    {
        CellStyle style = workbook.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        style.setBorderRight(BorderStyle.THIN);
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderLeft(BorderStyle.THIN);
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderTop(BorderStyle.THIN);
        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderBottom(BorderStyle.THIN);
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        Font font = workbook.createFont();
        font.setFontName("Arial");
        font.setFontHeightInPoints((short) 10);
        font.setBold(true);
        style.setFont(font);
        DataFormat dataFormat = workbook.createDataFormat();
        style.setDataFormat(dataFormat.getFormat("@"));
        return style;
    }
    /**
     * 创建数据样式
     */
    private static CellStyle createDataStyle(Workbook workbook)
    {
        CellStyle style = workbook.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderRight(BorderStyle.THIN);
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderLeft(BorderStyle.THIN);
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderTop(BorderStyle.THIN);
        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderBottom(BorderStyle.THIN);
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        Font font = workbook.createFont();
        font.setFontName("Arial");
        font.setFontHeightInPoints((short) 10);
        style.setFont(font);
        return style;
    }
    /**
     * 设置单元格值
     */
    private static void setCellValueByType(Cell cell, Object value, Excel excel, CellStyle dataStyle, Workbook workbook)
    {
        cell.setCellStyle(dataStyle);
        if (value == null)
        {
            cell.setCellValue("");
            return;
        }
        String dateFormat = excel.dateFormat();
        if (StringUtils.isNotEmpty(dateFormat) && value instanceof Date)
        {
            cell.setCellValue(new SimpleDateFormat(dateFormat).format((Date) value));
        }
        else if (value instanceof Date)
        {
            cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format((Date) value));
        }
        else if (value instanceof BigDecimal)
        {
            cell.setCellValue(((BigDecimal) value).doubleValue());
        }
        else if (value instanceof Number)
        {
            cell.setCellValue(((Number) value).doubleValue());
        }
        else if (value instanceof Boolean)
        {
            cell.setCellValue((Boolean) value);
        }
        else
        {
            String strValue = Convert.toStr(value);
            // 防止CSV注入
            if (StringUtils.startsWithAny(strValue, FORMULA_STR))
            {
                strValue = RegExUtils.replaceFirst(strValue, FORMULA_REGEX_STR, "\t$0");
            }
            cell.setCellValue(strValue);
        }
    }
}