feat: initial iShare project code

This commit is contained in:
purovps
2026-02-16 23:20:59 +08:00
parent 8c83a6fd46
commit 6f270a972e
1910 changed files with 218015 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>5.2.0</version>
</parent>
<artifactId>pigx-common-excel</artifactId>
<packaging>jar</packaging>
<description>pigx excel 操作</description>
<properties>
<commons-compress>1.21</commons-compress>
<easyexcel.version>3.1.1</easyexcel.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--引入AOP依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>${commons-compress}</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,83 @@
package com.pig4cloud.pigx.common.excel;
import com.alibaba.excel.converters.Converter;
import com.pig4cloud.pigx.common.excel.aop.ResponseExcelReturnValueHandler;
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
import com.pig4cloud.pigx.common.excel.enhance.DefaultWriterBuilderEnhancer;
import com.pig4cloud.pigx.common.excel.enhance.WriterBuilderEnhancer;
import com.pig4cloud.pigx.common.excel.handler.ManySheetWriteHandler;
import com.pig4cloud.pigx.common.excel.handler.SheetWriteHandler;
import com.pig4cloud.pigx.common.excel.handler.SingleSheetWriteHandler;
import com.pig4cloud.pigx.common.excel.head.I18nHeaderCellWriteHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import java.util.List;
/**
* @author Hccake 2020/10/28
* @version 1.0
*/
@RequiredArgsConstructor
public class ExcelHandlerConfiguration {
private final ExcelConfigProperties configProperties;
private final ObjectProvider<List<Converter<?>>> converterProvider;
/**
* ExcelBuild增强
* @return DefaultWriterBuilderEnhancer 默认什么也不做的增强器
*/
@Bean
@ConditionalOnMissingBean
public WriterBuilderEnhancer writerBuilderEnhancer() {
return new DefaultWriterBuilderEnhancer();
}
/**
* 单sheet 写入处理器
*/
@Bean
@ConditionalOnMissingBean
public SingleSheetWriteHandler singleSheetWriteHandler() {
return new SingleSheetWriteHandler(configProperties, converterProvider, writerBuilderEnhancer());
}
/**
* 多sheet 写入处理器
*/
@Bean
@ConditionalOnMissingBean
public ManySheetWriteHandler manySheetWriteHandler() {
return new ManySheetWriteHandler(configProperties, converterProvider, writerBuilderEnhancer());
}
/**
* 返回Excel文件的 response 处理器
* @param sheetWriteHandlerList 页签写入处理器集合
* @return ResponseExcelReturnValueHandler
*/
@Bean
@ConditionalOnMissingBean
public ResponseExcelReturnValueHandler responseExcelReturnValueHandler(
List<SheetWriteHandler> sheetWriteHandlerList) {
return new ResponseExcelReturnValueHandler(sheetWriteHandlerList);
}
/**
* excel 头的国际化处理器
* @param messageSource 国际化源
*/
@Bean
@ConditionalOnBean(MessageSource.class)
@ConditionalOnMissingBean
public I18nHeaderCellWriteHandler i18nHeaderCellWriteHandler(MessageSource messageSource) {
return new I18nHeaderCellWriteHandler(messageSource);
}
}

View File

@@ -0,0 +1,87 @@
package com.pig4cloud.pigx.common.excel;
import com.pig4cloud.pigx.common.excel.aop.DynamicNameAspect;
import com.pig4cloud.pigx.common.excel.aop.RequestExcelArgumentResolver;
import com.pig4cloud.pigx.common.excel.aop.ResponseExcelReturnValueHandler;
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
import com.pig4cloud.pigx.common.excel.processor.NameProcessor;
import com.pig4cloud.pigx.common.excel.processor.NameSpelExpressionProcessor;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
/**
* @author lengleng
* @date 2020/3/29
* <p>
* 配置初始化
*/
@AutoConfiguration
@RequiredArgsConstructor
@Import(ExcelHandlerConfiguration.class)
@EnableConfigurationProperties(ExcelConfigProperties.class)
public class ResponseExcelAutoConfiguration {
private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;
private final ResponseExcelReturnValueHandler responseExcelReturnValueHandler;
/**
* SPEL 解析处理器
* @return NameProcessor excel名称解析器
*/
@Bean
@ConditionalOnMissingBean
public NameProcessor nameProcessor() {
return new NameSpelExpressionProcessor();
}
/**
* Excel名称解析处理切面
* @param nameProcessor SPEL 解析处理器
* @return DynamicNameAspect
*/
@Bean
@ConditionalOnMissingBean
public DynamicNameAspect dynamicNameAspect(NameProcessor nameProcessor) {
return new DynamicNameAspect(nameProcessor);
}
/**
* 追加 Excel返回值处理器 到 springmvc 中
*/
@PostConstruct
public void setReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> returnValueHandlers = requestMappingHandlerAdapter
.getReturnValueHandlers();
List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<>();
newHandlers.add(responseExcelReturnValueHandler);
assert returnValueHandlers != null;
newHandlers.addAll(returnValueHandlers);
requestMappingHandlerAdapter.setReturnValueHandlers(newHandlers);
}
/**
* 追加 Excel 请求处理器 到 springmvc 中
*/
@PostConstruct
public void setRequestExcelArgumentResolver() {
List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
List<HandlerMethodArgumentResolver> resolverList = new ArrayList<>();
resolverList.add(new RequestExcelArgumentResolver());
resolverList.addAll(argumentResolvers);
requestMappingHandlerAdapter.setArgumentResolvers(resolverList);
}
}

View File

@@ -0,0 +1,10 @@
package com.pig4cloud.pigx.common.excel.annotation;
import java.lang.annotation.*;
@Documented
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelLine {
}

View File

@@ -0,0 +1,43 @@
package com.pig4cloud.pigx.common.excel.annotation;
import com.pig4cloud.pigx.common.excel.handler.DefaultAnalysisEventListener;
import com.pig4cloud.pigx.common.excel.handler.ListAnalysisEventListener;
import java.lang.annotation.*;
/**
* 导入excel
*
* @author lengleng
* @author L.cm
* @date 2021/4/16
*/
@Documented
@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestExcel {
/**
* 前端上传字段名称 file
*/
String fileName() default "file";
/**
* 读取的监听器类
* @return readListener
*/
Class<? extends ListAnalysisEventListener<?>> readListener() default DefaultAnalysisEventListener.class;
/**
* 是否跳过空行
* @return 默认跳过
*/
boolean ignoreEmptyRow() default false;
/**
* 指定读取的标题行
* @return
*/
int headRowNumber() default 1;
}

View File

@@ -0,0 +1,98 @@
package com.pig4cloud.pigx.common.excel.annotation;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.handler.WriteHandler;
import com.pig4cloud.pigx.common.excel.head.HeadGenerator;
import java.lang.annotation.*;
/**
* `@ResponseExcel 注解`
*
* @author lengleng
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ResponseExcel {
/**
* 文件名称
* @return string
*/
String name() default "";
/**
* 文件类型 xlsx xls
* @return string
*/
ExcelTypeEnum suffix() default ExcelTypeEnum.XLSX;
/**
* 文件密码
* @return password
*/
String password() default "";
/**
* sheet 名称,支持多个
* @return String[]
*/
Sheet[] sheets() default @Sheet(sheetName = "sheet1");
/**
* 内存操作
* @return
*/
boolean inMemory() default false;
/**
* excel 模板
* @return String
*/
String template() default "";
/**
* + 包含字段
* @return String[]
*/
String[] include() default {};
/**
* 排除字段
* @return String[]
*/
String[] exclude() default {};
/**
* 拦截器,自定义样式等处理器
* @return WriteHandler[]
*/
Class<? extends WriteHandler>[] writeHandler() default {};
/**
* 转换器
* @return Converter[]
*/
Class<? extends Converter>[] converter() default {};
/**
* 自定义Excel头生成器
* @return HeadGenerator
*/
Class<? extends HeadGenerator> headGenerator() default HeadGenerator.class;
/**
* excel 头信息国际化
* @return boolean
*/
boolean i18nHeader() default false;
/**
* 填充模式
* @return
*/
boolean fill() default false;
}

View File

@@ -0,0 +1,41 @@
package com.pig4cloud.pigx.common.excel.annotation;
import com.pig4cloud.pigx.common.excel.head.HeadGenerator;
import java.lang.annotation.*;
/**
* @author Yakir
* @Topic Sheet
* @Description
* @date 2021/4/29 15:03
* @Version 1.0
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Sheet {
int sheetNo() default -1;
/**
* sheet name
*/
String sheetName();
/**
* 包含字段
*/
String[] includes() default {};
/**
* 排除字段
*/
String[] excludes() default {};
/**
* 头生成器
*/
Class<? extends HeadGenerator> headGenerateClass() default HeadGenerator.class;
}

View File

@@ -0,0 +1,46 @@
package com.pig4cloud.pigx.common.excel.aop;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.excel.processor.NameProcessor;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* @author lengleng
* @date 2020/3/29
*/
@Aspect
@RequiredArgsConstructor
public class DynamicNameAspect {
public static final String EXCEL_NAME_KEY = "__EXCEL_NAME_KEY__";
private final NameProcessor processor;
@Before("@annotation(excel)")
public void around(JoinPoint point, ResponseExcel excel) {
MethodSignature ms = (MethodSignature) point.getSignature();
String name = excel.name();
// 当配置的 excel 名称为空时,取当前时间
if (!StringUtils.hasText(name)) {
name = LocalDateTime.now().toString();
}
else {
name = processor.doDetermineName(point.getArgs(), ms.getMethod(), excel.name());
}
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
Objects.requireNonNull(requestAttributes).setAttribute(EXCEL_NAME_KEY, name, RequestAttributes.SCOPE_REQUEST);
}
}

View File

@@ -0,0 +1,89 @@
package com.pig4cloud.pigx.common.excel.aop;
import com.alibaba.excel.EasyExcel;
import com.pig4cloud.pigx.common.excel.annotation.RequestExcel;
import com.pig4cloud.pigx.common.excel.converters.*;
import com.pig4cloud.pigx.common.excel.handler.ListAnalysisEventListener;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.util.List;
/**
* 上传excel 解析注解
*
* @author lengleng
* @author L.cm
* @date 2021/4/16
*/
@Slf4j
public class RequestExcelArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestExcel.class);
}
@Override
@SneakyThrows(Exception.class)
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer modelAndViewContainer,
NativeWebRequest webRequest, WebDataBinderFactory webDataBinderFactory) {
Class<?> parameterType = parameter.getParameterType();
if (!parameterType.isAssignableFrom(List.class)) {
throw new IllegalArgumentException(
"Excel upload request resolver error, @RequestExcel parameter is not List " + parameterType);
}
// 处理自定义 readListener
RequestExcel requestExcel = parameter.getParameterAnnotation(RequestExcel.class);
assert requestExcel != null;
Class<? extends ListAnalysisEventListener<?>> readListenerClass = requestExcel.readListener();
ListAnalysisEventListener<?> readListener = BeanUtils.instantiateClass(readListenerClass);
// 获取请求文件流
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
assert request != null;
InputStream inputStream;
if (request instanceof MultipartRequest) {
MultipartFile file = ((MultipartRequest) request).getFile(requestExcel.fileName());
assert file != null;
inputStream = file.getInputStream();
}
else {
inputStream = request.getInputStream();
}
// 获取目标类型
Class<?> excelModelClass = ResolvableType.forMethodParameter(parameter).getGeneric(0).resolve();
// 这里需要指定读用哪个 class 去读,然后读取第一个 sheet 文件流会自动关闭
EasyExcel.read(inputStream, excelModelClass, readListener).registerConverter(LocalDateStringConverter.INSTANCE)
.registerConverter(LocalTimeStringConverter.INSTANCE)
.registerConverter(LocalDateTimeStringConverter.INSTANCE)
.registerConverter(LongStringConverter.INSTANCE).registerConverter(StringArrayConverter.INSTANCE)
.ignoreEmptyRow(requestExcel.ignoreEmptyRow()).sheet().headRowNumber(requestExcel.headRowNumber())
.doRead();
// 校验失败的数据处理 交给 BindResult
WebDataBinder dataBinder = webDataBinderFactory.createBinder(webRequest, readListener.getErrors(), "excel");
ModelMap model = modelAndViewContainer.getModel();
model.put(BindingResult.MODEL_KEY_PREFIX + "excel", dataBinder.getBindingResult());
return readListener.getList();
}
}

View File

@@ -0,0 +1,58 @@
package com.pig4cloud.pigx.common.excel.aop;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.excel.handler.SheetWriteHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 处理@ResponseExcel 返回值
*
* @author lengleng
*/
@Slf4j
@RequiredArgsConstructor
public class ResponseExcelReturnValueHandler implements HandlerMethodReturnValueHandler {
private final List<SheetWriteHandler> sheetWriteHandlerList;
/**
* 只处理@ResponseExcel 声明的方法
* @param parameter 方法签名
* @return 是否处理
*/
@Override
public boolean supportsReturnType(MethodParameter parameter) {
return parameter.getMethodAnnotation(ResponseExcel.class) != null;
}
/**
* 处理逻辑
* @param o 返回参数
* @param parameter 方法签名
* @param mavContainer 上下文容器
* @param nativeWebRequest 上下文
*/
@Override
public void handleReturnValue(Object o, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest nativeWebRequest) {
/* check */
HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class);
Assert.state(response != null, "No HttpServletResponse");
ResponseExcel responseExcel = parameter.getMethodAnnotation(ResponseExcel.class);
Assert.state(responseExcel != null, "No @ResponseExcel");
mavContainer.setRequestHandled(true);
sheetWriteHandlerList.stream().filter(handler -> handler.support(o)).findFirst()
.ifPresent(handler -> handler.export(o, response, responseExcel));
}
}

View File

@@ -0,0 +1,21 @@
package com.pig4cloud.pigx.common.excel.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author lengleng
* @date 2020/3/29
*/
@Data
@ConfigurationProperties(prefix = ExcelConfigProperties.PREFIX)
public class ExcelConfigProperties {
static final String PREFIX = "excel";
/**
* 模板路径
*/
private String templatePath = "excel";
}

View File

@@ -0,0 +1,62 @@
package com.pig4cloud.pigx.common.excel.converters;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
/**
* LocalDate and string converter
*
* @author L.cm
*/
public enum LocalDateStringConverter implements Converter<LocalDate> {
/**
* 实例
*/
INSTANCE;
@Override
public Class supportJavaTypeKey() {
return LocalDate.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public LocalDate convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws ParseException {
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
return LocalDate.parse(cellData.getStringValue());
}
else {
DateTimeFormatter formatter = DateTimeFormatter
.ofPattern(contentProperty.getDateTimeFormatProperty().getFormat());
return LocalDate.parse(cellData.getStringValue(), formatter);
}
}
@Override
public WriteCellData<String> convertToExcelData(LocalDate value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
DateTimeFormatter formatter;
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
formatter = DateTimeFormatter.ISO_LOCAL_DATE;
}
else {
formatter = DateTimeFormatter.ofPattern(contentProperty.getDateTimeFormatProperty().getFormat());
}
return new WriteCellData<>(value.format(formatter));
}
}

View File

@@ -0,0 +1,94 @@
package com.pig4cloud.pigx.common.excel.converters;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.DateUtils;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* LocalDateTime and string converter
*
* @author L.cm
*/
public enum LocalDateTimeStringConverter implements Converter<LocalDateTime> {
/**
* 实例
*/
INSTANCE;
private static final String MINUS = "-";
@Override
public Class supportJavaTypeKey() {
return LocalDateTime.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public LocalDateTime convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws ParseException {
String stringValue = cellData.getStringValue();
String pattern;
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
pattern = switchDateFormat(stringValue);
}
else {
pattern = contentProperty.getDateTimeFormatProperty().getFormat();
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return LocalDateTime.parse(cellData.getStringValue(), formatter);
}
@Override
public WriteCellData<String> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
String pattern;
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
pattern = DateUtils.DATE_FORMAT_19;
}
else {
pattern = contentProperty.getDateTimeFormatProperty().getFormat();
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return new WriteCellData<>(value.format(formatter));
}
/**
* switch date format
* @param dateString dateString
* @return pattern
*/
private static String switchDateFormat(String dateString) {
int length = dateString.length();
switch (length) {
case 19:
if (dateString.contains(MINUS)) {
return DateUtils.DATE_FORMAT_19;
}
else {
return DateUtils.DATE_FORMAT_19_FORWARD_SLASH;
}
case 17:
return DateUtils.DATE_FORMAT_17;
case 14:
return DateUtils.DATE_FORMAT_14;
case 10:
return DateUtils.DATE_FORMAT_10;
default:
throw new IllegalArgumentException("can not find date format for" + dateString);
}
}
}

View File

@@ -0,0 +1,62 @@
package com.pig4cloud.pigx.common.excel.converters;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import java.text.ParseException;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
/**
* LocalDate and string converter
*
* @author L.cm
*/
public enum LocalTimeStringConverter implements Converter<LocalTime> {
/**
* 实例
*/
INSTANCE;
@Override
public Class supportJavaTypeKey() {
return LocalTime.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public LocalTime convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws ParseException {
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
return LocalTime.parse(cellData.getStringValue());
}
else {
DateTimeFormatter formatter = DateTimeFormatter
.ofPattern(contentProperty.getDateTimeFormatProperty().getFormat());
return LocalTime.parse(cellData.getStringValue(), formatter);
}
}
@Override
public WriteCellData<String> convertToExcelData(LocalTime value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
DateTimeFormatter formatter;
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
formatter = DateTimeFormatter.ISO_LOCAL_TIME;
}
else {
formatter = DateTimeFormatter.ofPattern(contentProperty.getDateTimeFormatProperty().getFormat());
}
return new WriteCellData<>(value.format(formatter));
}
}

View File

@@ -0,0 +1,46 @@
package com.pig4cloud.pigx.common.excel.converters;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import java.text.ParseException;
/**
* Long and string converter
*
* @author L.cm
*/
public enum LongStringConverter implements Converter<Long> {
/**
* 实例
*/
INSTANCE;
@Override
public Class supportJavaTypeKey() {
return Long.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public Long convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws ParseException {
return Long.parseLong(cellData.getStringValue());
}
@Override
public WriteCellData<String> convertToExcelData(Long value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return new WriteCellData<>(String.valueOf(value));
}
}

View File

@@ -0,0 +1,48 @@
package com.pig4cloud.pigx.common.excel.converters;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import java.text.ParseException;
import java.util.Arrays;
import java.util.stream.Collectors;
/**
* LocalDate and string converter
*
* @author L.cm
*/
public enum StringArrayConverter implements Converter<String[]> {
/**
* 实例
*/
INSTANCE;
@Override
public Class supportJavaTypeKey() {
return String[].class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public String[] convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws ParseException {
return cellData.getStringValue().split(",");
}
@Override
public WriteCellData<String> convertToExcelData(String[] value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return new WriteCellData<>(Arrays.stream(value).collect(Collectors.joining()));
}
}

View File

@@ -0,0 +1,48 @@
package com.pig4cloud.pigx.common.excel.enhance;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.excel.head.HeadGenerator;
import javax.servlet.http.HttpServletResponse;
/**
* @author Hccake 2020/12/18
* @version 1.0
*/
public class DefaultWriterBuilderEnhancer implements WriterBuilderEnhancer {
/**
* ExcelWriterBuilder 增强
* @param writerBuilder ExcelWriterBuilder
* @param response HttpServletResponse
* @param responseExcel ResponseExcel
* @param templatePath 模板地址
* @return ExcelWriterBuilder
*/
@Override
public ExcelWriterBuilder enhanceExcel(ExcelWriterBuilder writerBuilder, HttpServletResponse response,
ResponseExcel responseExcel, String templatePath) {
// doNothing
return writerBuilder;
}
/**
* ExcelWriterSheetBuilder 增强
* @param writerSheetBuilder ExcelWriterSheetBuilder
* @param sheetNo sheet角标
* @param sheetName sheet名有模板时为空
* @param dataClass 当前写入的数据所属类
* @param template 模板文件
* @param headEnhancerClass 当前指定的自定义头处理器
* @return ExcelWriterSheetBuilder
*/
@Override
public ExcelWriterSheetBuilder enhanceSheet(ExcelWriterSheetBuilder writerSheetBuilder, Integer sheetNo,
String sheetName, Class<?> dataClass, String template, Class<? extends HeadGenerator> headEnhancerClass) {
// doNothing
return writerSheetBuilder;
}
}

View File

@@ -0,0 +1,42 @@
package com.pig4cloud.pigx.common.excel.enhance;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.excel.head.HeadGenerator;
import javax.servlet.http.HttpServletResponse;
/**
* ExcelWriterBuilder 增强
*
* @author Hccake 2020/12/18
* @version 1.0
*/
public interface WriterBuilderEnhancer {
/**
* ExcelWriterBuilder 增强
* @param writerBuilder ExcelWriterBuilder
* @param response HttpServletResponse
* @param responseExcel ResponseExcel
* @param templatePath 模板地址
* @return ExcelWriterBuilder
*/
ExcelWriterBuilder enhanceExcel(ExcelWriterBuilder writerBuilder, HttpServletResponse response,
ResponseExcel responseExcel, String templatePath);
/**
* ExcelWriterSheetBuilder 增强
* @param writerSheetBuilder ExcelWriterSheetBuilder
* @param sheetNo sheet角标
* @param sheetName sheet名有模板时为空
* @param dataClass 当前写入的数据所属类
* @param template 模板文件
* @param headEnhancerClass 当前指定的自定义头处理器
* @return ExcelWriterSheetBuilder
*/
ExcelWriterSheetBuilder enhanceSheet(ExcelWriterSheetBuilder writerSheetBuilder, Integer sheetNo, String sheetName,
Class<?> dataClass, String template, Class<? extends HeadGenerator> headEnhancerClass);
}

View File

@@ -0,0 +1,234 @@
package com.pig4cloud.pigx.common.excel.handler;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.excel.annotation.Sheet;
import com.pig4cloud.pigx.common.excel.aop.DynamicNameAspect;
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
import com.pig4cloud.pigx.common.excel.converters.*;
import com.pig4cloud.pigx.common.excel.enhance.WriterBuilderEnhancer;
import com.pig4cloud.pigx.common.excel.head.HeadGenerator;
import com.pig4cloud.pigx.common.excel.head.HeadMeta;
import com.pig4cloud.pigx.common.excel.head.I18nHeaderCellWriteHandler;
import com.pig4cloud.pigx.common.excel.kit.ExcelException;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Modifier;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
/**
* @author lengleng
* @author L.cm
* @date 2020/3/31
*/
@RequiredArgsConstructor
public abstract class AbstractSheetWriteHandler implements SheetWriteHandler, ApplicationContextAware {
private final ExcelConfigProperties configProperties;
private final ObjectProvider<List<Converter<?>>> converterProvider;
private final WriterBuilderEnhancer excelWriterBuilderEnhance;
private ApplicationContext applicationContext;
@Getter
@Setter
@Autowired(required = false)
private I18nHeaderCellWriteHandler i18nHeaderCellWriteHandler;
@Override
public void check(ResponseExcel responseExcel) {
if (responseExcel.sheets().length == 0) {
throw new ExcelException("@ResponseExcel sheet 配置不合法");
}
}
@Override
@SneakyThrows(UnsupportedEncodingException.class)
public void export(Object o, HttpServletResponse response, ResponseExcel responseExcel) {
check(responseExcel);
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
String name = (String) Objects.requireNonNull(requestAttributes).getAttribute(DynamicNameAspect.EXCEL_NAME_KEY,
RequestAttributes.SCOPE_REQUEST);
if (name == null) {
name = UUID.randomUUID().toString();
}
String fileName = String.format("%s%s", URLEncoder.encode(name, "UTF-8"), responseExcel.suffix().getValue());
// 根据实际的文件类型找到对应的 contentType
String contentType = MediaTypeFactory.getMediaType(fileName).map(MediaType::toString)
.orElse("application/vnd.ms-excel");
response.setContentType(contentType);
response.setCharacterEncoding("utf-8");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename*=utf-8''" + fileName);
write(o, response, responseExcel);
}
/**
* 通用的获取ExcelWriter方法
* @param response HttpServletResponse
* @param responseExcel ResponseExcel注解
* @return ExcelWriter
*/
@SneakyThrows(IOException.class)
public ExcelWriter getExcelWriter(HttpServletResponse response, ResponseExcel responseExcel) {
ExcelWriterBuilder writerBuilder = EasyExcel.write(response.getOutputStream())
.registerConverter(LocalTimeStringConverter.INSTANCE)
.registerConverter(LocalDateStringConverter.INSTANCE)
.registerConverter(LocalDateTimeStringConverter.INSTANCE)
.registerConverter(LongStringConverter.INSTANCE).registerConverter(StringArrayConverter.INSTANCE)
.autoCloseStream(true).excelType(responseExcel.suffix()).inMemory(responseExcel.inMemory());
if (StringUtils.hasText(responseExcel.password())) {
writerBuilder.password(responseExcel.password());
}
if (responseExcel.include().length != 0) {
writerBuilder.includeColumnFieldNames(Arrays.asList(responseExcel.include()));
}
if (responseExcel.exclude().length != 0) {
writerBuilder.excludeColumnFieldNames(Arrays.asList(responseExcel.exclude()));
}
for (Class<? extends WriteHandler> clazz : responseExcel.writeHandler()) {
writerBuilder.registerWriteHandler(BeanUtils.instantiateClass(clazz));
}
// 开启国际化头信息处理
if (responseExcel.i18nHeader() && i18nHeaderCellWriteHandler != null) {
writerBuilder.registerWriteHandler(i18nHeaderCellWriteHandler);
}
// 自定义注入的转换器
registerCustomConverter(writerBuilder);
for (Class<? extends Converter> clazz : responseExcel.converter()) {
writerBuilder.registerConverter(BeanUtils.instantiateClass(clazz));
}
String templatePath = configProperties.getTemplatePath();
if (StringUtils.hasText(responseExcel.template())) {
ClassPathResource classPathResource = new ClassPathResource(
templatePath + File.separator + responseExcel.template());
InputStream inputStream = classPathResource.getInputStream();
writerBuilder.withTemplate(inputStream);
}
writerBuilder = excelWriterBuilderEnhance.enhanceExcel(writerBuilder, response, responseExcel, templatePath);
return writerBuilder.build();
}
/**
* 自定义注入转换器 如果有需要,子类自己重写
* @param builder ExcelWriterBuilder
*/
public void registerCustomConverter(ExcelWriterBuilder builder) {
converterProvider.ifAvailable(converters -> converters.forEach(builder::registerConverter));
}
/**
* 获取 WriteSheet 对象
* @param sheet sheet annotation info
* @param dataClass 数据类型
* @param template 模板
* @param bookHeadEnhancerClass 自定义头处理器
* @return WriteSheet
*/
public WriteSheet sheet(Sheet sheet, Class<?> dataClass, String template,
Class<? extends HeadGenerator> bookHeadEnhancerClass) {
// Sheet 编号和名称
Integer sheetNo = sheet.sheetNo() >= 0 ? sheet.sheetNo() : null;
String sheetName = sheet.sheetName();
// 是否模板写入
ExcelWriterSheetBuilder writerSheetBuilder = StringUtils.hasText(template) ? EasyExcel.writerSheet(sheetNo)
: EasyExcel.writerSheet(sheetNo, sheetName);
// 头信息增强 1. 优先使用 sheet 指定的头信息增强 2. 其次使用 @ResponseExcel 中定义的全局头信息增强
Class<? extends HeadGenerator> headGenerateClass = null;
if (isNotInterface(sheet.headGenerateClass())) {
headGenerateClass = sheet.headGenerateClass();
}
else if (isNotInterface(bookHeadEnhancerClass)) {
headGenerateClass = bookHeadEnhancerClass;
}
// 定义头信息增强则使用其生成头信息,否则使用 dataClass 来自动获取
if (headGenerateClass != null) {
fillCustomHeadInfo(dataClass, bookHeadEnhancerClass, writerSheetBuilder);
}
else if (dataClass != null) {
writerSheetBuilder.head(dataClass);
if (sheet.excludes().length > 0) {
writerSheetBuilder.excludeColumnFieldNames(Arrays.asList(sheet.excludes()));
}
if (sheet.includes().length > 0) {
writerSheetBuilder.excludeColumnFieldNames(Arrays.asList(sheet.includes()));
}
}
// sheetBuilder 增强
writerSheetBuilder = excelWriterBuilderEnhance.enhanceSheet(writerSheetBuilder, sheetNo, sheetName, dataClass,
template, headGenerateClass);
return writerSheetBuilder.build();
}
private void fillCustomHeadInfo(Class<?> dataClass, Class<? extends HeadGenerator> headEnhancerClass,
ExcelWriterSheetBuilder writerSheetBuilder) {
HeadGenerator headGenerator = this.applicationContext.getBean(headEnhancerClass);
Assert.notNull(headGenerator, "The header generated bean does not exist.");
HeadMeta head = headGenerator.head(dataClass);
writerSheetBuilder.head(head.getHead());
writerSheetBuilder.excludeColumnFieldNames(head.getIgnoreHeadFields());
}
/**
* 是否为Null Head Generator
* @param headGeneratorClass 头生成器类型
* @return true 已指定 false 未指定(默认值)
*/
private boolean isNotInterface(Class<? extends HeadGenerator> headGeneratorClass) {
return !Modifier.isInterface(headGeneratorClass.getModifiers());
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

View File

@@ -0,0 +1,74 @@
package com.pig4cloud.pigx.common.excel.handler;
import com.alibaba.excel.context.AnalysisContext;
import com.pig4cloud.pigx.common.excel.annotation.ExcelLine;
import com.pig4cloud.pigx.common.excel.kit.Validators;
import com.pig4cloud.pigx.common.excel.vo.ErrorMessage;
import lombok.extern.slf4j.Slf4j;
import javax.validation.ConstraintViolation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 默认的 AnalysisEventListener
*
* @author lengleng
* @author L.cm
* @date 2021/4/16
*/
@Slf4j
public class DefaultAnalysisEventListener extends ListAnalysisEventListener<Object> {
private final List<Object> list = new ArrayList<>();
private final List<ErrorMessage> errorMessageList = new ArrayList<>();
private Long lineNum = 1L;
@Override
public void invoke(Object o, AnalysisContext analysisContext) {
lineNum++;
Set<ConstraintViolation<Object>> violations = Validators.validate(o);
if (!violations.isEmpty()) {
Set<String> messageSet = violations.stream().map(ConstraintViolation::getMessage)
.collect(Collectors.toSet());
errorMessageList.add(new ErrorMessage(lineNum, messageSet));
}
else {
Field[] fields = o.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(ExcelLine.class) && field.getType() == Long.class) {
try {
field.setAccessible(true);
field.set(o, lineNum);
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
list.add(o);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
log.debug("Excel read analysed");
}
@Override
public List<Object> getList() {
return list;
}
@Override
public List<ErrorMessage> getErrors() {
return errorMessageList;
}
}

View File

@@ -0,0 +1,27 @@
package com.pig4cloud.pigx.common.excel.handler;
import com.alibaba.excel.event.AnalysisEventListener;
import com.pig4cloud.pigx.common.excel.vo.ErrorMessage;
import java.util.List;
/**
* list analysis EventListener
*
* @author L.cm
*/
public abstract class ListAnalysisEventListener<T> extends AnalysisEventListener<T> {
/**
* 获取 excel 解析的对象列表
* @return 集合
*/
public abstract List<T> getList();
/**
* 获取异常校验结果
* @return 集合
*/
public abstract List<ErrorMessage> getErrors();
}

View File

@@ -0,0 +1,78 @@
package com.pig4cloud.pigx.common.excel.handler;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.excel.annotation.Sheet;
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
import com.pig4cloud.pigx.common.excel.enhance.WriterBuilderEnhancer;
import com.pig4cloud.pigx.common.excel.kit.ExcelException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.util.CollectionUtils;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* @author lengleng
* @author L.cm
* @date 2020/3/29
*/
public class ManySheetWriteHandler extends AbstractSheetWriteHandler {
public ManySheetWriteHandler(ExcelConfigProperties configProperties,
ObjectProvider<List<Converter<?>>> converterProvider, WriterBuilderEnhancer excelWriterBuilderEnhance) {
super(configProperties, converterProvider, excelWriterBuilderEnhance);
}
/**
* 当且仅当List不为空且List中的元素也是List 才返回true
* @param obj 返回对象
* @return boolean
*/
@Override
public boolean support(Object obj) {
if (obj instanceof List) {
List<?> objList = (List<?>) obj;
return !objList.isEmpty() && objList.get(0) instanceof List;
}
else {
throw new ExcelException("@ResponseExcel 返回值必须为List类型");
}
}
@Override
public void write(Object obj, HttpServletResponse response, ResponseExcel responseExcel) {
List<?> objList = (List<?>) obj;
ExcelWriter excelWriter = getExcelWriter(response, responseExcel);
Sheet[] sheets = responseExcel.sheets();
WriteSheet sheet;
for (int i = 0; i < sheets.length; i++) {
List<?> eleList = (List<?>) objList.get(i);
if (CollectionUtils.isEmpty(eleList)) {
sheet = EasyExcel.writerSheet(responseExcel.sheets()[i].sheetName()).build();
}
else {
// 有模板则不指定sheet名
Class<?> dataClass = eleList.get(0).getClass();
sheet = this.sheet(responseExcel.sheets()[i], dataClass, responseExcel.template(),
responseExcel.headGenerator());
}
// 填充 sheet
if (responseExcel.fill()) {
excelWriter.fill(eleList, sheet);
}
else {
// 写入sheet
excelWriter.write(eleList, sheet);
}
}
excelWriter.finish();
}
}

View File

@@ -0,0 +1,44 @@
package com.pig4cloud.pigx.common.excel.handler;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import javax.servlet.http.HttpServletResponse;
/**
* @author lengleng
* @date 2020/3/29
* <p>
* sheet 写出处理器
*/
public interface SheetWriteHandler {
/**
* 是否支持
* @param obj
* @return
*/
boolean support(Object obj);
/**
* 校验
* @param responseExcel 注解
*/
void check(ResponseExcel responseExcel);
/**
* 返回的对象
* @param o obj
* @param response 输出对象
* @param responseExcel 注解
*/
void export(Object o, HttpServletResponse response, ResponseExcel responseExcel);
/**
* 写成对象
* @param o obj
* @param response 输出对象
* @param responseExcel 注解
*/
void write(Object o, HttpServletResponse response, ResponseExcel responseExcel);
}

View File

@@ -0,0 +1,74 @@
package com.pig4cloud.pigx.common.excel.handler;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
import com.pig4cloud.pigx.common.excel.enhance.WriterBuilderEnhancer;
import com.pig4cloud.pigx.common.excel.kit.ExcelException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.util.CollectionUtils;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* @author lengleng
* @author L.cm
* @date 2020/3/29
* <p>
* 处理单sheet 页面
*/
public class SingleSheetWriteHandler extends AbstractSheetWriteHandler {
public SingleSheetWriteHandler(ExcelConfigProperties configProperties,
ObjectProvider<List<Converter<?>>> converterProvider, WriterBuilderEnhancer excelWriterBuilderEnhance) {
super(configProperties, converterProvider, excelWriterBuilderEnhance);
}
/**
* obj 是List 且list不为空同时list中的元素不是是List 才返回true
* @param obj 返回对象
* @return boolean
*/
@Override
public boolean support(Object obj) {
if (obj instanceof List) {
List<?> objList = (List<?>) obj;
return !objList.isEmpty() && !(objList.get(0) instanceof List);
}
else {
throw new ExcelException("@ResponseExcel 返回值必须为List类型");
}
}
@Override
public void write(Object obj, HttpServletResponse response, ResponseExcel responseExcel) {
List<?> eleList = (List<?>) obj;
ExcelWriter excelWriter = getExcelWriter(response, responseExcel);
WriteSheet sheet;
if (CollectionUtils.isEmpty(eleList)) {
sheet = EasyExcel.writerSheet(responseExcel.sheets()[0].sheetName()).build();
}
else {
// 有模板则不指定sheet名
Class<?> dataClass = eleList.get(0).getClass();
sheet = this.sheet(responseExcel.sheets()[0], dataClass, responseExcel.template(),
responseExcel.headGenerator());
}
// 填充 sheet
if (responseExcel.fill()) {
excelWriter.fill(eleList, sheet);
}
else {
// 写入sheet
excelWriter.write(eleList, sheet);
}
excelWriter.finish();
}
}

View File

@@ -0,0 +1,22 @@
package com.pig4cloud.pigx.common.excel.head;
/**
* Excel头生成器用于自定义生成头部信息
*
* @author Hccake 2020/10/27
* @version 1.0
*/
public interface HeadGenerator {
/**
* <p>
* 自定义头部信息
* </p>
* 实现类根据数据的class信息定制Excel头<br/>
* 具体方法使用参考https://www.yuque.com/easyexcel/doc/write#b4b9de00
* @param clazz 当前sheet的数据类型
* @return List<List<String>> Head头信息
*/
HeadMeta head(Class<?> clazz);
}

View File

@@ -0,0 +1,29 @@
package com.pig4cloud.pigx.common.excel.head;
import lombok.Data;
import java.util.List;
import java.util.Set;
/**
* @author Yakir
* @date 2021/4/26 10:58
*/
@Data
public class HeadMeta {
/**
* <p>
* 自定义头部信息
* </p>
* 实现类根据数据的class信息定制Excel头<br/>
* 具体方法使用参考https://www.yuque.com/easyexcel/doc/write#b4b9de00
*/
private List<List<String>> head;
/**
* 忽略头对应字段名称
*/
private Set<String> ignoreHeadFields;
}

View File

@@ -0,0 +1,61 @@
package com.pig4cloud.pigx.common.excel.head;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Row;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.util.PropertyPlaceholderHelper;
import java.util.List;
import java.util.stream.Collectors;
/**
* 对表头进行国际化处理
*
* @author hccake
*/
@RequiredArgsConstructor
public class I18nHeaderCellWriteHandler implements CellWriteHandler {
/**
* 国际化消息源
*/
private final MessageSource messageSource;
/**
* 国际化翻译
*/
private final PropertyPlaceholderHelper.PlaceholderResolver placeholderResolver;
public I18nHeaderCellWriteHandler(MessageSource messageSource) {
this.messageSource = messageSource;
this.placeholderResolver = placeholderName -> this.messageSource.getMessage(placeholderName, null,
LocaleContextHolder.getLocale());
}
/**
* 占位符处理
*/
private final PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("{", "}");
@Override
public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
if (isHead != null && isHead) {
List<String> originHeadNameList = head.getHeadNameList();
if (CollectionUtils.isNotEmpty(originHeadNameList)) {
// 国际化处理
List<String> i18nHeadNames = originHeadNameList.stream()
.map(headName -> propertyPlaceholderHelper.replacePlaceholders(headName, placeholderResolver))
.collect(Collectors.toList());
head.setHeadNameList(i18nHeadNames);
}
}
}
}

View File

@@ -0,0 +1,15 @@
package com.pig4cloud.pigx.common.excel.kit;
/**
* @author lengleng
* @date 2020/3/31
*/
public class ExcelException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ExcelException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,37 @@
package com.pig4cloud.pigx.common.excel.kit;
import javax.validation.*;
import java.util.Set;
/**
* 校验工具
*
* @author L.cm
*/
public final class Validators {
private Validators() {
}
private static final Validator VALIDATOR;
static {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
VALIDATOR = factory.getValidator();
}
/**
* Validates all constraints on {@code object}.
* @param object object to validate
* @param <T> the type of the object to validate
* @return constraint violations or an empty set if none
* @throws IllegalArgumentException if object is {@code null} or if {@code null} is
* passed to the varargs groups
* @throws ValidationException if a non recoverable error happens during the
* validation process
*/
public static <T> Set<ConstraintViolation<T>> validate(T object) {
return VALIDATOR.validate(object);
}
}

View File

@@ -0,0 +1,20 @@
package com.pig4cloud.pigx.common.excel.processor;
import java.lang.reflect.Method;
/**
* @author lengleng
* @date 2020/3/29
*/
public interface NameProcessor {
/**
* 解析名称
* @param args 拦截器对象
* @param method
* @param key 表达式
* @return
*/
String doDetermineName(Object[] args, Method method, String key);
}

View File

@@ -0,0 +1,40 @@
package com.pig4cloud.pigx.common.excel.processor;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import java.lang.reflect.Method;
/**
* @author lengleng
* @date 2020/3/29
*/
public class NameSpelExpressionProcessor implements NameProcessor {
/**
* 参数发现器
*/
private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
/**
* Express语法解析器
*/
private static final ExpressionParser PARSER = new SpelExpressionParser();
@Override
public String doDetermineName(Object[] args, Method method, String key) {
if (!key.contains("#")) {
return key;
}
EvaluationContext context = new MethodBasedEvaluationContext(null, method, args, NAME_DISCOVERER);
final Object value = PARSER.parseExpression(key).getValue(context);
return value == null ? null : value.toString();
}
}

View File

@@ -0,0 +1,41 @@
package com.pig4cloud.pigx.common.excel.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.HashSet;
import java.util.Set;
/**
* 校验错误信息
*
* @author lengleng
* @date 2021/8/4
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ErrorMessage {
/**
* 行号
*/
private Long lineNum;
/**
* 错误信息
*/
private Set<String> errors = new HashSet<>();
public ErrorMessage(Set<String> errors) {
this.errors = errors;
}
public ErrorMessage(String error) {
HashSet<String> objects = new HashSet<>();
objects.add(error);
this.errors = objects;
}
}

View File

@@ -0,0 +1,19 @@
{
"groups": [
{
"name": "excel",
"type": "com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties",
"sourceType": "com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties"
}
],
"properties": [
{
"name": "excel.template-path",
"type": "java.lang.String",
"description": "模板路径",
"defaultValue": "excel",
"sourceType": "com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties"
}
],
"hints": []
}

View File

@@ -0,0 +1 @@
com.pig4cloud.pigx.common.excel.ResponseExcelAutoConfiguration