From 5c613b468bf915b003ea81f3ca3154c10373ca58 Mon Sep 17 00:00:00 2001 From: liuchen864 <23082234@qq.com> Date: Wed, 11 Oct 2023 17:06:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BB=E5=AD=90=E8=A1=A8=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=B0=81=E8=A3=85=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../excel/core/annotations/OnlyOne.java | 15 ++ .../excel/core/annotations/SubObject.java | 15 ++ .../excel/core/listener/ExcelListener.java | 177 ++++++++++++++++++ .../framework/excel/core/util/ExcelUtils.java | 10 +- 4 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/annotations/OnlyOne.java create mode 100644 win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/annotations/SubObject.java create mode 100644 win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/listener/ExcelListener.java diff --git a/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/annotations/OnlyOne.java b/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/annotations/OnlyOne.java new file mode 100644 index 00000000..f4a27776 --- /dev/null +++ b/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/annotations/OnlyOne.java @@ -0,0 +1,15 @@ +package com.win.framework.excel.core.annotations; + +import java.lang.annotation.*; + +/** + * 字典格式化 + * + * 实现将字典数据的值,格式化成字典数据的标签 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface OnlyOne { + +} diff --git a/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/annotations/SubObject.java b/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/annotations/SubObject.java new file mode 100644 index 00000000..bcb94f08 --- /dev/null +++ b/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/annotations/SubObject.java @@ -0,0 +1,15 @@ +package com.win.framework.excel.core.annotations; + +import java.lang.annotation.*; + +/** + * 字典格式化 + * + * 实现将字典数据的值,格式化成字典数据的标签 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface SubObject { + +} diff --git a/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/listener/ExcelListener.java b/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/listener/ExcelListener.java new file mode 100644 index 00000000..73749217 --- /dev/null +++ b/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/listener/ExcelListener.java @@ -0,0 +1,177 @@ +package com.win.framework.excel.core.listener; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ReflectUtil; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.google.common.base.Objects; +import com.win.framework.common.exception.ServiceException; +import com.win.framework.excel.core.annotations.OnlyOne; +import com.win.framework.excel.core.annotations.SubObject; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@Data +@Slf4j +public class ExcelListener extends AnalysisEventListener { + + /** + * excel数据 + */ + private List dataList; + + /** + * 判断重复的属性方法 + */ + private String methodName; + + /** + * 子类 + */ + private String subObjectMethodName; + + /** + * 主表class + */ + private Class mainClass; + + /** + * 子表class + */ + private Class subClass; + + /** + * 构造函数 + */ + public ExcelListener(Class mainClass) { + dataList = new ArrayList<>(); + this.mainClass = mainClass; + } + + @Override + public void invoke(T object, AnalysisContext analysisContext) { + if(this.methodName == null) { + String methodName = this.getOnlyOneAnnotation(); + if(methodName == null) { + throw new ServiceException().setMessage("未发现OnlyOne注解属性"); + } + this.methodName = methodName; + } + if(this.subObjectMethodName == null) { + String subObjectMethodName = this.getSubObjectAnnotation(); + if(subObjectMethodName == null) { + throw new ServiceException().setMessage("未发现SubObject注解属性"); + } + this.subObjectMethodName = subObjectMethodName; + } + this.buildEntity(object); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) { + + } + + /** + * 判断数据是否存在 + */ + private Object checkDataIsExist(T object) { + for(Object obj : dataList) { + Object methodValue1 = ReflectUtil.invoke(obj, "get" + this.methodName); + Object methodValue2 = ReflectUtil.invoke(object, "get" + this.methodName); + if(methodValue1.equals(methodValue2)) { + return obj; + } + } + return null; + } + + /** + * 添加数据 + * @param vo + */ + private void buildEntity(T vo) { + Object mainObject = this.checkDataIsExist(vo); + Object subObject; + try {//创建子数据对象 + subObject = this.subClass.getDeclaredConstructors()[0].newInstance((Object) null); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + BeanUtil.copyProperties(vo, subObject); + if(mainObject == null) {//list中不存在主数据 + try {//创建主数据对象 + mainObject = this.mainClass.getDeclaredConstructors()[0].newInstance((Object) null); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + BeanUtil.copyProperties(vo, mainObject); + Method setMethod = ReflectUtil.getMethod(this.mainClass, "set" + this.subObjectMethodName); + List subClassList = new ArrayList<>(); + subClassList.add(subObject); + ReflectUtil.invoke(mainObject, setMethod, subClassList); + dataList.add(mainObject); + } else {//list中存在主数据,只添加子数据 + Method getMethod = ReflectUtil.getMethod(this.mainClass, "get" + this.subObjectMethodName); + List subClassList = ReflectUtil.invoke(mainObject, getMethod); + subClassList.add(subObject); + Method setMethod = ReflectUtil.getMethod(this.mainClass, "set" + this.subObjectMethodName); + ReflectUtil.invoke(mainObject, setMethod, subClassList); + } + } + + private String getOnlyOneAnnotation() { + Field[] fields = mainClass.getDeclaredFields(); + for (Field field : fields) { + // 只判断该字段拥有非空注解 + if (AnnotationUtil.hasAnnotation(field, OnlyOne.class)){ + //获取属性的名字 + String attributeName = field.getName(); + //将属性名字的首字母大写 + return Character.toUpperCase(attributeName.charAt(0)) + attributeName.substring(1); + } + } + return null; + } + + private String getSubObjectAnnotation() { + Object object; + try { + object = mainClass.getDeclaredConstructors()[0].newInstance((Object) null); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + Field[] fields = object.getClass().getDeclaredFields(); + for (Field field : fields) { + // 只判断该字段拥有非空注解 + if (AnnotationUtil.hasAnnotation(field, SubObject.class)) { + Class fieldType = field.getType(); + if (Collection.class.isAssignableFrom(fieldType)) { + java.lang.reflect.Type[] actualType = ((ParameterizedType) field.getGenericType()).getActualTypeArguments(); + if (actualType.length != 1 || actualType[0] == null || actualType[0].toString().length() < 5 || !Objects.equal(actualType[0].toString().substring(0, 5).toLowerCase(), "class")){ + // 非class类型的不处理 + throw new ServiceException().setMessage("SubObject非泛型"); + } + this.subClass = actualType[0].getClass(); + } else { + throw new ServiceException().setMessage("SubObject注解属性必须是Collection实现类"); + } + //获取属性的名字 + String attributeName = field.getName(); + //将属性名字的首字母大写 + return Character.toUpperCase(attributeName.charAt(0)) + attributeName.substring(1); + } + } + return null; + } + +} diff --git a/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/util/ExcelUtils.java b/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/util/ExcelUtils.java index 575ecc11..183ce3a4 100644 --- a/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/util/ExcelUtils.java +++ b/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/util/ExcelUtils.java @@ -1,6 +1,7 @@ package com.win.framework.excel.core.util; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import org.springframework.web.multipart.MultipartFile; @@ -27,8 +28,7 @@ public class ExcelUtils { * @param 泛型,保证 head 和 data 类型的一致性 * @throws IOException 写入失败的情况 */ - public static void write(HttpServletResponse response, String filename, String sheetName, - Class head, List data) throws IOException { + public static void write(HttpServletResponse response, String filename, String sheetName, Class head, List data) throws IOException { // 输出 Excel EasyExcel.write(response.getOutputStream(), head) .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 @@ -45,4 +45,10 @@ public class ExcelUtils { .doReadAllSync(); } + public static List read(MultipartFile file, Class head, AnalysisEventListener listener) throws IOException { + return EasyExcel.read(file.getInputStream(), head, listener) + .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 + .doReadAllSync(); + } + }