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.提供给前端的接口

publicLonguploadBatchUpdateOrgFile(@RequestParam("file") MultipartFilefile) throwsIOException {
Stringoperator=InvocationContexts.getContext().getUserId();
IccAssert.notNull(file, "上传的导入文件不能为空");
IccAssert.notEmpty(file.getOriginalFilename(), "上传的党组织文件名不能为空");
// 插入导入任务 ObExcelScheduleTasktaskParam=newObExcelScheduleTask();
// taskParam.set        ...
ObExcelScheduleTasktask=taskService.add(taskParam);
if (task==null) {
thrownewIccException(IccError.ICC_ERROR);
        }
InputStreaminputStream=file.getInputStream();
// 启用线程池异步导入数据ThreadPoolBusService.IMPORT_TASK_POOL.submit(newRunnable() {
@Overridepublicvoidrun() {
// 数据解析 实现AnalysisEventListenerOrgBatchUpdateExcelListenerorgBatchUpdateExcelListener=newOrgBatchUpdateExcelListener(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());
            }
        });
returntask.getId();
    }

5.讲解AnalysisEventListener< T>

监听器有哪些方法

publicabstractclassAnalysisEventListener<T>implementsReadListener<T> {
// 这是监听器的构造方法,一般我们可以通过构造方法传入一些我们需要在解析excel时使用的数据publicAnalysisEventListener() {}
// 调用invokeHeadMap来获取表头数据publicvoidinvokeHead(Map<Integer, CellData>headMap, AnalysisContextcontext) {
this.invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context), context);
    }
// 获取表头数据publicvoidinvokeHeadMap(Map<Integer, String>headMap, AnalysisContextcontext) {}
// 读取条额外信息:批注、超链接、合并单元格信息等publicvoidextra(CellExtraextra, AnalysisContextcontext) {}
// 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。publicvoidonException(Exceptionexception, AnalysisContextcontext) throwsException {throwexception;}
publicbooleanhasNext(AnalysisContextcontext) {returntrue;}
}


其中可以看到AnalysisEventListener 实现了 ReadListener

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

publicinterfaceReadListener<T>extendsListener {
voidonException(Exceptionvar1, AnalysisContextvar2) throwsException;
voidinvokeHead(Map<Integer, CellData>var1, AnalysisContextvar2);
// 一行行读取表格内容voidinvoke(Tvar1, AnalysisContextvar2);
voidextra(CellExtravar1, AnalysisContextvar2);
// 读取完成后的操作voiddoAfterAllAnalysed(AnalysisContextvar1);
booleanhasNext(AnalysisContextvar1);
}

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

publicclassOrgBatchUpdateExcelListenerextendsAnalysisEventListener<OrgBatchUpdateExcelModel> {
privateCommonDictServicecommonDictService;
privateOrgQueryServiceorgQueryService;
privateTaskServicetaskService;
privatePartyUserServicepartyUserService;
privateObExcelScheduleTasktask;
/*** 源文件名*/privateStringoriginalFilename;
/*** 组织编码*/privateStringorgBusinessId;
/*** 验证成功的数据*/privateList<OrgBatchUpdateExcelModel>successResultList;
/*** 验证失败的数据*/privateList<OrgBatchUpdateExcelModel>failResultList;
/*** 党组织类别*/privateMap<String, String>dzzlbRemarkMap;
/*** 党组织所在行政区划*/privateMap<String, String>xzqhMap;
/*** 党组织所在单位情况*/privateMap<String, String>dwqkMap;
/*** 党组织类别*/privateMap<String, String>dzzlbMap;
/*** 删除操作*/privatestaticfinalStringOPERATE_DELETE="删除";
/*** 修改操作*/privatestaticfinalStringOPERATE_UPDATE="修改";
privatestaticfinalStringEXCEL_HEAD_ERROR="文件不正确,请重新下载模板";
privatestaticfinalStringEXCEL_EMPTY_ERROR="文件里没有数据";
privatestaticfinalStringEXCEL_DATA_T00_LONG="excel里数据不能大于1000条";
/*** excel头*/privatestaticfinalStringHEAD="{0=党组织全称, 1=上级党组织全称, 2=党组织联系人, 3=党组织联系电话(手机号), 4=党组织类别, 5=党组织所在单位情况, 6=党组织所在行政区划, 7=主要业务, 8=操作}";
/*** excel头*/privatestaticfinalStringHEAD_TWO="{0=党组织全称, 1=上级党组织全称, 2=党组织联系人, 3=党组织联系电话(手机号), 4=党组织类别, 5=党组织所在单位情况, 6=党组织所在行政区划, 7=主要业务, 8=操作, 9=错误原因}";
/*** excel头*/privateMap<Integer, String>HEAD_MAP=null;
/*** 导入的起始行*/privatestaticfinalIntegerSTART_ROW_INDEX=3;
/*** 最大导入数量*/privatestaticfinalIntegerMAX_ROWS=1000;
/*** 租户*/privateStringtenantCode;
publicOrgBatchUpdateExcelListener(CommonDictServicecommonDictService, OrgQueryServiceorgQueryService, TaskServicetaskService,
StringtenantCode, StringoriginalFilename, StringorgBusinessId, ObExcelScheduleTasktask,
PartyUserServicepartyUserService) {
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();
    }
privatevoidinit() {
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=newArrayList<>();
this.failResultList=newArrayList<>();
    }
@OverridepublicvoidinvokeHeadMap(Map<Integer, String>headMap, AnalysisContextcontext) {
log.info("fileName:"+originalFilename);
// 验证excel是否合规context.readSheetHolder().setHeadRowNumber(START_ROW_INDEX);
HEAD_MAP=headMap;
introwIndex=context.readRowHolder().getRowIndex();
if (rowIndex==START_ROW_INDEX-1) {
Stringhead=String.valueOf(HEAD_MAP);
if (!StringUtils.equals(head, HEAD) &&!StringUtils.equals(head, HEAD_TWO)) {
log.info("head:{}", head);
thrownewExcelAnalysisException(EXCEL_HEAD_ERROR);
            }
inttotalRows=context.readSheetHolder().getApproximateTotalRowNumber();
if (totalRows<=START_ROW_INDEX) {
thrownewExcelAnalysisException(EXCEL_EMPTY_ERROR);
            }
if (totalRows-START_ROW_INDEX>MAX_ROWS) {
thrownewExcelAnalysisException(EXCEL_DATA_T00_LONG);
            }
        }
    }
@Overridepublicvoidinvoke(OrgBatchUpdateExcelModelorgBatchUpdateExcelModel, AnalysisContextanalysisContext) {
StringBuffererrorMsg=newStringBuffer();
introwIndex=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("|操作列不能未空");
            } elseif (StringUtils.equals(orgBatchUpdateExcelModel.getOperate(), OPERATE_DELETE)) {
// 删除orgBatchUpdateExcelModel.setDeleteFlag(OrgConst.YES_STATE);
            } else {
// 更新Stringlinker=orgBatchUpdateExcelModel.getLinker();
StringlinkPhone=orgBatchUpdateExcelModel.getLinkPhone();
StringpartyTypeName=orgBatchUpdateExcelModel.getPartyTypeName();
StringpartyCompanyIntroName=orgBatchUpdateExcelModel.getPartyCompanyIntroName();
StringxzqhName=orgBatchUpdateExcelModel.getXzqhName();
StringmainBusiness=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)) {
StringpartyType=dzzlbMap.get(orgBatchUpdateExcelModel.getPartyTypeName());
if (StringUtils.isBlank(partyType)) {
errorMsg.append("|党组织类别错误");
                    } else {
orgBatchUpdateExcelModel.setPartyType(partyType);
orgBatchUpdateExcelModel.setPartyTypeSimple(dzzlbRemarkMap.get(orgBatchUpdateExcelModel.getPartyTypeName()));
                    }
                }
if (StringUtils.isNotBlank(partyCompanyIntroName)) {
StringpartyCompanyIntro=dwqkMap.get(orgBatchUpdateExcelModel.getPartyCompanyIntroName());
if (StringUtils.isBlank(partyCompanyIntro)) {
errorMsg.append("|党组织所在单位情况错误");
                    } else {
orgBatchUpdateExcelModel.setPartyCompanyIntro(partyCompanyIntro);
                    }
                }
if (StringUtils.isNotBlank(xzqhName)) {
Stringxzqh=xzqhMap.get(orgBatchUpdateExcelModel.getXzqhName());
if (StringUtils.isBlank(xzqh)) {
errorMsg.append("|党组织所在行政区划错误");
                    } else {
orgBatchUpdateExcelModel.setXzqh(xzqh);
                    }
                }
            }
StringerrorMsgStr=errorMsg.toString();
if (StringUtils.isNotBlank(errorMsgStr)) {
orgBatchUpdateExcelModel.setErrorMsg(errorMsgStr);
this.clear(orgBatchUpdateExcelModel);
failResultList.add(orgBatchUpdateExcelModel);
            } else {
successResultList.add(orgBatchUpdateExcelModel);
            }
        }
    }
@OverridepublicvoiddoAfterAllAnalysed(AnalysisContextanalysisContext) {
if (CollectionUtils.isEmpty(successResultList) &&CollectionUtils.isEmpty(failResultList)) {
thrownewExcelAnalysisException(EXCEL_EMPTY_ERROR);
        }
if (!CollectionUtils.isEmpty(successResultList)) {
List<String>orgNameList=successResultList.stream().map(e->e.getOrgName()).collect(Collectors.toList());
Stringpids=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);
booleanerrorFlag=false;
for (OrgBatchUpdateExcelModelmodel : successResultList) {
Stringkey=model.getOrgName() +model.getParentOrgName();
ObOrgDTOobOrgDTO=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())){
PartyUserCriteriapartyUserCriteria=newPartyUserCriteria();
partyUserCriteria.setPid(model.getOrgBusinessId());
longuserCount=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=newArrayList<>();
if (!CollectionUtils.isEmpty(successList)) {
successResultList.addAll(successList);
                }
            }
        }
    }
/*** 清楚不需要字段** @param model*/privatevoidclear(OrgBatchUpdateExcelModelmodel) {
model.setPartyTypeSimple(null);
model.setPartyType(null);
model.setDeleteFlag(null);
model.setXzqh(null);
model.setPartyCompanyIntro(null);
model.setCompanyType(null);
model.setOrgBusinessId(null);
model.setCompanyBusinessId(null);
    }
@OverridepublicvoidonException(Exceptionexception, AnalysisContextcontext) {
task.setTaskEnd(newDate());
task.setTaskStatus(EnumTaskStatus.FAIL);
task.setTaskErrorInfo(exception.getMessage());
taskService.updateStatusById(task);
    }
publicList<OrgBatchUpdateExcelModel>getSuccessResultList() {
returnsuccessResultList;
    }
publicList<OrgBatchUpdateExcelModel>getFailResultList() {
returnfailResultList;
    }

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

/*** 导入文件-批量修改党组织** @param operator         操作人* @param tenantCode       租户* @param originalFilename 文件名* @param task             任务* @param successList      成功的数据* @param failList         失败的数据*/@Transactional(rollbackFor=Exception.class)
publicvoidbatchUpdateImportOrg(Stringoperator, StringtenantCode, StringoriginalFilename, ObExcelScheduleTasktask,
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, newDate(), tenantCode);
                }
            } catch (Exceptione) {
task.setTaskStatus(EnumTaskStatus.FAIL);
task.setTaskErrorInfo(CommonUtil.stringAppend("生成和上传党组织批量修改错误文件时异常 errorId=", ErrorIdUtil.getErrorId(), " error=", ExceptionUtil.stacktraceToString(e, 150)));
            }
        }
// 上传错误数据文件到ossif (!CollectionUtils.isEmpty(failList)) {
InputStreamossInputStream=null;
try (ByteArrayOutputStreamoutputStream=newByteArrayOutputStream()) {
EasyExcel.write(outputStream, OrgBatchUpdateExcelVO.class).sheet(OrgConst.BATCH_UPDATE_EXCEL_SHEET).doWrite(failList);
ossInputStream=newByteArrayInputStream(outputStream.toByteArray());
//上传文件FileUploadResultresult=fileStorageService.uploadFile(ossInputStream, UploadResourceType.EXCEL, System.currentTimeMillis() +originalFilename);
if (result!=null) {
task.setTaskStatus(EnumTaskStatus.FAIL);
task.setOssFileUrl(result.getUrl());
task.setOssFileStatus(EnumOssFileStatus.NORMAL);
                }
            } catch (Exceptione) {
task.setTaskErrorInfo(CommonUtil.stringAppend("生成和上传党组织批量修改错误文件时异常 errorId=", ErrorIdUtil.getErrorId(), " error=", ExceptionUtil.stacktraceToString(e, 150)));
            }
        }
task.setTaskEnd(newDate());
taskService.updateStatusById(task);
    }
相关文章
SpringBoot+Mybatis-Plus+PageHelper分页+多条件查询
SpringBoot+Mybatis-Plus+PageHelper分页+多条件查询
964 0
|
安全 Windows
电脑出现No Bootable Device无法开机该怎么办?
本文介绍笔记本电脑出现No Bootable Device错误提示,且无法开机的多种解决办法~
2618 1
电脑出现No Bootable Device无法开机该怎么办?
|
Java 关系型数据库 MySQL
分布式配置管理中心Apollo
分布式配置管理中心Apollo
|
存储 芯片
单片机原理与应用以及C51编程技术——硬件体系结构梳理(下)
单片机原理与应用以及C51编程技术——硬件体系结构梳理(下)
1459 0
单片机原理与应用以及C51编程技术——硬件体系结构梳理(下)
|
Linux 数据安全/隐私保护
CentOS 7 忘记root密码的解决方法
CentOS 7 忘记root密码的解决方法,亲测有效
11242 0
|
机器学习/深度学习 人工智能 安全
探索人工智能在网络安全中的创新应用
探索人工智能在网络安全中的创新应用
934 0
|
XML Java 数据库连接
MyBatis实现SaveOrUpdate
这篇文章主要讲如何通过xml方式实现SaveOrUpdate,但是仍然建议在Service中实现。 例子 select count(*) from country where id = #{id} update country se...
4469 0
|
easyexcel Java API
【Alibaba工具型技术系列】「EasyExcel技术专题」实战技术针对于项目中常用的Excel操作指南
【Alibaba工具型技术系列】「EasyExcel技术专题」实战技术针对于项目中常用的Excel操作指南
1786 0
【Alibaba工具型技术系列】「EasyExcel技术专题」实战技术针对于项目中常用的Excel操作指南
|
9月前
|
缓存 监控 Java
用 Spring Boot 3 构建高性能 RESTful API 的 10 个关键技巧
本文介绍使用 Spring Boot 3 构建高性能 RESTful API 的 10 大关键技巧,涵盖启动优化、数据库连接池、缓存策略、异步处理、分页查询、限流熔断、日志监控等方面。通过合理配置与代码优化,显著提升响应速度、并发能力与系统稳定性,助力打造高效云原生应用。
781 3

热门文章

最新文章