疯狂的狮子li
2021-11-26 c00e3974050ff609fb34463d70292e768a786fea
update 抽象 Excel 导入支持自定义监听器
已添加3个文件
已修改4个文件
已删除2个文件
607 ■■■■ 文件已修改
ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/excel/DefautExcelResult.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelResult.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelListener.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelResult.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java 220 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoImportVo.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/demo/demo/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,101 @@
package com.ruoyi.common.excel;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.utils.ValidatorUtils;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
 * Excel å¯¼å…¥ç›‘听
 *
 * @author Yjoioooo
 * @author Lion Li
 */
@Slf4j
@NoArgsConstructor
public class DefaultExcelListener<T> extends AnalysisEventListener<T> {
    /**
     * æ˜¯å¦Validator检验,默认为是
     */
    private Boolean isValidate = Boolean.TRUE;
    /**
     * å¯¼å…¥å›žæ‰§
     */
    private ExcelResult<T> excelResult;
    public DefaultExcelListener(boolean isValidate) {
        this.excelResult = new DefautExcelResult<>();
        this.isValidate = isValidate;
    }
    /**
     * å¤„理异常
     *
     * @param exception ExcelDataConvertException
     * @param context   Excel ä¸Šä¸‹æ–‡
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        String errMsg = null;
        if (exception instanceof ExcelDataConvertException) {
            // å¦‚果是某一个单元格的转换异常 èƒ½èŽ·å–åˆ°å…·ä½“è¡Œå·
            // å¦‚果要获取头的信息 é…åˆdoAfterAllAnalysedHeadMap使用
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
            errMsg = StrUtil.format("第{}行-第{}列解析异常<br/>",
                excelDataConvertException.getRowIndex() + 1,
                excelDataConvertException.getColumnIndex() + 1);
            if (log.isDebugEnabled()) {
                log.error(errMsg);
            }
        }
        if (exception instanceof ConstraintViolationException) {
            ConstraintViolationException constraintViolationException = (ConstraintViolationException) exception;
            Set<ConstraintViolation<?>> constraintViolations = constraintViolationException.getConstraintViolations();
            String constraintViolationsMsg = constraintViolations.stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.joining(", "));
            errMsg = StrUtil.format("第{}行数据校验异常: {}", context.readRowHolder().getRowIndex() + 1, constraintViolationsMsg);
            if (log.isDebugEnabled()) {
                log.error(errMsg);
            }
        }
        excelResult.getErrorList().add(errMsg);
        throw new ExcelAnalysisException(errMsg);
    }
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        log.debug("解析到一条头数据: {}", JSON.toJSONString(headMap));
    }
    @Override
    public void invoke(T data, AnalysisContext context) {
        if (isValidate) {
            ValidatorUtils.validate(data);
        }
        excelResult.getList().add(data);
    }
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.debug("所有数据解析完成!");
    }
    public ExcelResult<T> getExcelResult() {
        return excelResult;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/excel/DefautExcelResult.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,78 @@
package com.ruoyi.common.excel;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
/**
 * é»˜è®¤excel返回对象
 *
 * @author Yjoioooo
 * @author Lion Li
 */
public class DefautExcelResult<T> implements ExcelResult<T> {
    /**
     * æ•°æ®å¯¹è±¡list
     */
    @Setter
    private List<T> list;
    /**
     * é”™è¯¯ä¿¡æ¯åˆ—表
     */
    @Setter
    private List<String> errorList;
    public DefautExcelResult() {
        this.list = new ArrayList<>();
        this.errorList = new ArrayList<>();
    }
    public DefautExcelResult(List<T> list, List<String> errorList) {
        this.list = list;
        this.errorList = errorList;
    }
    public DefautExcelResult(ExcelResult<T> excelResult) {
        this.list = excelResult.getList();
        this.errorList = excelResult.getErrorList();
    }
    @Override
    public List<T> getList() {
        return list;
    }
    @Override
    public List<String> getErrorList() {
        return errorList;
    }
    /**
     * èŽ·å–å¯¼å…¥å›žæ‰§
     *
     * @return å¯¼å…¥å›žæ‰§
     */
    @Override
    public String getAnalysis() {
        int successCount = list.size();
        int errorCount = errorList.size();
        if (successCount == 0) {
            return "读取失败,未解析到数据";
        } else {
            if (errorList.size() == 0) {
                return StrUtil.format("恭喜您,全部读取成功!共{}条", successCount);
            } else {
                return StrUtil.format("部分读取成功,其中成功{}条,失败{}条,错误信息如下:<br/>{}",
                    successCount,
                    errorCount,
                    CollUtil.join(errorList, "<br/>"));
            }
        }
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelResult.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package com.ruoyi.common.excel;
import java.util.List;
/**
 * excel返回对象
 *
 * @author Lion Li
 */
public interface ExcelResult<T> {
    /**
     * å¯¹è±¡åˆ—表
     */
    List<T> getList();
    /**
     * é”™è¯¯åˆ—表
     */
    List<String> getErrorList();
    /**
     * å¯¼å…¥å›žæ‰§
     */
    String getAnalysis();
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelListener.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelResult.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
@@ -4,9 +4,10 @@
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.ruoyi.common.convert.ExcelBigNumberConvert;
import com.ruoyi.common.excel.DefaultExcelListener;
import com.ruoyi.common.excel.ExcelResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import org.apache.poi.ss.formula.functions.T;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
@@ -21,118 +22,133 @@
 */
public class ExcelUtil {
    /**
     * å¯¹excel表单默认第一个索引名转换成list(EasyExcel)
     *
     * @param is è¾“入流
     * @return è½¬æ¢åŽé›†åˆ
     */
    public static <T> List<T> importExcel(InputStream is, Class<T> clazz) {
        return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync();
    }
    /**
     * å¯¹excel表单默认第一个索引名转换成list(EasyExcel)
     * åŒæ­¥å¯¼å…¥
     *
     * @param is è¾“入流
     * @return è½¬æ¢åŽé›†åˆ
     */
    public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, boolean isValidate, boolean skipException) {
        ExcelListener<T> listener = new ExcelListener<>(isValidate, skipException);
    public static <T> List<T> importExcel(InputStream is, Class<T> clazz) {
        return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync();
    }
    /**
     * ä½¿ç”¨æ ¡éªŒç›‘听器处理导入
     *
     * @param is            è¾“入流
     * @param clazz         å¯¹è±¡ç±»åž‹
     * @param isValidate    æ˜¯å¦ Validator æ£€éªŒ é»˜è®¤ä¸ºæ˜¯
     * @return è½¬æ¢åŽé›†åˆ
     */
    public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, boolean isValidate) {
        DefaultExcelListener<T> listener = new DefaultExcelListener<>(isValidate);
        EasyExcel.read(is, clazz, listener).sheet().doRead();
        return listener.getExcelResult();
    }
    /**
     * å¯¹list数据源将其里面的数据导入到excel表单(EasyExcel)
     *
     * @param list      å¯¼å‡ºæ•°æ®é›†åˆ
     * @param sheetName å·¥ä½œè¡¨çš„名称
     * @return ç»“æžœ
     */
    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {
        try {
            String filename = encodingFilename(sheetName);
            response.reset();
            FileUtils.setAttachmentResponseHeader(response, filename);
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
            ServletOutputStream os = response.getOutputStream();
            EasyExcel.write(os, clazz)
                .autoCloseStream(false)
                // è‡ªåŠ¨é€‚é…
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                // å¤§æ•°å€¼è‡ªåŠ¨è½¬æ¢ é˜²æ­¢å¤±çœŸ
                .registerConverter(new ExcelBigNumberConvert())
                .sheet(sheetName).doWrite(list);
        } catch (IOException e) {
            throw new RuntimeException("导出Excel异常");
        }
    }
    /**
     * ä½¿ç”¨è‡ªå®šä¹‰ç›‘听器导入
     *
     * @param is            è¾“入流
     * @param clazz         å¯¹è±¡ç±»åž‹
     * @param readListener  è‡ªå®šä¹‰ç›‘听器
     * @return è½¬æ¢åŽé›†åˆ
     */
    public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, DefaultExcelListener<T> readListener) {
        EasyExcel.read(is, clazz, readListener).sheet().doRead();
        return readListener.getExcelResult();
    }
    /**
     * è§£æžå¯¼å‡ºå€¼ 0=男,1=女,2=未知
     *
     * @param propertyValue å‚数值
     * @param converterExp  ç¿»è¯‘注解
     * @param separator     åˆ†éš”符
     * @return è§£æžåŽå€¼
     */
    public static String convertByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(separator, propertyValue)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[0].equals(value)) {
                        propertyString.append(itemArray[1] + separator);
                        break;
                    }
                }
            } else {
                if (itemArray[0].equals(propertyValue)) {
                    return itemArray[1];
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }
    /**
     * å¯¼å‡ºexcel
     *
     * @param list      å¯¼å‡ºæ•°æ®é›†åˆ
     * @param sheetName å·¥ä½œè¡¨çš„名称
     * @return ç»“æžœ
     */
    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {
        try {
            String filename = encodingFilename(sheetName);
            response.reset();
            FileUtils.setAttachmentResponseHeader(response, filename);
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
            ServletOutputStream os = response.getOutputStream();
            EasyExcel.write(os, clazz)
                .autoCloseStream(false)
                // è‡ªåŠ¨é€‚é…
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                // å¤§æ•°å€¼è‡ªåŠ¨è½¬æ¢ é˜²æ­¢å¤±çœŸ
                .registerConverter(new ExcelBigNumberConvert())
                .sheet(sheetName).doWrite(list);
        } catch (IOException e) {
            throw new RuntimeException("导出Excel异常");
        }
    }
    /**
     * åå‘解析值 ç”·=0,女=1,未知=2
     *
     * @param propertyValue å‚数值
     * @param converterExp  ç¿»è¯‘注解
     * @param separator     åˆ†éš”符
     * @return è§£æžåŽå€¼
     */
    public static String reverseByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(separator, propertyValue)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[1].equals(value)) {
                        propertyString.append(itemArray[0] + separator);
                        break;
                    }
                }
            } else {
                if (itemArray[1].equals(propertyValue)) {
                    return itemArray[0];
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }
    /**
     * è§£æžå¯¼å‡ºå€¼ 0=男,1=女,2=未知
     *
     * @param propertyValue å‚数值
     * @param converterExp  ç¿»è¯‘注解
     * @param separator     åˆ†éš”符
     * @return è§£æžåŽå€¼
     */
    public static String convertByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(separator, propertyValue)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[0].equals(value)) {
                        propertyString.append(itemArray[1] + separator);
                        break;
                    }
                }
            } else {
                if (itemArray[0].equals(propertyValue)) {
                    return itemArray[1];
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }
    /**
     * ç¼–码文件名
     */
    public static String encodingFilename(String filename) {
        return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx";
    }
    /**
     * åå‘解析值 ç”·=0,女=1,未知=2
     *
     * @param propertyValue å‚数值
     * @param converterExp  ç¿»è¯‘注解
     * @param separator     åˆ†éš”符
     * @return è§£æžåŽå€¼
     */
    public static String reverseByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(separator, propertyValue)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[1].equals(value)) {
                        propertyString.append(itemArray[0] + separator);
                        break;
                    }
                }
            } else {
                if (itemArray[1].equals(propertyValue)) {
                    return itemArray[0];
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }
    /**
     * ç¼–码文件名
     */
    public static String encodingFilename(String filename) {
        return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx";
    }
}
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
@@ -5,14 +5,13 @@
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.core.validate.QueryGroup;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.ValidatorUtils;
import com.ruoyi.common.utils.poi.ExcelResult;
import com.ruoyi.common.excel.ExcelResult;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.domain.bo.TestDemoBo;
@@ -69,7 +68,7 @@
        return iTestDemoService.customPageList(bo);
    }
    @ApiOperation("导入测试单表")
    @ApiOperation("导入测试-校验")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "file", value = "导入文件", dataType = "java.io.File", required = true),
    })
@@ -77,7 +76,7 @@
    @PreAuthorize("@ss.hasPermi('demo:demo:import')")
    @PostMapping("/importData")
    public AjaxResult<Void> importData(@RequestPart("file") MultipartFile file) throws Exception {
        ExcelResult<TestDemoImportVo> excelResult = ExcelUtil.importExcel(file.getInputStream(), TestDemoImportVo.class, true, true);
        ExcelResult<TestDemoImportVo> excelResult = ExcelUtil.importExcel(file.getInputStream(), TestDemoImportVo.class, true);
        List<TestDemoImportVo> volist = excelResult.getList();
        List<TestDemo> list = BeanUtil.copyToList(volist, TestDemo.class);
        iTestDemoService.saveAll(list);
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoImportVo.java
@@ -1,13 +1,9 @@
package com.ruoyi.demo.domain.bo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@@ -18,16 +14,14 @@
 * @author Lion Li
 * @date 2021-07-26
 */
@Data
@ApiModel("测试单表业务对象")
public class TestDemoImportVo {
    /**
     * éƒ¨é—¨id
     */
    @ApiModelProperty("部门id")
    @ApiModelProperty("部门id")
    @NotNull(message = "部门id不能为空")
    @ExcelProperty(value = "部门id")
    private Long deptId;
@@ -35,7 +29,7 @@
    /**
     * ç”¨æˆ·id
     */
    @ApiModelProperty("用户id")
    @ApiModelProperty("用户id")
    @NotNull(message = "用户id不能为空")
    @ExcelProperty(value = "用户id")
    private Long userId;
@@ -43,7 +37,7 @@
    /**
     * æŽ’序号
     */
    @ApiModelProperty("排序号")
    @ApiModelProperty("排序号")
    @NotNull(message = "排序号不能为空")
    @ExcelProperty(value = "排序号")
    private Long orderNum;
@@ -51,7 +45,7 @@
    /**
     * key键
     */
    @ApiModelProperty("key键")
    @ApiModelProperty("key键")
    @NotBlank(message = "key键不能为空")
    @ExcelProperty(value = "key键")
    private String testKey;
@@ -59,8 +53,9 @@
    /**
     * å€¼
     */
    @ApiModelProperty("值")
    @ApiModelProperty("值")
    @NotBlank(message = "值不能为空")
    @ExcelProperty(value = "值")
    private String value;
}
ruoyi-ui/src/views/demo/demo/index.vue
@@ -79,7 +79,7 @@
          size="mini"
          @click="handleImport"
          v-hasPermi="['demo:demo:import']"
        >导入</el-button>
        >导入(校验)</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button