使用设计模式基于easypoi优雅的设计通用excel导入功能

文章目录

    • 概要
    • 整体架构流程
    • 代码设计
      • 配置类
      • 通用API
      • 分发器
      • 处理器
      • 业务逻辑处理service接口
      • 策略模型
    • 小结

概要

基于java原生 + easypoi结合适配器模式、策略模式、工厂模式设计一个通用的excel导入框架

整体架构流程

在这里插入图片描述

代码设计

由上到下,分别讲解代码

配置类

ExcelConfigEnum
该配置类是声明导入业务类型,导入参数与Handler之间的实例化关系。

@Getter
public enum ExcelConfigEnum {// 测试配置TEST("test", "测试", "com.xxx.TestExcelHandler"),;private String type;private String desc;private String importClazz;ExcelConfigEnum (String type, String desc, String importClazz) {// ... 全参构造函数}// 根据type获取enumpublic static ExcelConfigEnum getByType(String type) {// codes...}
}

ExcelPolicyConfiguration
该注解类用于配置handler对应的策略

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AsyncExcelPolicyConfiguration
/**
* 导入策略
*/
Class<?extends AsyncExcelPolicy>policy();
/**
* 分片大小
*/
int shardingNum()default 1000;
}

AsyncExcelTask
多线程调度任务类


@Sf4j
public class AsyncExcelTask implements Runnable{// 处理器private AsyncExcelHandler handler;AsyncExcelTask(AsyncExcelHandler handler){this.handler=handler;}@Overridepublic void run(){handler.importExcel();}
}

Factory
handler工厂,用于实例化handler

public class ExcelHandlerFactory {public static AsyncExcelHandler getInstance(String type,InputStream in,Object param,String importId,String userId)throws NoSuchMethodException,IllegalAccessException,InvocationTargetException,InstantiationException,ClassNotFoundException {AsyncExcelConfigEnum moduleEnum = AsyncExcelConfigEnum.getByType(type);Class clz = Class.forName(moduleEnum.getImportclazz());if (Objects.isNuLl(clz)){return null;}AsyncExcelPolicyConfiguration annotation = (AsyncExcelPolicyConfiguration)clz.getAnnotation(AsyncExcelPolicyConfiguration.class);if (Objects.isNuLL(annotation)){log.error("缺失导入策略注解")throw new ServiceException((500,"缺失导入策略注解")}Class<?extends AsyncExcelPolicy>policyClass = annotation.policy();int shardingNum = annotation.shardingNum();AsyncExcelPolicy policy = policyClass.newInstance();Constructor<?extends AbstractAsyncExcelHandler> constructor =clz.getDeclaredConstructor(InputStream.class,Object.class,String.class,String.class,AsyncExcelPolicy.class,int.class);return constructor.newInstance(in,param,importId,userId,policy,shardingNum);}
}

通用API

@Api(va1ue="异形exce1守人守出",tags={"异步exce1手人子田")
@RestController
@RequestMapping("/common/asyncExcel")
public class AsyncExcelController {@Autowiredprivate AuthUtils authUtils;private static final String MODULE_AME="异步exce1导入导出";@OperLogOption(module MODULE_NAME,oper OperLogOption.Oper.IMPORT,desc ="excel")@PostMapping("/import")public Boolean asyncImportExcel(ExcelImportParams excelImportParams){AsyncExcelDispatcher.asyncDispatch(excelImportParams.getMultipartFiles[0],excelImportParams.getType(),excelImportParams.getObject(),authUtils.currentUserId());return true;}
}

分发器

根据前段传递的type通过factory构建handler

@s1f4j
public class AsyncExcelDispatcher {public static void asyncDispatch(MultipartFile file,String type,Object param,String userId){//构造异步导入excel记录对象UserAsyncExcel userAsyncExcel buildAsyncExceLEntity(file,userId);try {AsyncExcelConfigEnum excelEnum AsyncExcelConfigEnum.getByType(type);// 检查导入配置是否存在if (Objects.isNuLL(excelEnum) || StringUtils.isBLank(excelEnum.getImportClazz())){throw new ServiceException(BizExceptionCommonEnum.INTERFACE_NOT_EXSIT_ERROR);}// 保存导入信息,状态执行中IUserFeignService userFeignService Springutils.getBean(IUserFeignService.class);userFeignService.save(userAsyncExcel);//获取handLer实例InputStream in = file.getInputStream();AsyncExcelHandler handler = ExcelHandlerFactory.getInstance(type,in,param,userAsyncExcel.getId(),userId);ExecutorService executor ThreadPoolFactory.ThreadPoolEnum.IMPORT_EXCEL.getThreadPool();//异步提交任务AsyncExcelTask asyncExcelTask = new AsyncExcelTask(handler);executor.submit(asyncExcelTask);catch (IOException | InstantiationException | InvocationTargetException | NoSuchMethodException |	IllegalAccessException | ClassNotFoundException ex){log.error("获取实例失败",ex);//写入状态IUserFeignService userFeignService SpringUtils.getBean(IUserFeignService.class);userAsyncExcel.setStatus(AsyncExcelStatus.EXCEPTION);userAsyncExcel.setMessage(ex.getMessage());userFeignService.update(userAsyncExcel);throw new ServiceException(BizExceptionCommonEnum.INTERFACE_NOT_EXSIT_ERROR);}private static UserAsyncExcel buildAsyncExcelEntity(MultipartFile file,String userId){UserAsyncExceluserAsyncExcel new UserAsyncExcel();String id UuidUtils.getId();userAsyncExcel.setCreateTime(Timestamp.valueof(LocalDateTime.now(Clock.systemDefaultZone())));userAsyncExcel.setCreateUserId(userId);userAsyncExcel.setstatus(AsyncExcelStatus.EXECUTING);userAsyncExcel.setTitle(file.getoriginalFilename());userAsyncExcel.setType("I");userAsyncExcel.setId(id)jreturn userAsyncExcel;}}

处理器

IAsyncExcelHandler
handler接口, 规范编码

public interface AsyncExcelHandler<T extends ExcelImportBaseEntity> {// 导入excelvoid importExcel();// 重置导入参数void resetImportParams();// 设置数据处理器void setDataHandler(IExcelDataHandler dataHandler);// 设置字典处理器void setDictHandler(IExcelDictHandler dictHandler);// 是否需要字段校验void needverify(boolean needverify);// 获取业务服务对象IAsyncExcelService getservice();
}

AbstractAsyncExcelHandler
处理器抽象实现类

public abstract class AbstractAsyncExcelHandler<T extends ExcelImportBaseEntity> implements AsyncExcelHandler{/**文件流*/protected InputStream in;/***导入参数*/protected Object object;/**导入id*/protected String importId;/**导入用户*/protected String userId;/**策略*/protected AsyncExcelPolicy policy;/** 分片大小*/protected int shardingNum;/** 导入设置参数*/protected ImportParams importParams new ImportParams();/**文件服务*/protected IFileFeignService fileFeignService=SpringUtils.getBean(IFileFeignService.class);/** user服务*/protected IUserFeignService userFeignService SpringUtils.getBean(IUserFeignService .class);/** 构造函数*/public AbstractAsyncExcelHandler(InputStream in,Object object,String importId,String userId,AsyncExcelPolicy policy,int shardingNum){this.in = in;this.object = object;this.importId = importId;this.userId = userId;this.policypolicy = policy;this.shardingNum = shardingNum;}@Overridepublic void importExcel(){//1导入之前设置参效resetImportParams();setDataHandler(new DefaultDataHandler());setDictHandler(new DefaultExcelDictHandler());needverify(true);ParameterizedType parameterizedType = (ParameterizedType) this.getclass().getGenericSuperclass();Class clazz = (Class) parameterizedType.getActualTypeArguments()[0];ExcelImportResult<T> result;ByteArrayOutputStream bos = new ByteArrayOutputStream();ByteArrayInputStream bis = null;try {//复制一份流用于输出校验结果IOUtils.copy(in,bos);bis = new ByteArrayInputStream(bos.toByteArray());result = ExcelImportUtil.importExceLMore(bis,clazz,importParams);}catch (Exception ex) {IOUtils.closeQuietly(this.in);IOUtils.cLoseQuietly(bos);IOUtils.closeQuietLy(bis);log,error("excel解析失败“,ex);// 更新异常状态updateMessage4Exception(ex.getMessage());throw new ServiceException(BizExceptionCommonEnum.POI_ERROR)}//导入后处理//注解校验结哭if (result.isverifyFail()) {// 校验结果处理postverifyFailed(result,bos,bis);return;}// 校验是否实现serviceIAsyncExcelService service = getService();if (Objects.isNuLL(service)){Log.error("业务服务未返回service对象");//更新导入异常状态updateMessage4Exception("业务服务未实现");throw new ServiceException(500,"业务服务未实现");}// 业务校验service.verify(result);if (result.isVerifyFail()){//校验结果处理postverifyFailed(result,bos,bis);return;}//业务逻辑runPolicy(result.getList());}// 策略运行protected void runPolicy(List<T> list){try {policy.runPolicy(list,shardingNum,getService(),importId);} catch (Exception ex){//标识异常Log.error("业务数据插入异常。",e×);UserAsyncExcel userAsyncExcel new UserAsyncExcel();userAsyncExcel.setId(importId);userAsyncExcel.setstatus(AsyncExcelStatus.EXCEPTION);userAsyncExcel.setMessage("分片号入发生异常");userFeignService.update(userAsvncExcel):}}// 校验失败后置处理private void postVerifyFailed(ExcelImportResult<T> result,ByteArrayOutputstream bos,ByteArrayInputstream bis){try {List<T> failList = result.getFailList();bis.reset();Workbook workbook = WorkbookFactory.create(bis);Sheet sheet = workbook.getsheetAt(index:0);int titleRowNum = importParams.getTitleRows(>+importParams.getHeadRows();short lastCellNum = sheet.getRow(titleRowNum).getLastCellNum();CellStyle errorCellStyle = createCellStyle(workbook);for (T failEntity failList){Integer rowNum failEntity.getRowNum();Rowrow sheet.getRow(rowNum)jCell cell row.createCell(lastCellNum);cell.setCellValue(failEntity.getErrorMsg());cell.setCellStyle(errorCellStyle);}workbook.getsheetAt(0).setColumnwidth(lastCellNum, 5000);UserAsyncExcel userAsyncExcel = userFeignService.getById(importId).getData();// 流清空,重用bos.flush();bos.reset();workbook.write(bos);Map<String,String> errFileMap = uploadVerifyFailedExcel(bos,userAsyncExcel.getTitle());// 更新导入结果为校验失败String fileId = String.join(StringPool.COMMA,errFileMap.keySet());userAsyncExcel.setstatus(AsyncExcelStatus.VERIFY_FATLED);userAsyncExcel.setResFileId(fileId);userFeignService.update(userAsyncExcel);}catch (IOException ex) {IOUtils.closeQuietly(bos);IOUtils.cLoseQuietly(bis);Log.error("设置校验结果异常",ex);/更新导入异常状态updateMessage4Exception(ex.getMessage());throw new ServiceException(5OB,"业务校验失败")} finally {Log.info("原始流复件关闭");IOUtils.cLoseQuietly(bos);IOUtils.cLoseQuietly(bis);}}private Map<String,String>uploadVerifyFailedExcel(ByteArrayOutputStream erroros, String fileName){long time System.currentTimeMiLLis();int idx fileName.lastIndexof(StringPool.DOT);String pre fileName.substring(0,idx);String suffer fileName.substring(idx);fileName pre time suffer;MultipartFile[]multipartFiles new MultipartFile[]{new MockMultipartFile(Constants.ExcelConstants.UPLOAD_FILE_FIELD,fileName,MediaType.MUL TIPART_FOR_DATA_VALUE,erroros.toByteArray())};return fileFeignService.uploadTemp(multipartFiles).getData();}protected Cellstyle createCellStyle(Workbook workbook){CellStyle errorCellStyle workbook.createCellstyle();Font font workbook.createFont();font.setColor(Font.COLOR_RED);errorCellStyle.setFont(font);// 背景色// 设置背景色填充方式为实线填充// errorCeLlStyle.setFiLLPattern(FillPatternType.SOLID_FOREGROUND);// errorceLlStyle.setFiLLForegroundcolor(IndexedColors.RED.getIndex());errorCellstyle.setWrapText(true);return errorCellStyle;}private void updateMessage4Exception(String message){UserAsyncExcel userAsyncExcel = new UserAsyncExcel();userAsyncExcel.setId(importId);userAsyncExcel.setStatus(AsyncExcelStatus.EXCEPTION);userAsyncExcel.setMessage(message);userFeignService.update(userAsyncExcel);}
@Override
public void resetImportParams(){//设置标题行数this.importParams.setTitleRows(1);//设置表头行数this.importParams.setHeadRows(2);
}@Overridepublic void setDataHandler(IExcelDataHandler dataHandler){this.importParams.setDataHandler(dataHandler)};@Overridepublic void setDictHandler(IExcelDictHandler dictHandler){this.importParams.setDictHandler(dictHandler)};@Overridepublic void needverify(boolean needVerify){this.importParams.setNeedVerify(needVerify)};}

业务逻辑处理service接口

IAsyncExcelService

public interface IAsyncExcelService<T extends ExcelImportBaseEntity>{/** 业务校验* @param result*/void verify(ExcelImportResult<T> result);/*** 处理导入数据**/void dealResultData(List<T> list);
}

策略模型

AsyncExcelPolicy 策略接口
规范策略实现

public interface IAsyncExcelService<T extends ExcelImportBaseEntity> {/*** 业务校验*/void verify(ExcelImportResult<T> result);/*** 处理导入数据*/void dealResultData(List<T> list);
}

ShardingPolicy分片策略

Public class phardangPolicy implements AsyncExcelPolicy{@Overridepublie void runPolicy(List list,int shardingNun,IAsyncExcelService service,String impertId){//数据分片NongoTerplate mongoTeplate = Springutils.getBean(NangoTerplate.class);List<List> splitList = Listutil.splitlist(shardinghun,list);List<AsyncExcelShardingEntity> nongoEntityList = splitList.streas().map(v->{AsyncExcelShardingEntity entity new AsyncExcelshardingEntity(entity.setImportId(impertId);entity.setshardingId(Uuidutils.getId());entity.setSucceeded(falae);entity.setData(v);return entity;}).collect(Collectors.tolist());// 分片数据存入mongomongoTerplate.insertAll(mongoEntityList);Log.info("分片数据:{}", ongoEntitylist);//分片进入业务导入方法for (AsyncExcelShardingEntity entity mangofntitylist){List subList = (List) entity.gotData();service.dealResultData(subList);// 执行成功成功删除数据mangoTenplate.remove(entity);}}
}

小结

业务服务使用只需要三步

  1. 配置对应的handler权限定名。
  2. 实现handler类,根据自己的excel样式设置参数,用于解析excel数据。
  3. 实现service服务接口,实现校验数据方法和导入方法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/123566.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Go学习第十六章——Gin文件上传与下载

Go web框架——Gin文件上传与下载 1. 文件上传1.1 入门案例&#xff08;单文件&#xff09;1.2 服务端保存文件的几种方式SaveUploadedFileCreateCopy 1.3 读取上传的文件1.4 多文件上传 2. 文件下载2.1 快速入门2.2 前后端模式下的文件下载2.3 中文乱码问题 1. 文件上传 1.1 …

计算机毕业设计选题推荐-周边美食推荐微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

kafka3.X基本概念和使用

kafka基本概念和使用 文章目录 kafka基本概念和使用 kafka的概念基本概念Kafka的使用 首先kafka的安装kafka的简单实用和理解搭建集群&#xff08;3个节点&#xff09;windows版本环境搭建 本文"kafka的概念"部分是在[初谈Kafka][ https://juejin.im/post/5a8e7f…

VulnHub DC-1

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

微信小程序 - 页面继承(非完美解决方案)

微信小程序 - 面页继承&#xff08;非完美解决方案&#xff09; 废话思路首页 indexindex.jsindex.jsonindex.wxml 父页面 page-basepage-base.jspage-base.wxml 子页面 page-apage-a.jspage-a.wxml 子页面 page-bpage-b.jspage-b.wxml 其它app.jsapp.jsonapp.wxss 参考资料 废…

BI是什么?想要了解BI需要从哪些方面入手?

企业为了执行数字化战略&#xff0c;实行数字化转型&#xff0c;实现数据价值&#xff0c;除了需要相关数字化技术及理念、人才等&#xff0c;还需要借助数字化相关应用&#xff0c;例如商业世界中广受企业欢迎的ERP、OA、CRM等业务信息系统&#xff0c;以及上升势头非常迅猛的…

【STM32】STM32中断体系

一、STM32的NVIC和起始代码中的ISP 1.NVIC(嵌套向量中断控制器) (1)数据手册中相关部分浏览 (2)地址映射时0地址映射到Flash或SRAM (3)中断向量表可以被人为重新映射&#xff0c;一般用来IAP中 (4)STM32采用一维的中断向量表 (5)中断优先级设置有点复杂&#xff0c;后面细说 1…

【排序算法】 归并排序详解!分治思想!

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; 算法—排序篇 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言&#x1f324;️归并排序的思想☁️基本思想☁️归并的思想实现☁️分治法 &#x1f3…

IOC课程整理-20 Spring 应用上下文生命周期

0.目录 1. Spring 应用上下文启动准备阶段 2. BeanFactory 创建阶段 3. BeanFactory 准备阶段 4. BeanFactory 后置处理阶段 5. BeanFactory 注册 BeanPostProcessor 阶段 6. 初始化內建 Bean&#xff1a;MessageSource 7. 初始化內建 Bean&#xff1a;Spring 事件广播器…

计算机毕业设计选题推荐-戏曲文化苑微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

Springboot 使用JavaMailSender发送邮件 + Excel附件

目录 1.生成Excel表格 1.依赖设置 2.代码&#xff1a; 2.邮件发送 1.邮件发送功能实现-带附件 2.踩过的坑 1.附件名中文乱码问题 3.参考文章&#xff1a; 需求描述&#xff1a;项目审批完毕后&#xff0c;需要发送邮件通知相关人员&#xff0c;并且要附带数据库表生成的…

Linux C/C++ 实现网络流量分析(性能工具)

网络流量分析的原理基于对数据包的捕获、解析和统计分析&#xff0c;通过对网络流量的细致观察和分析&#xff0c;帮助管理员了解和优化网络的性能、提高网络安全性&#xff0c;并快速排查和解决网络故障和问题。 Linux中的网络流量常见类型 在Linux中&#xff0c;网络流量可以…

校园物业报修小程序开发笔记一

背景 校园规模和复杂性&#xff1a; 大型学校和校园通常拥有众多的建筑物、设施和设备&#xff0c;需要有效的维护和报修系统&#xff0c;以满足学生、教职员工和校园管理人员的需求。 学生和员工需求&#xff1a; 学生和员工在校园内可能遇到各种维修问题&#xff0c;如故障的…

ESP8266,手机与电脑之间的TCP通讯

电脑端运行通讯猫调试助手,作为服务端: 电脑端 电脑的IP地址是: 192.168.2.232 手机与电脑之间的TCP通讯 手机端运行网络调试精灵,作为客户端: 手机端 如果从手机端点击"发送"按钮,则也会将"ghhh东方红广场"几个字发送到电脑上(服务端). ESP8266作为客户…

elasticsearch一些重要的配置参数

先看一下官网给我们提供的全部的参数配置项 官网地址 官方文档链接&#xff1a;注意版本是8.1Configuring Elasticsearch | Elasticsearch Guide [8.1] | Elastic​编辑https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html 重要&#xff08;基本…

【2023Mathorcup大数据】B题 电商零售商家需求预测及库存优化问题 python代码解析

【2023Mathorcup大数据】B题 电商零售商家需求预测及库存优化问题 python代码解析 1 题目 2023 年MathorCup 高校数学建模挑战赛——大数据竞赛赛道B&#xff1a;电商零售商家需求预测及库存优化问题电商平台存在着上千个商家&#xff0c;他们会将商品货物放在电商配套的仓库…

通道洗牌的思想神了

大家好啊&#xff0c;我是董董灿。 昨天写了一篇关于分组卷积的文章&#xff1a;分组卷积的思想神了&#xff0c;然后有同学希望多了解下通道洗牌。 我个人感觉&#xff0c;通道洗牌这个算法&#xff0c;或者说这个思想&#xff0c;可以称之为小而精&#xff0c;并且是实际解…

rust 创建多线程web server

创建一个 http server&#xff0c;处理 http 请求。 创建一个单线程的 web 服务 web server 中主要的两个协议是 http 和 tcp。tcp 是底层协议&#xff0c;http 是构建在 tcp 之上的。 通过std::net库创建一个 tcp 连接的监听对象&#xff0c;监听地址为127.0.0.1:8080. us…

NEFU数字图像处理(三)图像分割

一、图像分割的基本概念 1.1专有名词 前景和背景 在图像分割中&#xff0c;我们通常需要将图像分为前景和背景两个部分。前景是指图像中我们感兴趣、要分割出来的部分&#xff0c;背景是指和前景不相关的部分。例如&#xff0c;对于一张人物照片&#xff0c;人物就是前景&…

python把ChestX-Det-Dataset的json样本转为COCO数据集的json格式

ChestX-Det-Dataset数据集网址&#xff1a;https://github.com/Deepwise-AILab/ChestX-Det-Dataset/tree/main 数据集JSON内容&#xff1a; [{"file_name": "36199.png","syms": [],"boxes": [],"polygons": []},{"f…