zhou zhou
6 小时以前 6e5ff559023efd2d24fdca2adcb7268d06420e46
rsf-server/src/main/java/com/vincent/rsf/server/common/utils/ExcelUtil.java
@@ -1,16 +1,26 @@
package com.vincent.rsf.server.common.utils;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.SpringUtils;
import com.vincent.rsf.server.manager.entity.excel.MatnrsTemplate;
import com.vincent.rsf.server.manager.entity.excel.annotation.ExcelComment;
import com.vincent.rsf.server.system.entity.Fields;
import com.vincent.rsf.server.system.service.FieldsService;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;
import org.apache.poi.ss.util.CellRangeAddress;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
@@ -19,8 +29,8 @@
/**
 * Created by vincent on 2/17/2024
 */
@Slf4j
public class ExcelUtil {
    public static void build(Workbook workbook, HttpServletResponse response) {
        response.reset();
        Http.cors(response);
@@ -28,16 +38,27 @@
        try {
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("export", "UTF-8"));
            workbook.write(response.getOutputStream());
        } catch (IOException ignore) {}
        } catch (IOException ignore) {
        }
    }
    public static <T> Workbook create(List<T> list, Class<T> clz) {
        HSSFWorkbook workbook = new HSSFWorkbook();
        return create(list, clz, false);
    }
    public static <T> Workbook create(List<T> list, Class<T> clz, boolean flagTemplate) {
        XSSFWorkbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet(clz.getSimpleName());
        Row header = sheet.createRow(0);
        Field[] fields = Cools.getAllFields(clz);
        int headerIdx = 0;
        FieldsService itemService = SpringUtils.getBean(FieldsService.class);
        List<Fields> sysFields = itemService.list(new LambdaQueryWrapper<Fields>()
                .eq(Fields::getStatus, 1)
                .eq(Fields::getFlagEnable, 1));
        for (Field field : fields) {
            if (Modifier.isFinal(field.getModifiers())
                    || Modifier.isStatic(field.getModifiers())
@@ -45,44 +66,66 @@
                continue;
            }
            String memo = "Undefined";
            if (flagTemplate) {
                memo = field.getName();
            } else {
                if (field.isAnnotationPresent(Excel.class)) {
                    memo = field.getAnnotation(Excel.class).name();
                }
            }
            if (field.isAnnotationPresent(ApiModelProperty.class)) {
                memo = field.getAnnotation(ApiModelProperty.class).value();
            }
            header.createCell(headerIdx).setCellValue(memo);
            headerIdx++;
        }
        int rowIndex = 1;
        for (T t : list) {
            Row row = sheet.createRow(rowIndex++);
            int cellIndex = 0;
            for (Field field : fields) {
                if (Modifier.isFinal(field.getModifiers())
                        || Modifier.isStatic(field.getModifiers())
                        || Modifier.isTransient(field.getModifiers())) {
                    continue;
                }
                field.setAccessible(true);  // 此行很重要,特别是字段为private时
                Object value = null;
                try {
                    value = field.get(t);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                if (value != null) {
                    if (value instanceof Date) {
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        row.createCell(cellIndex).setCellValue(sdf.format((Date) value));
        if (clz.equals(MatnrsTemplate.class)) {
            //添加扩展字段别名
            if (!sysFields.isEmpty()) {
                for (Fields field : sysFields) {
                    if (flagTemplate) {
                        header.createCell(headerIdx).setCellValue(field.getFields());
                    } else {
                        row.createCell(cellIndex).setCellValue(value.toString());
                        header.createCell(headerIdx).setCellValue(field.getFieldsAlise());
                    }
                    headerIdx++;
                }
                cellIndex++;
            }
        }
        int rowIndex = 1;
        if (!Objects.isNull(list)) {
            for (T t : list) {
                Row row = sheet.createRow(rowIndex++);
                int cellIndex = 0;
                for (Field field : fields) {
                    if (Modifier.isFinal(field.getModifiers())
                            || Modifier.isStatic(field.getModifiers())
                            || Modifier.isTransient(field.getModifiers())) {
                        continue;
                    }
                    // 此行很重要,特别是字段为private时
                    field.setAccessible(true);
                    Object value = null;
                    try {
                        value = field.get(t);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    if (value != null) {
                        if (value instanceof Date) {
                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                            row.createCell(cellIndex).setCellValue(sdf.format((Date) value));
                        } else {
                            row.createCell(cellIndex).setCellValue(value.toString());
                        }
                    }
                    cellIndex++;
                }
            }
        }
        for (int i = 0; i <= fields.length; i++) {
            sheet.autoSizeColumn(i);
        }
@@ -90,77 +133,450 @@
        return workbook;
    }
    /**
     * Excel 导入
     * @param file 文件
     * @param keys 数据顺序
     */
    public static  List<Map<String, Object>>  importExcel(MultipartFile file, String[] keys) throws Exception{
        Workbook wb = null;
        String fileName = file.getOriginalFilename();
        if (fileName.endsWith("xls")) {
            POIFSFileSystem pois = new POIFSFileSystem(file.getInputStream());
            wb = new HSSFWorkbook(pois);
        } else if (fileName.endsWith("xlsx")) {
            wb = new XSSFWorkbook(file.getInputStream());
    public static Workbook create(List<?> list, List<ColumnMeta> columns) {
        return create(list, columns, null);
    }
    public static Workbook create(List<?> list, List<ColumnMeta> columns, ReportMeta reportMeta) {
        XSSFWorkbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("export");
        List<ColumnMeta> safeColumns = columns == null ? Collections.emptyList() : columns;
        int sheetColumnCount = safeColumns.size() + 1;
        configureA4PrintLayout(sheet);
        CellStyle titleStyle = createTitleStyle(workbook);
        CellStyle metaLabelStyle = createMetaLabelStyle(workbook);
        CellStyle metaValueStyle = createMetaValueStyle(workbook);
        CellStyle headerStyle = createHeaderStyle(workbook);
        CellStyle bodyStyle = createBodyStyle(workbook);
        CellStyle serialStyle = createCenteredBodyStyle(workbook);
        int rowIndex = 0;
        if (reportMeta != null) {
            Row titleRow = sheet.createRow(rowIndex++);
            titleRow.setHeightInPoints(28);
            Cell titleCell = titleRow.createCell(0);
            titleCell.setCellValue(StringUtils.defaultIfBlank(reportMeta.getTitle(), "报表"));
            titleCell.setCellStyle(titleStyle);
            sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, Math.max(0, sheetColumnCount - 1)));
            Row metaRow = sheet.createRow(rowIndex++);
            int metaCol = 0;
            metaCol = writeMetaPair(metaRow, metaCol, "报表日期", reportMeta.getReportDate(), metaLabelStyle, metaValueStyle);
            writeMetaPair(metaRow, metaCol, "打印人", reportMeta.getPrintedBy(), metaLabelStyle, metaValueStyle);
            rowIndex++;
        }
        Sheet sheet = wb.getSheetAt(0);
        int rowCount = sheet.getPhysicalNumberOfRows();
        if (sheet.getRow( 1).getPhysicalNumberOfCells() != keys.length){
            throw new RuntimeException("导入的Excel和模板的列不匹配");
        int headerRowIndex = rowIndex;
        Row header = sheet.createRow(rowIndex++);
        Cell serialHeaderCell = header.createCell(0);
        serialHeaderCell.setCellValue("序号");
        serialHeaderCell.setCellStyle(headerStyle);
        for (int i = 0; i < safeColumns.size(); i++) {
            ColumnMeta column = safeColumns.get(i);
            Cell headerCell = header.createCell(i + 1);
            headerCell.setCellValue(
                    StringUtils.isBlank(column.getLabel()) ? column.getSource() : column.getLabel()
            );
            headerCell.setCellStyle(headerStyle);
        }
        List<Map<String,Object>> result = new ArrayList<>();
        for (int i = 0; i < rowCount - 1; i++) {
            Row row = sheet.getRow(i + 1);
            Map<String,Object> tmp = new HashMap<>();
            for (int j = 0;j < keys.length; j++){
                Cell cell = row.getCell(j);
                // 把类型转行String
//                cell.setCellType(CellType.STRING);
                tmp.put(keys[j], cell.getStringCellValue());
        if (list != null) {
            int serialNo = 1;
            for (Object rowObj : list) {
                Row row = sheet.createRow(rowIndex++);
                Cell serialCell = row.createCell(0);
                serialCell.setCellValue(String.format("%03d", serialNo++));
                serialCell.setCellStyle(serialStyle);
                for (int i = 0; i < safeColumns.size(); i++) {
                    Object value = getColumnValue(rowObj, safeColumns.get(i).getSource());
                    Cell cell = row.createCell(i + 1);
                    cell.setCellStyle(bodyStyle);
                    if (value != null) {
                        if (value instanceof Date) {
                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                            cell.setCellValue(sdf.format((Date) value));
                        } else {
                            cell.setCellValue(value.toString());
                        }
                    }
                }
            }
            result.add(tmp);
        }
        return result;
        for (int i = 0; i <= safeColumns.size(); i++) {
            sheet.autoSizeColumn(i);
            sheet.setColumnWidth(i, Math.min(sheet.getColumnWidth(i) + 1024, 12000));
        }
        sheet.setRepeatingRows(CellRangeAddress.valueOf((headerRowIndex + 1) + ":" + (headerRowIndex + 1)));
        return workbook;
    }
    /**
     * 表头样式
     */
    private static CellStyle HeaderStyle(Workbook wb){
        Font font = wb.createFont();
        font.setFontName("宋体");
        font.setFontHeightInPoints((short) 11);
        CellStyle cellStyle = commonStyle(wb);
        cellStyle.setFont(font);
        return cellStyle;
    private static void configureA4PrintLayout(Sheet sheet) {
        sheet.setAutobreaks(true);
        sheet.setFitToPage(true);
        sheet.setHorizontallyCenter(true);
        sheet.setDisplayGridlines(false);
        PrintSetup printSetup = sheet.getPrintSetup();
        printSetup.setPaperSize(PrintSetup.A4_PAPERSIZE);
        printSetup.setLandscape(true);
        printSetup.setFitWidth((short) 1);
        printSetup.setFitHeight((short) 0);
        sheet.setMargin(Sheet.LeftMargin, 0.3);
        sheet.setMargin(Sheet.RightMargin, 0.3);
        sheet.setMargin(Sheet.TopMargin, 0.4);
        sheet.setMargin(Sheet.BottomMargin, 0.4);
    }
    /**
     * 内容样式
     */
    private static CellStyle contentStyle(Workbook wb){
        Font font = wb.createFont();
        font.setFontName("宋体");
        font.setFontHeightInPoints((short) 10);
        CellStyle cellStyle = commonStyle(wb);
        cellStyle.setFont(font);
        return cellStyle;
    private static int writeMetaPair(Row row, int startCol, String label, String value, CellStyle labelStyle, CellStyle valueStyle) {
        Cell labelCell = row.createCell(startCol);
        labelCell.setCellValue(label + ":");
        labelCell.setCellStyle(labelStyle);
        Cell valueCell = row.createCell(startCol + 1);
        valueCell.setCellValue(StringUtils.defaultString(value));
        valueCell.setCellStyle(valueStyle);
        return startCol + 2;
    }
    /**
     * 公共样式
     */
    private static CellStyle commonStyle(Workbook wb){
        CellStyle style = wb.createCellStyle();
    private static CellStyle createTitleStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        style.setBorderTop(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);
        style.setWrapText(true);// 自动换行
        style.setBorderBottom(BorderStyle.THICK);
        Font font = workbook.createFont();
        font.setBold(true);
        font.setFontHeightInPoints((short) 16);
        style.setFont(font);
        return style;
    }
    private static CellStyle createMetaLabelStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        style.setAlignment(HorizontalAlignment.LEFT);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderBottom(BorderStyle.THIN);
        Font font = workbook.createFont();
        font.setBold(true);
        style.setFont(font);
        return style;
    }
    private static CellStyle createMetaValueStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        style.setAlignment(HorizontalAlignment.LEFT);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderBottom(BorderStyle.THIN);
        return style;
    }
    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);
        Font font = workbook.createFont();
        font.setBold(true);
        style.setFont(font);
        return style;
    }
    private static CellStyle createBodyStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        style.setAlignment(HorizontalAlignment.LEFT);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        return style;
    }
    private static CellStyle createCenteredBodyStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        return style;
    }
    private static Object getColumnValue(Object rowObj, String source) {
        if (rowObj == null || StringUtils.isBlank(source)) {
            return null;
        }
        if (rowObj instanceof Map) {
            return getValueFromMap((Map<?, ?>) rowObj, source);
        }
        String extendFieldKey = extractExtendFieldKey(source);
        if (extendFieldKey != null) {
            Object extendFields = getBeanValue(rowObj, "extendFields");
            if (extendFields instanceof Map) {
                return ((Map<?, ?>) extendFields).get(extendFieldKey);
            }
            return null;
        }
        return getBeanValue(rowObj, source);
    }
    private static Object getValueFromMap(Map<?, ?> rowObj, String source) {
        String extendFieldKey = extractExtendFieldKey(source);
        if (extendFieldKey != null) {
            Object extendFields = rowObj.get("extendFields");
            if (extendFields instanceof Map) {
                return ((Map<?, ?>) extendFields).get(extendFieldKey);
            }
            return null;
        }
        return rowObj.get(source);
    }
    private static String extractExtendFieldKey(String source) {
        if (source == null || !source.startsWith("extendFields.[")) {
            return null;
        }
        int startIndex = source.indexOf('[');
        int endIndex = source.indexOf(']');
        if (startIndex < 0 || endIndex <= startIndex) {
            return null;
        }
        return source.substring(startIndex + 1, endIndex);
    }
    private static Object getBeanValue(Object rowObj, String source) {
        Object value = invokeGetter(rowObj, source);
        if (value != null) {
            return value;
        }
        Field field = findField(rowObj.getClass(), source);
        if (field == null) {
            return null;
        }
        try {
            field.setAccessible(true);
            return field.get(rowObj);
        } catch (IllegalAccessException ignore) {
            return null;
        }
    }
    private static Object invokeGetter(Object target, String source) {
        String suffix = Character.toUpperCase(source.charAt(0)) + source.substring(1);
        String[] methodNames = new String[] { "get" + suffix, "is" + suffix };
        for (String methodName : methodNames) {
            try {
                Method method = target.getClass().getMethod(methodName);
                return method.invoke(target);
            } catch (Exception ignore) {
            }
        }
        return null;
    }
    private static Field findField(Class<?> clazz, String source) {
        Class<?> current = clazz;
        while (current != null && current != Object.class) {
            try {
                return current.getDeclaredField(source);
            } catch (NoSuchFieldException ignore) {
                current = current.getSuperclass();
            }
        }
        return null;
    }
    /**
     * 添加导入excel配置参数
     * 注:默认配置可满足当前需求
     *
     * @return
     */
    public static ImportParams getDefaultImportParams() {
        ImportParams importParams = new ImportParams();
        importParams.setTitleRows(0);
//        importParams.setNeedVerify(true);
        importParams.setHeadRows(1);
        importParams.setSheetNum(1);
        return importParams;
    }
    /**
     * 根据 {@code tClass} 相关成员变量的 {@link ExcelComment#example()} 字段创建模拟数据,暂不支持 复杂类型
     *
     * @param tClass
     * @return
     */
    public static <T> T mockData(Class<T> tClass) {
        if (tClass == null) {
            return null;
        }
        T instance = null;
        try {
            instance = tClass.newInstance();
            Field[] declaredFields = tClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                ExcelComment comment = declaredField.getAnnotation(ExcelComment.class);
                if (comment == null) {
                    continue;
                }
                declaredField.setAccessible(true);
                Class<?> fieldType = declaredField.getType();
                String exampleValue = comment.example();
                Object value = null;
                if (fieldType == int.class || fieldType == Integer.class) {
                    value = StringUtils.isBlank(exampleValue) ? 0 : Integer.parseInt(exampleValue);
                } else if (fieldType == short.class || fieldType == Short.class) {
                    value = StringUtils.isBlank(exampleValue) ? 0 : Short.parseShort(exampleValue);
                } else if (fieldType == long.class || fieldType == Long.class) {
                    value = StringUtils.isBlank(exampleValue) ? 0 : Long.parseLong(exampleValue);
                } else if (fieldType == double.class || fieldType == Double.class) {
                    value = StringUtils.isBlank(exampleValue) ? 0 : Double.parseDouble(exampleValue);
                } else if (fieldType == boolean.class || fieldType == Boolean.class) {
                    value = StringUtils.isNotBlank(exampleValue) && Boolean.parseBoolean(exampleValue);
                } else if (fieldType == String.class) {
                    value = exampleValue;
                } else if (fieldType == Date.class) {
                    value = DateUtils.parse(exampleValue);
                }
                if (value == null && !isBaseType(fieldType)) {
                    declaredField.set(instance, null);
                } else {
                    declaredField.set(instance, value);
                }
            }
        } catch (Exception e) {
            log.error("数据构造失败,请查询详细信息", e);
            return instance;
        }
        return instance;
    }
    /**
     * 是否是基础数据类型
     *
     * @param className
     * @return
     */
    private static boolean isBaseType(Class<?> className) {
        if (className.equals(java.lang.Integer.class) ||
                className.equals(java.lang.Byte.class) ||
                className.equals(java.lang.Long.class) ||
                className.equals(java.lang.Double.class) ||
                className.equals(java.lang.Float.class) ||
                className.equals(java.lang.Character.class) ||
                className.equals(java.lang.Short.class) ||
                className.equals(java.lang.Boolean.class)) {
            return true;
        }
        return false;
    }
    public static class ColumnMeta {
        private String key;
        private String source;
        private String label;
        private Boolean extendField;
        public String getKey() {
            return key;
        }
        public ColumnMeta setKey(String key) {
            this.key = key;
            return this;
        }
        public String getSource() {
            return source;
        }
        public ColumnMeta setSource(String source) {
            this.source = source;
            return this;
        }
        public String getLabel() {
            return label;
        }
        public ColumnMeta setLabel(String label) {
            this.label = label;
            return this;
        }
        public Boolean getExtendField() {
            return extendField;
        }
        public ColumnMeta setExtendField(Boolean extendField) {
            this.extendField = extendField;
            return this;
        }
    }
    public static class ReportMeta {
        private String title;
        private String companyName;
        private String printedBy;
        private String reportDate;
        private String reportDateValue;
        public String getTitle() {
            return title;
        }
        public ReportMeta setTitle(String title) {
            this.title = title;
            return this;
        }
        public String getCompanyName() {
            return companyName;
        }
        public ReportMeta setCompanyName(String companyName) {
            this.companyName = companyName;
            return this;
        }
        public String getReportDate() {
            return reportDate;
        }
        public ReportMeta setReportDate(String reportDate) {
            this.reportDate = reportDate;
            return this;
        }
        public String getReportDateValue() {
            return reportDateValue;
        }
        public ReportMeta setReportDateValue(String reportDateValue) {
            this.reportDateValue = reportDateValue;
            return this;
        }
        public String getPrintedBy() {
            return printedBy;
        }
        public ReportMeta setPrintedBy(String printedBy) {
            this.printedBy = printedBy;
            return this;
        }
    }
}