93 changed files with 3327 additions and 237 deletions
			
			
		@ -0,0 +1,872 @@
				@@ -0,0 +1,872 @@
					 | 
				
			||||
package com.ruoyi.common.utils.poi; | 
				
			||||
 | 
				
			||||
import java.io.File; | 
				
			||||
import java.io.FileOutputStream; | 
				
			||||
import java.io.IOException; | 
				
			||||
import java.io.InputStream; | 
				
			||||
import java.io.OutputStream; | 
				
			||||
import java.lang.reflect.Field; | 
				
			||||
import java.lang.reflect.Method; | 
				
			||||
import java.math.BigDecimal; | 
				
			||||
import java.text.DecimalFormat; | 
				
			||||
import java.util.ArrayList; | 
				
			||||
import java.util.Arrays; | 
				
			||||
import java.util.Date; | 
				
			||||
import java.util.HashMap; | 
				
			||||
import java.util.List; | 
				
			||||
import java.util.Map; | 
				
			||||
import java.util.UUID; | 
				
			||||
import org.apache.poi.hssf.usermodel.HSSFDateUtil; | 
				
			||||
import org.apache.poi.ss.usermodel.BorderStyle; | 
				
			||||
import org.apache.poi.ss.usermodel.Cell; | 
				
			||||
import org.apache.poi.ss.usermodel.CellStyle; | 
				
			||||
import org.apache.poi.ss.usermodel.CellType; | 
				
			||||
import org.apache.poi.ss.usermodel.DataValidation; | 
				
			||||
import org.apache.poi.ss.usermodel.DataValidationConstraint; | 
				
			||||
import org.apache.poi.ss.usermodel.DataValidationHelper; | 
				
			||||
import org.apache.poi.ss.usermodel.DateUtil; | 
				
			||||
import org.apache.poi.ss.usermodel.FillPatternType; | 
				
			||||
import org.apache.poi.ss.usermodel.Font; | 
				
			||||
import org.apache.poi.ss.usermodel.HorizontalAlignment; | 
				
			||||
import org.apache.poi.ss.usermodel.IndexedColors; | 
				
			||||
import org.apache.poi.ss.usermodel.Row; | 
				
			||||
import org.apache.poi.ss.usermodel.Sheet; | 
				
			||||
import org.apache.poi.ss.usermodel.VerticalAlignment; | 
				
			||||
import org.apache.poi.ss.usermodel.Workbook; | 
				
			||||
import org.apache.poi.ss.usermodel.WorkbookFactory; | 
				
			||||
import org.apache.poi.ss.util.CellRangeAddressList; | 
				
			||||
import org.apache.poi.xssf.streaming.SXSSFWorkbook; | 
				
			||||
import org.apache.poi.xssf.usermodel.XSSFDataValidation; | 
				
			||||
import org.slf4j.Logger; | 
				
			||||
import org.slf4j.LoggerFactory; | 
				
			||||
import com.ruoyi.framework.aspectj.lang.annotation.Excel; | 
				
			||||
import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType; | 
				
			||||
import com.ruoyi.framework.aspectj.lang.annotation.Excel.Type; | 
				
			||||
import com.ruoyi.framework.aspectj.lang.annotation.Excels; | 
				
			||||
import com.ruoyi.framework.config.RuoYiConfig; | 
				
			||||
import com.ruoyi.framework.web.domain.AjaxResult; | 
				
			||||
import com.ruoyi.common.core.text.Convert; | 
				
			||||
import com.ruoyi.common.exception.CustomException; | 
				
			||||
import com.ruoyi.common.utils.DateUtils; | 
				
			||||
import com.ruoyi.common.utils.StringUtils; | 
				
			||||
import com.ruoyi.common.utils.reflect.ReflectUtils; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Excel相关处理 | 
				
			||||
 *  | 
				
			||||
 * @author ruoyi | 
				
			||||
 */ | 
				
			||||
public class ExcelUtil<T> | 
				
			||||
{ | 
				
			||||
    private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Excel sheet最大行数,默认65536 | 
				
			||||
     */ | 
				
			||||
    public static final int sheetSize = 65536; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 工作表名称 | 
				
			||||
     */ | 
				
			||||
    private String sheetName; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 导出类型(EXPORT:导出数据;IMPORT:导入模板) | 
				
			||||
     */ | 
				
			||||
    private Type type; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 工作薄对象 | 
				
			||||
     */ | 
				
			||||
    private Workbook wb; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 工作表对象 | 
				
			||||
     */ | 
				
			||||
    private Sheet sheet; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 样式列表 | 
				
			||||
     */ | 
				
			||||
    private Map<String, CellStyle> styles; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 导入导出数据列表 | 
				
			||||
     */ | 
				
			||||
    private List<T> list; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 注解列表 | 
				
			||||
     */ | 
				
			||||
    private List<Object[]> fields; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 实体对象 | 
				
			||||
     */ | 
				
			||||
    public Class<T> clazz; | 
				
			||||
 | 
				
			||||
    public ExcelUtil(Class<T> clazz) | 
				
			||||
    { | 
				
			||||
        this.clazz = clazz; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public void init(List<T> list, String sheetName, Type type) | 
				
			||||
    { | 
				
			||||
        if (list == null) | 
				
			||||
        { | 
				
			||||
            list = new ArrayList<T>(); | 
				
			||||
        } | 
				
			||||
        this.list = list; | 
				
			||||
        this.sheetName = sheetName; | 
				
			||||
        this.type = type; | 
				
			||||
        createExcelField(); | 
				
			||||
        createWorkbook(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 对excel表单默认第一个索引名转换成list | 
				
			||||
     *  | 
				
			||||
     * @param is 输入流 | 
				
			||||
     * @return 转换后集合 | 
				
			||||
     */ | 
				
			||||
    public List<T> importExcel(InputStream is) throws Exception | 
				
			||||
    { | 
				
			||||
        return importExcel(StringUtils.EMPTY, is); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 对excel表单指定表格索引名转换成list | 
				
			||||
     *  | 
				
			||||
     * @param sheetName 表格索引名 | 
				
			||||
     * @param is 输入流 | 
				
			||||
     * @return 转换后集合 | 
				
			||||
     */ | 
				
			||||
    public List<T> importExcel(String sheetName, InputStream is) throws Exception | 
				
			||||
    { | 
				
			||||
        this.type = Type.IMPORT; | 
				
			||||
        this.wb = WorkbookFactory.create(is); | 
				
			||||
        List<T> list = new ArrayList<T>(); | 
				
			||||
        Sheet sheet = null; | 
				
			||||
        if (StringUtils.isNotEmpty(sheetName)) | 
				
			||||
        { | 
				
			||||
            // 如果指定sheet名,则取指定sheet中的内容.
 | 
				
			||||
            sheet = wb.getSheet(sheetName); | 
				
			||||
        } | 
				
			||||
        else | 
				
			||||
        { | 
				
			||||
            // 如果传入的sheet名不存在则默认指向第1个sheet.
 | 
				
			||||
            sheet = wb.getSheetAt(0); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if (sheet == null) | 
				
			||||
        { | 
				
			||||
            throw new IOException("文件sheet不存在"); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        int rows = sheet.getPhysicalNumberOfRows(); | 
				
			||||
 | 
				
			||||
        if (rows > 0) | 
				
			||||
        { | 
				
			||||
            // 定义一个map用于存放excel列的序号和field.
 | 
				
			||||
            Map<String, Integer> cellMap = new HashMap<String, Integer>(); | 
				
			||||
            // 获取表头
 | 
				
			||||
            Row heard = sheet.getRow(0); | 
				
			||||
            for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) | 
				
			||||
            { | 
				
			||||
                Cell cell = heard.getCell(i); | 
				
			||||
                if (StringUtils.isNotNull(cell != null)) | 
				
			||||
                { | 
				
			||||
                    String value = this.getCellValue(heard, i).toString(); | 
				
			||||
                    cellMap.put(value, i); | 
				
			||||
                } | 
				
			||||
                else | 
				
			||||
                { | 
				
			||||
                    cellMap.put(null, i); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            // 有数据时才处理 得到类的所有field.
 | 
				
			||||
            Field[] allFields = clazz.getDeclaredFields(); | 
				
			||||
            // 定义一个map用于存放列的序号和field.
 | 
				
			||||
            Map<Integer, Field> fieldsMap = new HashMap<Integer, Field>(); | 
				
			||||
            for (int col = 0; col < allFields.length; col++) | 
				
			||||
            { | 
				
			||||
                Field field = allFields[col]; | 
				
			||||
                Excel attr = field.getAnnotation(Excel.class); | 
				
			||||
                if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) | 
				
			||||
                { | 
				
			||||
                    // 设置类的私有字段属性可访问.
 | 
				
			||||
                    field.setAccessible(true); | 
				
			||||
                    Integer column = cellMap.get(attr.name()); | 
				
			||||
                    fieldsMap.put(column, field); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            for (int i = 1; i < rows; i++) | 
				
			||||
            { | 
				
			||||
                // 从第2行开始取数据,默认第一行是表头.
 | 
				
			||||
                Row row = sheet.getRow(i); | 
				
			||||
                T entity = null; | 
				
			||||
                for (Map.Entry<Integer, Field> entry : fieldsMap.entrySet()) | 
				
			||||
                { | 
				
			||||
                    Object val = this.getCellValue(row, entry.getKey()); | 
				
			||||
 | 
				
			||||
                    // 如果不存在实例则新建.
 | 
				
			||||
                    entity = (entity == null ? clazz.newInstance() : entity); | 
				
			||||
                    // 从map中得到对应列的field.
 | 
				
			||||
                    Field field = fieldsMap.get(entry.getKey()); | 
				
			||||
                    // 取得类型,并根据对象类型设置值.
 | 
				
			||||
                    Class<?> fieldType = field.getType(); | 
				
			||||
                    if (String.class == fieldType) | 
				
			||||
                    { | 
				
			||||
                        String s = Convert.toStr(val); | 
				
			||||
                        if (StringUtils.endsWith(s, ".0")) | 
				
			||||
                        { | 
				
			||||
                            val = StringUtils.substringBefore(s, ".0"); | 
				
			||||
                        } | 
				
			||||
                        else | 
				
			||||
                        { | 
				
			||||
                            val = Convert.toStr(val); | 
				
			||||
                        } | 
				
			||||
                    } | 
				
			||||
                    else if ((Integer.TYPE == fieldType) || (Integer.class == fieldType)) | 
				
			||||
                    { | 
				
			||||
                        val = Convert.toInt(val); | 
				
			||||
                    } | 
				
			||||
                    else if ((Long.TYPE == fieldType) || (Long.class == fieldType)) | 
				
			||||
                    { | 
				
			||||
                        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); | 
				
			||||
                        } | 
				
			||||
                    } | 
				
			||||
                    if (StringUtils.isNotNull(fieldType)) | 
				
			||||
                    { | 
				
			||||
                        Excel attr = field.getAnnotation(Excel.class); | 
				
			||||
                        String propertyName = field.getName(); | 
				
			||||
                        if (StringUtils.isNotEmpty(attr.targetAttr())) | 
				
			||||
                        { | 
				
			||||
                            propertyName = field.getName() + "." + attr.targetAttr(); | 
				
			||||
                        } | 
				
			||||
                        else if (StringUtils.isNotEmpty(attr.readConverterExp())) | 
				
			||||
                        { | 
				
			||||
                            val = reverseByExp(String.valueOf(val), attr.readConverterExp()); | 
				
			||||
                        } | 
				
			||||
                        ReflectUtils.invokeSetter(entity, propertyName, val); | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
                list.add(entity); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        return list; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 对list数据源将其里面的数据导入到excel表单 | 
				
			||||
     *  | 
				
			||||
     * @param list 导出数据集合 | 
				
			||||
     * @param sheetName 工作表的名称 | 
				
			||||
     * @return 结果 | 
				
			||||
     */ | 
				
			||||
    public AjaxResult exportExcel(List<T> list, String sheetName) | 
				
			||||
    { | 
				
			||||
        this.init(list, sheetName, Type.EXPORT); | 
				
			||||
        return exportExcel(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 对list数据源将其里面的数据导入到excel表单 | 
				
			||||
     *  | 
				
			||||
     * @param sheetName 工作表的名称 | 
				
			||||
     * @return 结果 | 
				
			||||
     */ | 
				
			||||
    public AjaxResult importTemplateExcel(String sheetName) | 
				
			||||
    { | 
				
			||||
        this.init(null, sheetName, Type.IMPORT); | 
				
			||||
        return exportExcel(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 对list数据源将其里面的数据导入到excel表单 | 
				
			||||
     *  | 
				
			||||
     * @return 结果 | 
				
			||||
     */ | 
				
			||||
    public AjaxResult exportExcel() | 
				
			||||
    { | 
				
			||||
        OutputStream out = null; | 
				
			||||
        try | 
				
			||||
        { | 
				
			||||
            // 取出一共有多少个sheet.
 | 
				
			||||
            double sheetNo = Math.ceil(list.size() / sheetSize); | 
				
			||||
            for (int index = 0; index <= sheetNo; index++) | 
				
			||||
            { | 
				
			||||
                createSheet(sheetNo, index); | 
				
			||||
 | 
				
			||||
                // 产生一行
 | 
				
			||||
                Row row = sheet.createRow(0); | 
				
			||||
                int column = 0; | 
				
			||||
                // 写入各个字段的列头名称
 | 
				
			||||
                for (Object[] os : fields) | 
				
			||||
                { | 
				
			||||
                    Excel excel = (Excel) os[1]; | 
				
			||||
                    this.createCell(excel, row, column++); | 
				
			||||
                } | 
				
			||||
                if (Type.EXPORT.equals(type)) | 
				
			||||
                { | 
				
			||||
                    fillExcelData(index, row); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            String filename = encodingFilename(sheetName); | 
				
			||||
            out = new FileOutputStream(getAbsoluteFile(filename)); | 
				
			||||
            wb.write(out); | 
				
			||||
            return AjaxResult.success(filename); | 
				
			||||
        } | 
				
			||||
        catch (Exception e) | 
				
			||||
        { | 
				
			||||
            log.error("导出Excel异常{}", e.getMessage()); | 
				
			||||
            throw new CustomException("导出Excel失败,请联系网站管理员!"); | 
				
			||||
        } | 
				
			||||
        finally | 
				
			||||
        { | 
				
			||||
            if (wb != null) | 
				
			||||
            { | 
				
			||||
                try | 
				
			||||
                { | 
				
			||||
                    wb.close(); | 
				
			||||
                } | 
				
			||||
                catch (IOException e1) | 
				
			||||
                { | 
				
			||||
                    e1.printStackTrace(); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            if (out != null) | 
				
			||||
            { | 
				
			||||
                try | 
				
			||||
                { | 
				
			||||
                    out.close(); | 
				
			||||
                } | 
				
			||||
                catch (IOException e1) | 
				
			||||
                { | 
				
			||||
                    e1.printStackTrace(); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 填充excel数据 | 
				
			||||
     *  | 
				
			||||
     * @param index 序号 | 
				
			||||
     * @param row 单元格行 | 
				
			||||
     */ | 
				
			||||
    public void fillExcelData(int index, Row row) | 
				
			||||
    { | 
				
			||||
        int startNo = index * sheetSize; | 
				
			||||
        int endNo = Math.min(startNo + sheetSize, list.size()); | 
				
			||||
        for (int i = startNo; i < endNo; i++) | 
				
			||||
        { | 
				
			||||
            row = sheet.createRow(i + 1 - startNo); | 
				
			||||
            // 得到导出对象.
 | 
				
			||||
            T vo = (T) list.get(i); | 
				
			||||
            int column = 0; | 
				
			||||
            for (Object[] os : fields) | 
				
			||||
            { | 
				
			||||
                Field field = (Field) os[0]; | 
				
			||||
                Excel excel = (Excel) os[1]; | 
				
			||||
                // 设置实体类私有属性可访问
 | 
				
			||||
                field.setAccessible(true); | 
				
			||||
                this.addCell(excel, row, vo, field, column++); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 创建表格样式 | 
				
			||||
     *  | 
				
			||||
     * @param wb 工作薄对象 | 
				
			||||
     * @return 样式列表 | 
				
			||||
     */ | 
				
			||||
    private Map<String, CellStyle> createStyles(Workbook wb) | 
				
			||||
    { | 
				
			||||
        // 写入各条记录,每条记录对应excel表中的一行
 | 
				
			||||
        Map<String, CellStyle> styles = new HashMap<String, CellStyle>(); | 
				
			||||
        CellStyle style = wb.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 dataFont = wb.createFont(); | 
				
			||||
        dataFont.setFontName("Arial"); | 
				
			||||
        dataFont.setFontHeightInPoints((short) 10); | 
				
			||||
        style.setFont(dataFont); | 
				
			||||
        styles.put("data", style); | 
				
			||||
 | 
				
			||||
        style = wb.createCellStyle(); | 
				
			||||
        style.cloneStyleFrom(styles.get("data")); | 
				
			||||
        style.setAlignment(HorizontalAlignment.CENTER); | 
				
			||||
        style.setVerticalAlignment(VerticalAlignment.CENTER); | 
				
			||||
        style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex()); | 
				
			||||
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND); | 
				
			||||
        Font headerFont = wb.createFont(); | 
				
			||||
        headerFont.setFontName("Arial"); | 
				
			||||
        headerFont.setFontHeightInPoints((short) 10); | 
				
			||||
        headerFont.setBold(true); | 
				
			||||
        headerFont.setColor(IndexedColors.WHITE.getIndex()); | 
				
			||||
        style.setFont(headerFont); | 
				
			||||
        styles.put("header", style); | 
				
			||||
 | 
				
			||||
        return styles; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 创建单元格 | 
				
			||||
     */ | 
				
			||||
    public Cell createCell(Excel attr, Row row, int column) | 
				
			||||
    { | 
				
			||||
        // 创建列
 | 
				
			||||
        Cell cell = row.createCell(column); | 
				
			||||
        // 写入列信息
 | 
				
			||||
        cell.setCellValue(attr.name()); | 
				
			||||
        setDataValidation(attr, row, column); | 
				
			||||
        cell.setCellStyle(styles.get("header")); | 
				
			||||
        return cell; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 设置单元格信息 | 
				
			||||
     *  | 
				
			||||
     * @param value 单元格值 | 
				
			||||
     * @param attr 注解相关 | 
				
			||||
     * @param cell 单元格信息 | 
				
			||||
     */ | 
				
			||||
    public void setCellVo(Object value, Excel attr, Cell cell) | 
				
			||||
    { | 
				
			||||
        if (ColumnType.STRING == attr.cellType()) | 
				
			||||
        { | 
				
			||||
            cell.setCellType(CellType.NUMERIC); | 
				
			||||
            cell.setCellValue(StringUtils.isNull(value) ? attr.defaultValue() : value + attr.suffix()); | 
				
			||||
        } | 
				
			||||
        else if (ColumnType.NUMERIC == attr.cellType()) | 
				
			||||
        { | 
				
			||||
            cell.setCellType(CellType.NUMERIC); | 
				
			||||
            cell.setCellValue(Integer.parseInt(value + "")); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 创建表格样式 | 
				
			||||
     */ | 
				
			||||
    public void setDataValidation(Excel attr, Row row, int column) | 
				
			||||
    { | 
				
			||||
        if (attr.name().indexOf("注:") >= 0) | 
				
			||||
        { | 
				
			||||
            sheet.setColumnWidth(column, 6000); | 
				
			||||
        } | 
				
			||||
        else | 
				
			||||
        { | 
				
			||||
            // 设置列宽
 | 
				
			||||
            sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); | 
				
			||||
            row.setHeight((short) (attr.height() * 20)); | 
				
			||||
        } | 
				
			||||
        // 如果设置了提示信息则鼠标放上去提示.
 | 
				
			||||
        if (StringUtils.isNotEmpty(attr.prompt())) | 
				
			||||
        { | 
				
			||||
            // 这里默认设了2-101列提示.
 | 
				
			||||
            setXSSFPrompt(sheet, "", attr.prompt(), 1, 100, column, column); | 
				
			||||
        } | 
				
			||||
        // 如果设置了combo属性则本列只能选择不能输入
 | 
				
			||||
        if (attr.combo().length > 0) | 
				
			||||
        { | 
				
			||||
            // 这里默认设了2-101列只能选择不能输入.
 | 
				
			||||
            setXSSFValidation(sheet, attr.combo(), 1, 100, column, column); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 添加单元格 | 
				
			||||
     */ | 
				
			||||
    public Cell addCell(Excel attr, Row row, T vo, Field field, int column) | 
				
			||||
    { | 
				
			||||
        Cell cell = null; | 
				
			||||
        try | 
				
			||||
        { | 
				
			||||
            // 设置行高
 | 
				
			||||
            row.setHeight((short) (attr.height() * 20)); | 
				
			||||
            // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
 | 
				
			||||
            if (attr.isExport()) | 
				
			||||
            { | 
				
			||||
                // 创建cell
 | 
				
			||||
                cell = row.createCell(column); | 
				
			||||
                cell.setCellStyle(styles.get("data")); | 
				
			||||
 | 
				
			||||
                // 用于读取对象中的属性
 | 
				
			||||
                Object value = getTargetValue(vo, field, attr); | 
				
			||||
                String dateFormat = attr.dateFormat(); | 
				
			||||
                String readConverterExp = attr.readConverterExp(); | 
				
			||||
                if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) | 
				
			||||
                { | 
				
			||||
                    cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value)); | 
				
			||||
                } | 
				
			||||
                else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) | 
				
			||||
                { | 
				
			||||
                    cell.setCellValue(convertByExp(String.valueOf(value), readConverterExp)); | 
				
			||||
                } | 
				
			||||
                else | 
				
			||||
                { | 
				
			||||
                    // 设置列类型
 | 
				
			||||
                    setCellVo(value, attr, cell); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        catch (Exception e) | 
				
			||||
        { | 
				
			||||
            log.error("导出Excel失败{}", e); | 
				
			||||
        } | 
				
			||||
        return cell; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 设置 POI XSSFSheet 单元格提示 | 
				
			||||
     *  | 
				
			||||
     * @param sheet 表单 | 
				
			||||
     * @param promptTitle 提示标题 | 
				
			||||
     * @param promptContent 提示内容 | 
				
			||||
     * @param firstRow 开始行 | 
				
			||||
     * @param endRow 结束行 | 
				
			||||
     * @param firstCol 开始列 | 
				
			||||
     * @param endCol 结束列 | 
				
			||||
     */ | 
				
			||||
    public void setXSSFPrompt(Sheet sheet, String promptTitle, String promptContent, int firstRow, int endRow, | 
				
			||||
            int firstCol, int endCol) | 
				
			||||
    { | 
				
			||||
        DataValidationHelper helper = sheet.getDataValidationHelper(); | 
				
			||||
        DataValidationConstraint constraint = helper.createCustomConstraint("DD1"); | 
				
			||||
        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); | 
				
			||||
        DataValidation dataValidation = helper.createValidation(constraint, regions); | 
				
			||||
        dataValidation.createPromptBox(promptTitle, promptContent); | 
				
			||||
        dataValidation.setShowPromptBox(true); | 
				
			||||
        sheet.addValidationData(dataValidation); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 设置某些列的值只能输入预制的数据,显示下拉框. | 
				
			||||
     *  | 
				
			||||
     * @param sheet 要设置的sheet. | 
				
			||||
     * @param textlist 下拉框显示的内容 | 
				
			||||
     * @param firstRow 开始行 | 
				
			||||
     * @param endRow 结束行 | 
				
			||||
     * @param firstCol 开始列 | 
				
			||||
     * @param endCol 结束列 | 
				
			||||
     * @return 设置好的sheet. | 
				
			||||
     */ | 
				
			||||
    public void setXSSFValidation(Sheet sheet, String[] textlist, int firstRow, int endRow, int firstCol, int endCol) | 
				
			||||
    { | 
				
			||||
        DataValidationHelper helper = sheet.getDataValidationHelper(); | 
				
			||||
        // 加载下拉列表内容
 | 
				
			||||
        DataValidationConstraint constraint = helper.createExplicitListConstraint(textlist); | 
				
			||||
        // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
 | 
				
			||||
        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); | 
				
			||||
        // 数据有效性对象
 | 
				
			||||
        DataValidation dataValidation = helper.createValidation(constraint, regions); | 
				
			||||
        // 处理Excel兼容性问题
 | 
				
			||||
        if (dataValidation instanceof XSSFDataValidation) | 
				
			||||
        { | 
				
			||||
            dataValidation.setSuppressDropDownArrow(true); | 
				
			||||
            dataValidation.setShowErrorBox(true); | 
				
			||||
        } | 
				
			||||
        else | 
				
			||||
        { | 
				
			||||
            dataValidation.setSuppressDropDownArrow(false); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        sheet.addValidationData(dataValidation); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 解析导出值 0=男,1=女,2=未知 | 
				
			||||
     *  | 
				
			||||
     * @param propertyValue 参数值 | 
				
			||||
     * @param converterExp 翻译注解 | 
				
			||||
     * @return 解析后值 | 
				
			||||
     * @throws Exception | 
				
			||||
     */ | 
				
			||||
    public static String convertByExp(String propertyValue, String converterExp) throws Exception | 
				
			||||
    { | 
				
			||||
        try | 
				
			||||
        { | 
				
			||||
            String[] convertSource = converterExp.split(","); | 
				
			||||
            for (String item : convertSource) | 
				
			||||
            { | 
				
			||||
                String[] itemArray = item.split("="); | 
				
			||||
                if (itemArray[0].equals(propertyValue)) | 
				
			||||
                { | 
				
			||||
                    return itemArray[1]; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        catch (Exception e) | 
				
			||||
        { | 
				
			||||
            throw e; | 
				
			||||
        } | 
				
			||||
        return propertyValue; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 反向解析值 男=0,女=1,未知=2 | 
				
			||||
     *  | 
				
			||||
     * @param propertyValue 参数值 | 
				
			||||
     * @param converterExp 翻译注解 | 
				
			||||
     * @return 解析后值 | 
				
			||||
     * @throws Exception | 
				
			||||
     */ | 
				
			||||
    public static String reverseByExp(String propertyValue, String converterExp) throws Exception | 
				
			||||
    { | 
				
			||||
        try | 
				
			||||
        { | 
				
			||||
            String[] convertSource = converterExp.split(","); | 
				
			||||
            for (String item : convertSource) | 
				
			||||
            { | 
				
			||||
                String[] itemArray = item.split("="); | 
				
			||||
                if (itemArray[1].equals(propertyValue)) | 
				
			||||
                { | 
				
			||||
                    return itemArray[0]; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        catch (Exception e) | 
				
			||||
        { | 
				
			||||
            throw e; | 
				
			||||
        } | 
				
			||||
        return propertyValue; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 编码文件名 | 
				
			||||
     */ | 
				
			||||
    public String encodingFilename(String filename) | 
				
			||||
    { | 
				
			||||
        filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx"; | 
				
			||||
        return filename; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 获取下载路径 | 
				
			||||
     *  | 
				
			||||
     * @param filename 文件名称 | 
				
			||||
     */ | 
				
			||||
    public String getAbsoluteFile(String filename) | 
				
			||||
    { | 
				
			||||
        String downloadPath = RuoYiConfig.getDownloadPath() + filename; | 
				
			||||
        File desc = new File(downloadPath); | 
				
			||||
        if (!desc.getParentFile().exists()) | 
				
			||||
        { | 
				
			||||
            desc.getParentFile().mkdirs(); | 
				
			||||
        } | 
				
			||||
        return downloadPath; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 获取bean中的属性值 | 
				
			||||
     *  | 
				
			||||
     * @param vo 实体对象 | 
				
			||||
     * @param field 字段 | 
				
			||||
     * @param excel 注解 | 
				
			||||
     * @return 最终的属性值 | 
				
			||||
     * @throws Exception | 
				
			||||
     */ | 
				
			||||
    private Object getTargetValue(T vo, Field field, Excel excel) throws Exception | 
				
			||||
    { | 
				
			||||
        Object o = field.get(vo); | 
				
			||||
        if (StringUtils.isNotEmpty(excel.targetAttr())) | 
				
			||||
        { | 
				
			||||
            String target = excel.targetAttr(); | 
				
			||||
            if (target.indexOf(".") > -1) | 
				
			||||
            { | 
				
			||||
                String[] targets = target.split("[.]"); | 
				
			||||
                for (String name : targets) | 
				
			||||
                { | 
				
			||||
                    o = getValue(o, name); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            else | 
				
			||||
            { | 
				
			||||
                o = getValue(o, target); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        return o; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 以类的属性的get方法方法形式获取值 | 
				
			||||
     *  | 
				
			||||
     * @param o | 
				
			||||
     * @param name | 
				
			||||
     * @return value | 
				
			||||
     * @throws Exception | 
				
			||||
     */ | 
				
			||||
    private Object getValue(Object o, String name) throws Exception | 
				
			||||
    { | 
				
			||||
        if (StringUtils.isNotEmpty(name)) | 
				
			||||
        { | 
				
			||||
            Class<?> clazz = o.getClass(); | 
				
			||||
            String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1); | 
				
			||||
            Method method = clazz.getMethod(methodName); | 
				
			||||
            o = method.invoke(o); | 
				
			||||
        } | 
				
			||||
        return o; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 得到所有定义字段 | 
				
			||||
     */ | 
				
			||||
    private void createExcelField() | 
				
			||||
    { | 
				
			||||
        this.fields = new ArrayList<Object[]>(); | 
				
			||||
        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)) | 
				
			||||
            { | 
				
			||||
                putToField(field, field.getAnnotation(Excel.class)); | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            // 多注解
 | 
				
			||||
            if (field.isAnnotationPresent(Excels.class)) | 
				
			||||
            { | 
				
			||||
                Excels attrs = field.getAnnotation(Excels.class); | 
				
			||||
                Excel[] excels = attrs.value(); | 
				
			||||
                for (Excel excel : excels) | 
				
			||||
                { | 
				
			||||
                    putToField(field, excel); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 放到字段集合中 | 
				
			||||
     */ | 
				
			||||
    private void putToField(Field field, Excel attr) | 
				
			||||
    { | 
				
			||||
        if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) | 
				
			||||
        { | 
				
			||||
            this.fields.add(new Object[] { field, attr }); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 创建一个工作簿 | 
				
			||||
     */ | 
				
			||||
    public void createWorkbook() | 
				
			||||
    { | 
				
			||||
        this.wb = new SXSSFWorkbook(500); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 创建工作表 | 
				
			||||
     *  | 
				
			||||
     * @param sheetNo sheet数量 | 
				
			||||
     * @param index 序号 | 
				
			||||
     */ | 
				
			||||
    public void createSheet(double sheetNo, int index) | 
				
			||||
    { | 
				
			||||
        this.sheet = wb.createSheet(); | 
				
			||||
        this.styles = createStyles(wb); | 
				
			||||
        // 设置工作表的名称.
 | 
				
			||||
        if (sheetNo == 0) | 
				
			||||
        { | 
				
			||||
            wb.setSheetName(index, sheetName); | 
				
			||||
        } | 
				
			||||
        else | 
				
			||||
        { | 
				
			||||
            wb.setSheetName(index, sheetName + index); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 获取单元格值 | 
				
			||||
     *  | 
				
			||||
     * @param row 获取的行 | 
				
			||||
     * @param column 获取单元格列号 | 
				
			||||
     * @return 单元格值 | 
				
			||||
     */ | 
				
			||||
    public Object getCellValue(Row row, int column) | 
				
			||||
    { | 
				
			||||
        if (row == null) | 
				
			||||
        { | 
				
			||||
            return row; | 
				
			||||
        } | 
				
			||||
        Object val = ""; | 
				
			||||
        try | 
				
			||||
        { | 
				
			||||
            Cell cell = row.getCell(column); | 
				
			||||
            if (cell != null) | 
				
			||||
            { | 
				
			||||
                if (cell.getCellTypeEnum() == CellType.NUMERIC || cell.getCellTypeEnum() == CellType.FORMULA) | 
				
			||||
                { | 
				
			||||
                    val = cell.getNumericCellValue(); | 
				
			||||
                    if (HSSFDateUtil.isCellDateFormatted(cell)) | 
				
			||||
                    { | 
				
			||||
                        val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
 | 
				
			||||
                    } | 
				
			||||
                    else | 
				
			||||
                    { | 
				
			||||
                        if ((Double) val % 1 > 0) | 
				
			||||
                        { | 
				
			||||
                            val = new DecimalFormat("0.00").format(val); | 
				
			||||
                        } | 
				
			||||
                        else | 
				
			||||
                        { | 
				
			||||
                            val = new DecimalFormat("0").format(val); | 
				
			||||
                        } | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
                else if (cell.getCellTypeEnum() == CellType.STRING) | 
				
			||||
                { | 
				
			||||
                    val = cell.getStringCellValue(); | 
				
			||||
                } | 
				
			||||
                else if (cell.getCellTypeEnum() == CellType.BOOLEAN) | 
				
			||||
                { | 
				
			||||
                    val = cell.getBooleanCellValue(); | 
				
			||||
                } | 
				
			||||
                else if (cell.getCellTypeEnum() == CellType.ERROR) | 
				
			||||
                { | 
				
			||||
                    val = cell.getErrorCellValue(); | 
				
			||||
                } | 
				
			||||
 | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        catch (Exception e) | 
				
			||||
        { | 
				
			||||
            return val; | 
				
			||||
        } | 
				
			||||
        return val; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,406 @@
				@@ -0,0 +1,406 @@
					 | 
				
			||||
package com.ruoyi.common.utils.reflect; | 
				
			||||
 | 
				
			||||
import java.lang.reflect.Field; | 
				
			||||
import java.lang.reflect.InvocationTargetException; | 
				
			||||
import java.lang.reflect.Method; | 
				
			||||
import java.lang.reflect.Modifier; | 
				
			||||
import java.lang.reflect.ParameterizedType; | 
				
			||||
import java.lang.reflect.Type; | 
				
			||||
import java.util.Date; | 
				
			||||
import org.apache.commons.lang3.StringUtils; | 
				
			||||
import org.apache.commons.lang3.Validate; | 
				
			||||
import org.apache.poi.ss.usermodel.DateUtil; | 
				
			||||
import org.slf4j.Logger; | 
				
			||||
import org.slf4j.LoggerFactory; | 
				
			||||
import com.ruoyi.common.core.text.Convert; | 
				
			||||
import com.ruoyi.common.utils.DateUtils; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. | 
				
			||||
 *  | 
				
			||||
 * @author ruoyi | 
				
			||||
 */ | 
				
			||||
@SuppressWarnings("rawtypes") | 
				
			||||
public class ReflectUtils | 
				
			||||
{ | 
				
			||||
    private static final String SETTER_PREFIX = "set"; | 
				
			||||
 | 
				
			||||
    private static final String GETTER_PREFIX = "get"; | 
				
			||||
 | 
				
			||||
    private static final String CGLIB_CLASS_SEPARATOR = "$$"; | 
				
			||||
 | 
				
			||||
    private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 调用Getter方法. | 
				
			||||
     * 支持多级,如:对象名.对象名.方法 | 
				
			||||
     */ | 
				
			||||
    @SuppressWarnings("unchecked") | 
				
			||||
    public static <E> E invokeGetter(Object obj, String propertyName) | 
				
			||||
    { | 
				
			||||
        Object object = obj; | 
				
			||||
        for (String name : StringUtils.split(propertyName, ".")) | 
				
			||||
        { | 
				
			||||
            String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); | 
				
			||||
            object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); | 
				
			||||
        } | 
				
			||||
        return (E) object; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 调用Setter方法, 仅匹配方法名。 | 
				
			||||
     * 支持多级,如:对象名.对象名.方法 | 
				
			||||
     */ | 
				
			||||
    public static <E> void invokeSetter(Object obj, String propertyName, E value) | 
				
			||||
    { | 
				
			||||
        Object object = obj; | 
				
			||||
        String[] names = StringUtils.split(propertyName, "."); | 
				
			||||
        for (int i = 0; i < names.length; i++) | 
				
			||||
        { | 
				
			||||
            if (i < names.length - 1) | 
				
			||||
            { | 
				
			||||
                String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); | 
				
			||||
                object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); | 
				
			||||
            } | 
				
			||||
            else | 
				
			||||
            { | 
				
			||||
                String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); | 
				
			||||
                invokeMethodByName(object, setterMethodName, new Object[] { value }); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. | 
				
			||||
     */ | 
				
			||||
    @SuppressWarnings("unchecked") | 
				
			||||
    public static <E> E getFieldValue(final Object obj, final String fieldName) | 
				
			||||
    { | 
				
			||||
        Field field = getAccessibleField(obj, fieldName); | 
				
			||||
        if (field == null) | 
				
			||||
        { | 
				
			||||
            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); | 
				
			||||
            return null; | 
				
			||||
        } | 
				
			||||
        E result = null; | 
				
			||||
        try | 
				
			||||
        { | 
				
			||||
            result = (E) field.get(obj); | 
				
			||||
        } | 
				
			||||
        catch (IllegalAccessException e) | 
				
			||||
        { | 
				
			||||
            logger.error("不可能抛出的异常{}", e.getMessage()); | 
				
			||||
        } | 
				
			||||
        return result; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. | 
				
			||||
     */ | 
				
			||||
    public static <E> void setFieldValue(final Object obj, final String fieldName, final E value) | 
				
			||||
    { | 
				
			||||
        Field field = getAccessibleField(obj, fieldName); | 
				
			||||
        if (field == null) | 
				
			||||
        { | 
				
			||||
            // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
 | 
				
			||||
            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); | 
				
			||||
            return; | 
				
			||||
        } | 
				
			||||
        try | 
				
			||||
        { | 
				
			||||
            field.set(obj, value); | 
				
			||||
        } | 
				
			||||
        catch (IllegalAccessException e) | 
				
			||||
        { | 
				
			||||
            logger.error("不可能抛出的异常: {}", e.getMessage()); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 直接调用对象方法, 无视private/protected修饰符. | 
				
			||||
     * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. | 
				
			||||
     * 同时匹配方法名+参数类型, | 
				
			||||
     */ | 
				
			||||
    @SuppressWarnings("unchecked") | 
				
			||||
    public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes, | 
				
			||||
            final Object[] args) | 
				
			||||
    { | 
				
			||||
        if (obj == null || methodName == null) | 
				
			||||
        { | 
				
			||||
            return null; | 
				
			||||
        } | 
				
			||||
        Method method = getAccessibleMethod(obj, methodName, parameterTypes); | 
				
			||||
        if (method == null) | 
				
			||||
        { | 
				
			||||
            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); | 
				
			||||
            return null; | 
				
			||||
        } | 
				
			||||
        try | 
				
			||||
        { | 
				
			||||
            return (E) method.invoke(obj, args); | 
				
			||||
        } | 
				
			||||
        catch (Exception e) | 
				
			||||
        { | 
				
			||||
            String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; | 
				
			||||
            throw convertReflectionExceptionToUnchecked(msg, e); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 直接调用对象方法, 无视private/protected修饰符, | 
				
			||||
     * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. | 
				
			||||
     * 只匹配函数名,如果有多个同名函数调用第一个。 | 
				
			||||
     */ | 
				
			||||
    @SuppressWarnings("unchecked") | 
				
			||||
    public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args) | 
				
			||||
    { | 
				
			||||
        Method method = getAccessibleMethodByName(obj, methodName, args.length); | 
				
			||||
        if (method == null) | 
				
			||||
        { | 
				
			||||
            // 如果为空不报错,直接返回空。
 | 
				
			||||
            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); | 
				
			||||
            return null; | 
				
			||||
        } | 
				
			||||
        try | 
				
			||||
        { | 
				
			||||
            // 类型转换(将参数数据类型转换为目标方法参数类型)
 | 
				
			||||
            Class<?>[] cs = method.getParameterTypes(); | 
				
			||||
            for (int i = 0; i < cs.length; i++) | 
				
			||||
            { | 
				
			||||
                if (args[i] != null && !args[i].getClass().equals(cs[i])) | 
				
			||||
                { | 
				
			||||
                    if (cs[i] == String.class) | 
				
			||||
                    { | 
				
			||||
                        args[i] = Convert.toStr(args[i]); | 
				
			||||
                        if (StringUtils.endsWith((String) args[i], ".0")) | 
				
			||||
                        { | 
				
			||||
                            args[i] = StringUtils.substringBefore((String) args[i], ".0"); | 
				
			||||
                        } | 
				
			||||
                    } | 
				
			||||
                    else if (cs[i] == Integer.class) | 
				
			||||
                    { | 
				
			||||
                        args[i] = Convert.toInt(args[i]); | 
				
			||||
                    } | 
				
			||||
                    else if (cs[i] == Long.class) | 
				
			||||
                    { | 
				
			||||
                        args[i] = Convert.toLong(args[i]); | 
				
			||||
                    } | 
				
			||||
                    else if (cs[i] == Double.class) | 
				
			||||
                    { | 
				
			||||
                        args[i] = Convert.toDouble(args[i]); | 
				
			||||
                    } | 
				
			||||
                    else if (cs[i] == Float.class) | 
				
			||||
                    { | 
				
			||||
                        args[i] = Convert.toFloat(args[i]); | 
				
			||||
                    } | 
				
			||||
                    else if (cs[i] == Date.class) | 
				
			||||
                    { | 
				
			||||
                        if (args[i] instanceof String) | 
				
			||||
                        { | 
				
			||||
                            args[i] = DateUtils.parseDate(args[i]); | 
				
			||||
                        } | 
				
			||||
                        else | 
				
			||||
                        { | 
				
			||||
                            args[i] = DateUtil.getJavaDate((Double) args[i]); | 
				
			||||
                        } | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            return (E) method.invoke(obj, args); | 
				
			||||
        } | 
				
			||||
        catch (Exception e) | 
				
			||||
        { | 
				
			||||
            String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; | 
				
			||||
            throw convertReflectionExceptionToUnchecked(msg, e); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. | 
				
			||||
     * 如向上转型到Object仍无法找到, 返回null. | 
				
			||||
     */ | 
				
			||||
    public static Field getAccessibleField(final Object obj, final String fieldName) | 
				
			||||
    { | 
				
			||||
        // 为空不报错。直接返回 null
 | 
				
			||||
        if (obj == null) | 
				
			||||
        { | 
				
			||||
            return null; | 
				
			||||
        } | 
				
			||||
        Validate.notBlank(fieldName, "fieldName can't be blank"); | 
				
			||||
        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) | 
				
			||||
        { | 
				
			||||
            try | 
				
			||||
            { | 
				
			||||
                Field field = superClass.getDeclaredField(fieldName); | 
				
			||||
                makeAccessible(field); | 
				
			||||
                return field; | 
				
			||||
            } | 
				
			||||
            catch (NoSuchFieldException e) | 
				
			||||
            { | 
				
			||||
                continue; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        return null; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. | 
				
			||||
     * 如向上转型到Object仍无法找到, 返回null. | 
				
			||||
     * 匹配函数名+参数类型。 | 
				
			||||
     * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) | 
				
			||||
     */ | 
				
			||||
    public static Method getAccessibleMethod(final Object obj, final String methodName, | 
				
			||||
            final Class<?>... parameterTypes) | 
				
			||||
    { | 
				
			||||
        // 为空不报错。直接返回 null
 | 
				
			||||
        if (obj == null) | 
				
			||||
        { | 
				
			||||
            return null; | 
				
			||||
        } | 
				
			||||
        Validate.notBlank(methodName, "methodName can't be blank"); | 
				
			||||
        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) | 
				
			||||
        { | 
				
			||||
            try | 
				
			||||
            { | 
				
			||||
                Method method = searchType.getDeclaredMethod(methodName, parameterTypes); | 
				
			||||
                makeAccessible(method); | 
				
			||||
                return method; | 
				
			||||
            } | 
				
			||||
            catch (NoSuchMethodException e) | 
				
			||||
            { | 
				
			||||
                continue; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        return null; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. | 
				
			||||
     * 如向上转型到Object仍无法找到, 返回null. | 
				
			||||
     * 只匹配函数名。 | 
				
			||||
     * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) | 
				
			||||
     */ | 
				
			||||
    public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) | 
				
			||||
    { | 
				
			||||
        // 为空不报错。直接返回 null
 | 
				
			||||
        if (obj == null) | 
				
			||||
        { | 
				
			||||
            return null; | 
				
			||||
        } | 
				
			||||
        Validate.notBlank(methodName, "methodName can't be blank"); | 
				
			||||
        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) | 
				
			||||
        { | 
				
			||||
            Method[] methods = searchType.getDeclaredMethods(); | 
				
			||||
            for (Method method : methods) | 
				
			||||
            { | 
				
			||||
                if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) | 
				
			||||
                { | 
				
			||||
                    makeAccessible(method); | 
				
			||||
                    return method; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        return null; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 | 
				
			||||
     */ | 
				
			||||
    public static void makeAccessible(Method method) | 
				
			||||
    { | 
				
			||||
        if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) | 
				
			||||
                && !method.isAccessible()) | 
				
			||||
        { | 
				
			||||
            method.setAccessible(true); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 | 
				
			||||
     */ | 
				
			||||
    public static void makeAccessible(Field field) | 
				
			||||
    { | 
				
			||||
        if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) | 
				
			||||
                || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) | 
				
			||||
        { | 
				
			||||
            field.setAccessible(true); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 | 
				
			||||
     * 如无法找到, 返回Object.class. | 
				
			||||
     */ | 
				
			||||
    @SuppressWarnings("unchecked") | 
				
			||||
    public static <T> Class<T> getClassGenricType(final Class clazz) | 
				
			||||
    { | 
				
			||||
        return getClassGenricType(clazz, 0); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. | 
				
			||||
     * 如无法找到, 返回Object.class. | 
				
			||||
     */ | 
				
			||||
    public static Class getClassGenricType(final Class clazz, final int index) | 
				
			||||
    { | 
				
			||||
        Type genType = clazz.getGenericSuperclass(); | 
				
			||||
 | 
				
			||||
        if (!(genType instanceof ParameterizedType)) | 
				
			||||
        { | 
				
			||||
            logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); | 
				
			||||
            return Object.class; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); | 
				
			||||
 | 
				
			||||
        if (index >= params.length || index < 0) | 
				
			||||
        { | 
				
			||||
            logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " | 
				
			||||
                    + params.length); | 
				
			||||
            return Object.class; | 
				
			||||
        } | 
				
			||||
        if (!(params[index] instanceof Class)) | 
				
			||||
        { | 
				
			||||
            logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); | 
				
			||||
            return Object.class; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        return (Class) params[index]; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static Class<?> getUserClass(Object instance) | 
				
			||||
    { | 
				
			||||
        if (instance == null) | 
				
			||||
        { | 
				
			||||
            throw new RuntimeException("Instance must not be null"); | 
				
			||||
        } | 
				
			||||
        Class clazz = instance.getClass(); | 
				
			||||
        if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) | 
				
			||||
        { | 
				
			||||
            Class<?> superClass = clazz.getSuperclass(); | 
				
			||||
            if (superClass != null && !Object.class.equals(superClass)) | 
				
			||||
            { | 
				
			||||
                return superClass; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        return clazz; | 
				
			||||
 | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 将反射时的checked exception转换为unchecked exception. | 
				
			||||
     */ | 
				
			||||
    public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) | 
				
			||||
    { | 
				
			||||
        if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException | 
				
			||||
                || e instanceof NoSuchMethodException) | 
				
			||||
        { | 
				
			||||
            return new IllegalArgumentException(msg, e); | 
				
			||||
        } | 
				
			||||
        else if (e instanceof InvocationTargetException) | 
				
			||||
        { | 
				
			||||
            return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); | 
				
			||||
        } | 
				
			||||
        return new RuntimeException(msg, e); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,113 @@
				@@ -0,0 +1,113 @@
					 | 
				
			||||
package com.ruoyi.framework.aspectj.lang.annotation; | 
				
			||||
 | 
				
			||||
import java.lang.annotation.ElementType; | 
				
			||||
import java.lang.annotation.Retention; | 
				
			||||
import java.lang.annotation.RetentionPolicy; | 
				
			||||
import java.lang.annotation.Target; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * 自定义导出Excel数据注解 | 
				
			||||
 *  | 
				
			||||
 * @author ruoyi | 
				
			||||
 */ | 
				
			||||
@Retention(RetentionPolicy.RUNTIME) | 
				
			||||
@Target(ElementType.FIELD) | 
				
			||||
public @interface Excel | 
				
			||||
{ | 
				
			||||
    /** | 
				
			||||
     * 导出到Excel中的名字. | 
				
			||||
     */ | 
				
			||||
    public String name() default ""; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 日期格式, 如: yyyy-MM-dd | 
				
			||||
     */ | 
				
			||||
    public String dateFormat() default ""; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 读取内容转表达式 (如: 0=男,1=女,2=未知) | 
				
			||||
     */ | 
				
			||||
    public String readConverterExp() default ""; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 导出类型(0数字 1字符串) | 
				
			||||
     */ | 
				
			||||
    public ColumnType cellType() default ColumnType.STRING; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 导出时在excel中每个列的高度 单位为字符 | 
				
			||||
     */ | 
				
			||||
    public double height() default 14; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 导出时在excel中每个列的宽 单位为字符 | 
				
			||||
     */ | 
				
			||||
    public double width() default 16; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 文字后缀,如% 90 变成90% | 
				
			||||
     */ | 
				
			||||
    public String suffix() default ""; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 当值为空时,字段的默认值 | 
				
			||||
     */ | 
				
			||||
    public String defaultValue() default ""; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 提示信息 | 
				
			||||
     */ | 
				
			||||
    public String prompt() default ""; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 设置只能选择不能输入的列内容. | 
				
			||||
     */ | 
				
			||||
    public String[] combo() default {}; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. | 
				
			||||
     */ | 
				
			||||
    public boolean isExport() default true; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 另一个类中的属性名称,支持多级获取,以小数点隔开 | 
				
			||||
     */ | 
				
			||||
    public String targetAttr() default ""; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 字段类型(0:导出导入;1:仅导出;2:仅导入) | 
				
			||||
     */ | 
				
			||||
    Type type() default Type.ALL; | 
				
			||||
 | 
				
			||||
    public enum Type | 
				
			||||
    { | 
				
			||||
        ALL(0), EXPORT(1), IMPORT(2); | 
				
			||||
        private final int value; | 
				
			||||
 | 
				
			||||
        Type(int value) | 
				
			||||
        { | 
				
			||||
            this.value = value; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        public int value() | 
				
			||||
        { | 
				
			||||
            return this.value; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public enum ColumnType | 
				
			||||
    { | 
				
			||||
        NUMERIC(0), STRING(1); | 
				
			||||
        private final int value; | 
				
			||||
 | 
				
			||||
        ColumnType(int value) | 
				
			||||
        { | 
				
			||||
            this.value = value; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        public int value() | 
				
			||||
        { | 
				
			||||
            return this.value; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,18 @@
				@@ -0,0 +1,18 @@
					 | 
				
			||||
package com.ruoyi.framework.aspectj.lang.annotation; | 
				
			||||
 | 
				
			||||
import java.lang.annotation.ElementType; | 
				
			||||
import java.lang.annotation.Retention; | 
				
			||||
import java.lang.annotation.RetentionPolicy; | 
				
			||||
import java.lang.annotation.Target; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Excel注解集 | 
				
			||||
 *  | 
				
			||||
 * @author ruoyi | 
				
			||||
 */ | 
				
			||||
@Target(ElementType.FIELD) | 
				
			||||
@Retention(RetentionPolicy.RUNTIME) | 
				
			||||
public @interface Excels | 
				
			||||
{ | 
				
			||||
    Excel[] value(); | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue