Alibaba EasyExcel实现excel数据批量导入-阿里云开发者社区

开发者社区> 1972394598275451> 正文

Alibaba EasyExcel实现excel数据批量导入

简介: 最近在党建业务需求开发中,PD提出需要对党组织数据进行批量替换的需求。 对需求分析理解后,我在项目中使用了Alibaba EasyExcel进行功能研发。
+关注继续查看

1.需求说明

通过党组织全称和上级党组织全称作为数据唯一key更新其它数据。

image.png

2.添加 EasyExcel 依赖

 <dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>easyexcel</artifactId>
   <version>2.1.6</version>
</dependency>

3.提供给前端的接口

public Long uploadBatchUpdateOrgFile(@RequestParam("file") MultipartFile file) throws IOException {
        String operator = InvocationContexts.getContext().getUserId();
        IccAssert.notNull(file, "上传的导入文件不能为空");
        IccAssert.notEmpty(file.getOriginalFilename(), "上传的党组织文件名不能为空");
        // 插入导入任务 
        ObExcelScheduleTask taskParam = new ObExcelScheduleTask();
        // taskParam.set
        ...
        ObExcelScheduleTask task = taskService.add(taskParam);
        if (task == null) {
            throw new IccException(IccError.ICC_ERROR);
        }
        InputStream inputStream = file.getInputStream();
        // 启用线程池异步导入数据
        ThreadPoolBusService.IMPORT_TASK_POOL.submit(new Runnable() {
            @Override
            public void run() {
                // 数据解析 实现AnalysisEventListener
                OrgBatchUpdateExcelListener orgBatchUpdateExcelListener = new OrgBatchUpdateExcelListener(commonDictService,
                        orgQueryService, taskService, tenantCode, file.getOriginalFilename(), orgBusinessId, task, partyUserService);
                EasyExcel.read(inputStream, OrgBatchUpdateExcelModel.class, orgBatchUpdateExcelListener).sheet().doRead();
                // 更新数据
                orgService.batchUpdateImportOrg(operator, tenantCode, file.getOriginalFilename(), task,
                        orgBatchUpdateExcelListener.getSuccessResultList(), orgBatchUpdateExcelListener.getFailResultList());
            }
        });
        return task.getId();
    }

5.讲解AnalysisEventListener< T>

监听器有哪些方法

public abstract class AnalysisEventListener<T> implements ReadListener<T> {
    // 这是监听器的构造方法,一般我们可以通过构造方法传入一些我们需要在解析excel时使用的数据
    public AnalysisEventListener() {}
    // 调用invokeHeadMap来获取表头数据
    public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) {
        this.invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context), context);
    }
    // 获取表头数据
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {}
    // 读取条额外信息:批注、超链接、合并单元格信息等
    public void extra(CellExtra extra, AnalysisContext context) {}
    // 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
    public void onException(Exception exception, AnalysisContext context) throws Exception {throw exception;}
    public boolean hasNext(AnalysisContext context) {return true;}
}


其中可以看到AnalysisEventListener 实现了 ReadListener

然后看到ReadListener后 发现其中还有两个方法是AnalysisEventListener没有实现的,而且这两个方法还是很重要的

public interface ReadListener<T> extends Listener {
    void onException(Exception var1, AnalysisContext var2) throws Exception;
    void invokeHead(Map<Integer, CellData> var1, AnalysisContext var2);
    // 一行行读取表格内容
    void invoke(T var1, AnalysisContext var2);
    void extra(CellExtra var1, AnalysisContext var2);
    // 读取完成后的操作
    void doAfterAllAnalysed(AnalysisContext var1);
    boolean hasNext(AnalysisContext var1);
}

6.实现AnalysisEventListener过滤成功和失败的数据

public class OrgBatchUpdateExcelListener extends AnalysisEventListener<OrgBatchUpdateExcelModel> {

    private CommonDictService commonDictService;

    private OrgQueryService orgQueryService;

    private TaskService taskService;

    private PartyUserService partyUserService;

    private ObExcelScheduleTask task;
    /**
     * 源文件名
     */
    private String originalFilename;
    /**
     * 组织编码
     */
    private String orgBusinessId;
    /**
     * 验证成功的数据
     */
    private List<OrgBatchUpdateExcelModel> successResultList;
    /**
     * 验证失败的数据
     */
    private List<OrgBatchUpdateExcelModel> failResultList;

    /**
     * 党组织类别
     */
    private Map<String, String> dzzlbRemarkMap;

    /**
     * 党组织所在行政区划
     */
    private Map<String, String> xzqhMap;

    /**
     * 党组织所在单位情况
     */
    private Map<String, String> dwqkMap;

    /**
     * 党组织类别
     */
    private Map<String, String> dzzlbMap;
    /**
     * 删除操作
     */
    private static final String OPERATE_DELETE = "删除";
    /**
     * 修改操作
     */
    private static final String OPERATE_UPDATE = "修改";
    private static final String EXCEL_HEAD_ERROR = "文件不正确,请重新下载模板";
    private static final String EXCEL_EMPTY_ERROR = "文件里没有数据";
    private static final String EXCEL_DATA_T00_LONG = "excel里数据不能大于1000条";

    /**
     * excel头
     */
    private static final String HEAD = "{0=党组织全称, 1=上级党组织全称, 2=党组织联系人, 3=党组织联系电话(手机号), 4=党组织类别, 5=党组织所在单位情况, 6=党组织所在行政区划, 7=主要业务, 8=操作}";
    /**
     * excel头
     */
    private static final String HEAD_TWO = "{0=党组织全称, 1=上级党组织全称, 2=党组织联系人, 3=党组织联系电话(手机号), 4=党组织类别, 5=党组织所在单位情况, 6=党组织所在行政区划, 7=主要业务, 8=操作, 9=错误原因}";
    /**
     * excel头
     */
    private Map<Integer, String> HEAD_MAP = null;
    /**
     * 导入的起始行
     */
    private static final Integer START_ROW_INDEX = 3;

    /**
     * 最大导入数量
     */
    private static final Integer MAX_ROWS = 1000;
    /**
     * 租户
     */
    private String tenantCode;

    public OrgBatchUpdateExcelListener(CommonDictService commonDictService, OrgQueryService orgQueryService, TaskService taskService,
                                       String tenantCode, String originalFilename, String orgBusinessId, ObExcelScheduleTask task,
                                       PartyUserService partyUserService) {
        this.commonDictService = commonDictService;
        this.orgQueryService = orgQueryService;
        this.taskService = taskService;
        this.tenantCode = tenantCode;
        this.originalFilename = originalFilename;
        this.orgBusinessId = orgBusinessId;
        this.task = task;
        this.partyUserService = partyUserService;
        init();
    }

    private void init() {
        Map<String, String> dzzlbMap = commonDictService.getValueKeyByTypeCode(OrgConst.DICT_DZZ_ZZLB);
        Map<String, String> dzzlbRemarkMap = commonDictService.getValueRemarkByTypeCode(OrgConst.DICT_DZZ_ZZLB);
        Map<String, String> xzqhMap = commonDictService.getValueKeyByTypeCode(OrgConst.D_DZZ_XZQH);
        Map<String, String> dwqkMap = commonDictService.getValueKeyByTypeCode(OrgConst.D_DZZ_DWQK);
        this.dzzlbRemarkMap = dzzlbRemarkMap;
        this.xzqhMap = xzqhMap;
        this.dwqkMap = dwqkMap;
        this.dzzlbMap = dzzlbMap;
        this.successResultList = new ArrayList<>();
        this.failResultList = new ArrayList<>();
    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        log.info("fileName:" + originalFilename);
        // 验证excel是否合规
        context.readSheetHolder().setHeadRowNumber(START_ROW_INDEX);
        HEAD_MAP = headMap;
        int rowIndex = context.readRowHolder().getRowIndex();
        if (rowIndex == START_ROW_INDEX - 1) {
            String head = String.valueOf(HEAD_MAP);
            if (!StringUtils.equals(head, HEAD) && !StringUtils.equals(head, HEAD_TWO)) {
                log.info("head:{}", head);
                throw new ExcelAnalysisException(EXCEL_HEAD_ERROR);
            }
            int totalRows = context.readSheetHolder().getApproximateTotalRowNumber();
            if (totalRows <= START_ROW_INDEX) {
                throw new ExcelAnalysisException(EXCEL_EMPTY_ERROR);
            }
            if (totalRows - START_ROW_INDEX > MAX_ROWS) {
                throw new ExcelAnalysisException(EXCEL_DATA_T00_LONG);
            }
        }
    }

    @Override
    public void invoke(OrgBatchUpdateExcelModel orgBatchUpdateExcelModel, AnalysisContext analysisContext) {
        StringBuffer errorMsg = new StringBuffer();
        int rowIndex = analysisContext.readRowHolder().getRowIndex();
        if (rowIndex >= START_ROW_INDEX) {
            log.info("====当前数据========:{}", orgBatchUpdateExcelModel);
            // 验证数据是否正确
            if (StringUtils.isBlank(orgBatchUpdateExcelModel.getOrgName())) {
                errorMsg.append("|党组织全称不能为空");
            }
            if (StringUtils.isBlank(orgBatchUpdateExcelModel.getParentOrgName())) {
                errorMsg.append("|上级党组织全称不能为空");
            }
            if (StringUtils.isBlank(orgBatchUpdateExcelModel.getOperate())) {
                errorMsg.append("|操作列不能未空");
            } else if (StringUtils.equals(orgBatchUpdateExcelModel.getOperate(), OPERATE_DELETE)) {
                // 删除
                orgBatchUpdateExcelModel.setDeleteFlag(OrgConst.YES_STATE);
            } else {
                // 更新
                String linker = orgBatchUpdateExcelModel.getLinker();
                String linkPhone = orgBatchUpdateExcelModel.getLinkPhone();
                String partyTypeName = orgBatchUpdateExcelModel.getPartyTypeName();
                String partyCompanyIntroName = orgBatchUpdateExcelModel.getPartyCompanyIntroName();
                String xzqhName = orgBatchUpdateExcelModel.getXzqhName();
                String mainBusiness = orgBatchUpdateExcelModel.getMainBusiness();
                if (StringUtils.isBlank(linker) && StringUtils.isBlank(linkPhone) && StringUtils.isBlank(partyTypeName)
                        && StringUtils.isBlank(partyCompanyIntroName) && StringUtils.isBlank(xzqhName)
                        && StringUtils.isBlank(mainBusiness)) {
                    errorMsg.append("|请至少输入一个修改项");
                }
                if (StringUtils.isNotBlank(linkPhone)
                        && !Validator.isMobile(linkPhone)) {
                    errorMsg.append("|党组织联系电话格式错误");
                }
                if (StringUtils.isNotBlank(partyTypeName)) {
                    String partyType = dzzlbMap.get(orgBatchUpdateExcelModel.getPartyTypeName());
                    if (StringUtils.isBlank(partyType)) {
                        errorMsg.append("|党组织类别错误");
                    } else {
                        orgBatchUpdateExcelModel.setPartyType(partyType);
                        orgBatchUpdateExcelModel.setPartyTypeSimple(dzzlbRemarkMap.get(orgBatchUpdateExcelModel.getPartyTypeName()));
                    }
                }
                if (StringUtils.isNotBlank(partyCompanyIntroName)) {
                    String partyCompanyIntro = dwqkMap.get(orgBatchUpdateExcelModel.getPartyCompanyIntroName());
                    if (StringUtils.isBlank(partyCompanyIntro)) {
                        errorMsg.append("|党组织所在单位情况错误");
                    } else {
                        orgBatchUpdateExcelModel.setPartyCompanyIntro(partyCompanyIntro);
                    }
                }
                if (StringUtils.isNotBlank(xzqhName)) {
                    String xzqh = xzqhMap.get(orgBatchUpdateExcelModel.getXzqhName());
                    if (StringUtils.isBlank(xzqh)) {
                        errorMsg.append("|党组织所在行政区划错误");
                    } else {
                        orgBatchUpdateExcelModel.setXzqh(xzqh);
                    }
                }
            }
            String errorMsgStr = errorMsg.toString();
            if (StringUtils.isNotBlank(errorMsgStr)) {
                orgBatchUpdateExcelModel.setErrorMsg(errorMsgStr);
                this.clear(orgBatchUpdateExcelModel);
                failResultList.add(orgBatchUpdateExcelModel);
            } else {
                successResultList.add(orgBatchUpdateExcelModel);
            }
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        if (CollectionUtils.isEmpty(successResultList) && CollectionUtils.isEmpty(failResultList)) {
            throw new ExcelAnalysisException(EXCEL_EMPTY_ERROR);
        }
        if (!CollectionUtils.isEmpty(successResultList)) {
            List<String> orgNameList = successResultList.stream().map(e -> e.getOrgName()).collect(Collectors.toList());
            String pids = orgQueryService.getPids(orgBusinessId, tenantCode);
            List<ObOrgDTO> obOrgDTOList = orgQueryService.queryForBatchUpdateOrg(orgNameList, orgBusinessId, pids, tenantCode);
            // map 用来获取组织编码
            Map<String, ObOrgDTO> map = obOrgDTOList.stream()
                    .collect(Collectors.toMap(ObOrgDTO::getOrgName, o -> o, (v1, v2) -> v1));
            // 上级党组织的信息
            List<String> pidList = obOrgDTOList.stream().map(e -> e.getPid()).collect(Collectors.toList());
            Map<String, ObPartyOrg> parentOrgMap = orgQueryService.batchQueryPartyOrg(pidList, tenantCode, OrgConst.NO_STATE);
            // 本级党组织的信息
            List<String> currentOrgIdList = obOrgDTOList.stream().map(e -> e.getOrgBusinessId()).collect(Collectors.toList());
            Map<String, ObPartyOrg> currentOrgMap = orgQueryService.batchQueryPartyOrg(currentOrgIdList, tenantCode, OrgConst.NO_STATE);
            boolean errorFlag = false;
            for (OrgBatchUpdateExcelModel model : successResultList) {
                String key = model.getOrgName() + model.getParentOrgName();
                ObOrgDTO obOrgDTO = map.get(key);
                if (obOrgDTO != null) {
                    model.setOrgBusinessId(obOrgDTO.getOrgBusinessId());
                    if (StringUtils.equals(model.getPartyCompanyIntro(), OrgConst.IN_THE_SAME_COMPANY_CODE)) {
                        model.setCompanyBusinessId(parentOrgMap.get(obOrgDTO.getPid()) == null ? null : parentOrgMap.get(obOrgDTO.getPid()).getCompanyBusinessId());
                        model.setCompanyType(parentOrgMap.get(obOrgDTO.getPid()) == null ? null : parentOrgMap.get(obOrgDTO.getPid()).getCompanyType());
                    } else {
                        if (currentOrgMap.get(model.getOrgBusinessId()) != null && !StringUtils.equals(currentOrgMap.get(model.getOrgBusinessId()).getPartyCompanyIntro(), OrgConst.IN_THE_SAME_COMPANY_CODE)) {
                            model.setCompanyType(currentOrgMap.get(model.getOrgBusinessId()).getCompanyType());
                            model.setCompanyBusinessId(currentOrgMap.get(model.getOrgBusinessId()).getOrgBusinessId());
                        }
                    }
                    if (OrgConst.YES_STATE.equals(model.getDeleteFlag())) {
                        // 删除需要判断 下级有没有组织或党员
                        List<String> childOrgBusinessId = orgQueryService.getAllChildOrgBusinessId(model.getOrgBusinessId(), tenantCode);
                        if (!CollectionUtils.isEmpty(childOrgBusinessId)) {
                            if (!currentOrgIdList.containsAll(childOrgBusinessId)) {
                                model.setErrorMsg("|该党组织下存在下级组织或党员数据");
                                this.clear(model);
                                failResultList.add(model);
                                errorFlag = true;
                            }
                        }
                        if(StringUtils.isBlank(model.getErrorMsg())){
                            PartyUserCriteria partyUserCriteria = new PartyUserCriteria();
                            partyUserCriteria.setPid(model.getOrgBusinessId());
                            long userCount = partyUserService.countPartyUser(partyUserCriteria);
                            if(userCount > 0){
                                model.setErrorMsg("|该党组织下存在下级组织或党员数据");
                                this.clear(model);
                                failResultList.add(model);
                                errorFlag = true;
                            }
                        }

                    }
                } else {
                    model.setErrorMsg("|党组织全称或上级上组织全称错误");
                    this.clear(model);
                    failResultList.add(model);
                    errorFlag = true;
                }
            }
            if (errorFlag) {
                List<OrgBatchUpdateExcelModel> successList = successResultList.stream().filter(e -> StringUtils.isBlank(e.getErrorMsg())).collect(Collectors.toList());
                successResultList = new ArrayList<>();
                if (!CollectionUtils.isEmpty(successList)) {
                    successResultList.addAll(successList);
                }
            }
        }
    }

    /**
     * 清楚不需要字段
     *
     * @param model
     */
    private void clear(OrgBatchUpdateExcelModel model) {
        model.setPartyTypeSimple(null);
        model.setPartyType(null);
        model.setDeleteFlag(null);
        model.setXzqh(null);
        model.setPartyCompanyIntro(null);
        model.setCompanyType(null);
        model.setOrgBusinessId(null);
        model.setCompanyBusinessId(null);
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) {
        task.setTaskEnd(new Date());
        task.setTaskStatus(EnumTaskStatus.FAIL);
        task.setTaskErrorInfo(exception.getMessage());
        taskService.updateStatusById(task);
    }

    public List<OrgBatchUpdateExcelModel> getSuccessResultList() {
        return successResultList;
    }

    public List<OrgBatchUpdateExcelModel> getFailResultList() {
        return failResultList;
    }

7.插入成功数据,上传失败数据到OSS供前端下载

/**
     * 导入文件-批量修改党组织
     *
     * @param operator         操作人
     * @param tenantCode       租户
     * @param originalFilename 文件名
     * @param task             任务
     * @param successList      成功的数据
     * @param failList         失败的数据
     */
  @Transactional(rollbackFor = Exception.class)
    public void batchUpdateImportOrg(String operator, String tenantCode, String originalFilename, ObExcelScheduleTask task,
                                     List<OrgBatchUpdateExcelModel> successList,
                                     List<OrgBatchUpdateExcelModel> failList) {

        task.setTaskStatus(EnumTaskStatus.DONE);
        // 修改数据
        if (!CollectionUtils.isEmpty(successList)) {
            try {
                obPartyOrgMapper.updateBatchForImportOrg(successList, operator, tenantCode);
                List<String> orgBusinessIdList = successList.stream().filter(e -> OrgConst.YES_STATE.equals(e.getDeleteFlag())).map(e -> e.getOrgBusinessId()).collect(Collectors.toList());
                if(!CollectionUtils.isEmpty(orgBusinessIdList)){
                    obOrgMapper.batchDeleteObOrg(orgBusinessIdList, operator, new Date(), tenantCode);
                }
            } catch (Exception e) {
                task.setTaskStatus(EnumTaskStatus.FAIL);
                task.setTaskErrorInfo(CommonUtil.stringAppend("生成和上传党组织批量修改错误文件时异常 errorId=", ErrorIdUtil.getErrorId(), " error=", ExceptionUtil.stacktraceToString(e, 150)));
            }
        }
        // 上传错误数据文件到oss
        if (!CollectionUtils.isEmpty(failList)) {
            InputStream ossInputStream = null;
            try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                EasyExcel.write(outputStream, OrgBatchUpdateExcelVO.class).sheet(OrgConst.BATCH_UPDATE_EXCEL_SHEET).doWrite(failList);
                ossInputStream = new ByteArrayInputStream(outputStream.toByteArray());
                //上传文件
                FileUploadResult result = fileStorageService.uploadFile(ossInputStream, UploadResourceType.EXCEL, System.currentTimeMillis() + originalFilename);
                if (result != null) {
                    task.setTaskStatus(EnumTaskStatus.FAIL);
                    task.setOssFileUrl(result.getUrl());
                    task.setOssFileStatus(EnumOssFileStatus.NORMAL);
                }
            } catch (Exception e) {
                task.setTaskErrorInfo(CommonUtil.stringAppend("生成和上传党组织批量修改错误文件时异常 errorId=", ErrorIdUtil.getErrorId(), " error=", ExceptionUtil.stacktraceToString(e, 150)));
            }
        }
        task.setTaskEnd(new Date());
        taskService.updateStatusById(task);
    }

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
异步导入导出Redis数据(利用Hiredis、Libevent)
最近工作中需要用到一个将数据从Redis导出到文本(或从文本导入Redis)的工具。找到一个用Ruby写的开源软件redis-dump(http://delanotes.com/redis-dump/)。
1237 0
Java数据导入(读)Excel文件 解析
  在编程中经常需要使用到表格(报表)的处理主要以Excel表格为主。下面给出用java读取excel表格方法:   1.添加jar文件   java导入导出Excel文件要引入jxl.jar包,最关键的是这套API是纯Java的,并不依赖Windows系统,即使运行在Linux下,它同样能够正确的处理Excel文件。
925 0
VSTO学习笔记(十四)Excel数据透视表与PowerPivot
原文:VSTO学习笔记(十四)Excel数据透视表与PowerPivot 近期公司内部在做一种通用查询报表,方便人力资源分析、统计数据。由于之前公司系统中有一个类似的查询使用Excel数据透视表完成的,故我也打算借鉴一下。
1439 0
如何通过 Excel import 的方式导入测试数据到 SAP Commerce Cloud 服务器
如何通过 Excel import 的方式导入测试数据到 SAP Commerce Cloud 服务器
6 0
阿里巴巴数据中心创新实践
阿里巴巴作为全球领先的互联网综合业务平台,其遍布全球的数据中心承载着世界上最大的以电商为核心的复杂业务体系。“双十一”销售额6分58秒破百亿,交易峰值17.5万笔/秒,24小时实现了销售额1207亿元,这一切都离不开阿里全球数十个数据中心的支持保障。下面就让我们深入了解阿里双11背后的技术后盾吧!
2408 0
VBA将Excel数据导入到数据库
1、如果Excel中的数据是标志格式的,即标题栏+数据这种类型,那么导入数据库将非常方便,示例代码如下: '函数:导入 Private Function F_K_Import() As Boolean Dim cnCurrent As ADODB.
929 0
1
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载