初始化 V1
This commit is contained in:
80
yudao-framework/yudao-spring-boot-starter-excel/pom.xml
Normal file
80
yudao-framework/yudao-spring-boot-starter-excel/pom.xml
Normal file
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yudao-spring-boot-starter-excel</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>Excel 拓展</description>
|
||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring 核心 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RPC 远程调用相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-rpc</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<scope>provided</scope> <!-- 设置为 provided,只有 ExcelUtils 使用 -->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope> <!-- 设置为 provided,只有 ExcelUtils 使用 -->
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId> <!-- 解决 https://github.com/alibaba/easyexcel/issues/3954 问题 -->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
|
||||
<optional>true</optional> <!-- 设置为 optional,只有在 AreaConvert 的时候使用 -->
|
||||
</dependency>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.iocoder.yudao.framework.dict.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.biz.system.dict.DictDataCommonApi;
|
||||
import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@AutoConfiguration
|
||||
public class YudaoDictAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("InstantiationOfUtilityClass")
|
||||
public DictFrameworkUtils dictUtils(DictDataCommonApi dictDataApi) {
|
||||
DictFrameworkUtils.init(dictDataApi);
|
||||
return new DictFrameworkUtils();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package cn.iocoder.yudao.framework.dict.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.biz.system.dict.DictDataCommonApi;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
/**
|
||||
* 字典用到 Feign 的配置项
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableFeignClients(clients = DictDataCommonApi.class) // 主要是引入相关的 API 服务
|
||||
public class YudaoDictRpcAutoConfiguration {
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package cn.iocoder.yudao.framework.dict.core;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.biz.system.dict.DictDataCommonApi;
|
||||
import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
|
||||
import cn.iocoder.yudao.framework.common.biz.system.dict.dto.DictDataRespDTO;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
/**
|
||||
* 字典工具类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
public class DictFrameworkUtils {
|
||||
|
||||
private static DictDataCommonApi dictDataApi;
|
||||
|
||||
/**
|
||||
* 针对 dictType 的字段数据缓存
|
||||
*/
|
||||
private static final LoadingCache<String, List<DictDataRespDTO>> GET_DICT_DATA_CACHE = CacheUtils.buildAsyncReloadingCache(
|
||||
Duration.ofMinutes(1L), // 过期时间 1 分钟
|
||||
new CacheLoader<String, List<DictDataRespDTO>>() {
|
||||
|
||||
@Override
|
||||
public List<DictDataRespDTO> load(String dictType) {
|
||||
return dictDataApi.getDictDataList(dictType).getCheckedData();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
public static void init(DictDataCommonApi dictDataApi) {
|
||||
DictFrameworkUtils.dictDataApi = dictDataApi;
|
||||
log.info("[init][初始化 DictFrameworkUtils 成功]");
|
||||
}
|
||||
|
||||
public static void clearCache() {
|
||||
GET_DICT_DATA_CACHE.invalidateAll();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String parseDictDataLabel(String dictType, Integer value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return parseDictDataLabel(dictType, String.valueOf(value));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String parseDictDataLabel(String dictType, String value) {
|
||||
List<DictDataRespDTO> dictDatas = GET_DICT_DATA_CACHE.get(dictType);
|
||||
DictDataRespDTO dictData = CollUtil.findOne(dictDatas, data -> Objects.equals(data.getValue(), value));
|
||||
return dictData != null ? dictData.getLabel(): null;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static List<String> getDictDataLabelList(String dictType) {
|
||||
List<DictDataRespDTO> dictDatas = GET_DICT_DATA_CACHE.get(dictType);
|
||||
return convertList(dictDatas, DictDataRespDTO::getLabel);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String parseDictDataValue(String dictType, String label) {
|
||||
List<DictDataRespDTO> dictDatas = GET_DICT_DATA_CACHE.get(dictType);
|
||||
DictDataRespDTO dictData = CollUtil.findOne(dictDatas, data -> Objects.equals(data.getLabel(), label));
|
||||
return dictData!= null ? dictData.getValue(): null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 字典数据模块,提供 {@link cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils} 工具类
|
||||
*
|
||||
* 通过将字典缓存在内存中,保证性能
|
||||
*/
|
||||
package cn.iocoder.yudao.framework.dict;
|
||||
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.framework.excel.core.annotations;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 字典格式化
|
||||
*
|
||||
* 实现将字典数据的值,格式化成字典数据的标签
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
public @interface DictFormat {
|
||||
|
||||
/**
|
||||
* 例如说,SysDictTypeConstants、InfDictTypeConstants
|
||||
*
|
||||
* @return 字典类型
|
||||
*/
|
||||
String value();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.framework.excel.core.annotations;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 给 Excel 列添加下拉选择数据
|
||||
*
|
||||
* 其中 {@link #dictType()} 和 {@link #functionName()} 二选一
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
public @interface ExcelColumnSelect {
|
||||
|
||||
/**
|
||||
* @return 字典类型
|
||||
*/
|
||||
String dictType() default "";
|
||||
|
||||
/**
|
||||
* @return 获取下拉数据源的方法名称
|
||||
*/
|
||||
String functionName() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package cn.iocoder.yudao.framework.excel.core.convert;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.iocoder.yudao.framework.ip.core.Area;
|
||||
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
||||
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.property.ExcelContentProperty;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Excel 数据地区转换器
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Slf4j
|
||||
public class AreaConvert implements Converter<Object> {
|
||||
|
||||
@Override
|
||||
public Class<?> supportJavaTypeKey() {
|
||||
throw new UnsupportedOperationException("暂不支持,也不需要");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CellDataTypeEnum supportExcelTypeKey() {
|
||||
throw new UnsupportedOperationException("暂不支持,也不需要");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty,
|
||||
GlobalConfiguration globalConfiguration) {
|
||||
// 解析地区编号
|
||||
String label = readCellData.getStringValue();
|
||||
Area area = AreaUtils.parseArea(label);
|
||||
if (area == null) {
|
||||
log.error("[convertToJavaData][label({}) 解析不掉]", label);
|
||||
return null;
|
||||
}
|
||||
// 将 value 转换成对应的属性
|
||||
Class<?> fieldClazz = contentProperty.getField().getType();
|
||||
return Convert.convert(fieldClazz, area.getId());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package cn.iocoder.yudao.framework.excel.core.convert;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Excel 数据字典转换器
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
public class DictConvert implements Converter<Object> {
|
||||
|
||||
@Override
|
||||
public Class<?> supportJavaTypeKey() {
|
||||
throw new UnsupportedOperationException("暂不支持,也不需要");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CellDataTypeEnum supportExcelTypeKey() {
|
||||
throw new UnsupportedOperationException("暂不支持,也不需要");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty,
|
||||
GlobalConfiguration globalConfiguration) {
|
||||
// 使用字典解析
|
||||
String type = getType(contentProperty);
|
||||
String label = readCellData.getStringValue();
|
||||
String value = DictFrameworkUtils.parseDictDataValue(type, label);
|
||||
if (value == null) {
|
||||
log.error("[convertToJavaData][type({}) 解析不掉 label({})]", type, label);
|
||||
return null;
|
||||
}
|
||||
// 将 String 的 value 转换成对应的属性
|
||||
Class<?> fieldClazz = contentProperty.getField().getType();
|
||||
return Convert.convert(fieldClazz, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty,
|
||||
GlobalConfiguration globalConfiguration) {
|
||||
// 空时,返回空
|
||||
if (object == null) {
|
||||
return new WriteCellData<>("");
|
||||
}
|
||||
|
||||
// 使用字典格式化
|
||||
String type = getType(contentProperty);
|
||||
String value = String.valueOf(object);
|
||||
String label = DictFrameworkUtils.parseDictDataLabel(type, value);
|
||||
if (label == null) {
|
||||
log.error("[convertToExcelData][type({}) 转换不了 label({})]", type, value);
|
||||
return new WriteCellData<>("");
|
||||
}
|
||||
// 生成 Excel 小表格
|
||||
return new WriteCellData<>(label);
|
||||
}
|
||||
|
||||
private static String getType(ExcelContentProperty contentProperty) {
|
||||
return contentProperty.getField().getAnnotation(DictFormat.class).value();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package cn.iocoder.yudao.framework.excel.core.convert;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import com.alibaba.excel.converters.Converter;
|
||||
import com.alibaba.excel.enums.CellDataTypeEnum;
|
||||
import com.alibaba.excel.metadata.GlobalConfiguration;
|
||||
import com.alibaba.excel.metadata.data.WriteCellData;
|
||||
import com.alibaba.excel.metadata.property.ExcelContentProperty;
|
||||
|
||||
/**
|
||||
* Excel Json 转换器
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class JsonConvert implements Converter<Object> {
|
||||
|
||||
@Override
|
||||
public Class<?> supportJavaTypeKey() {
|
||||
throw new UnsupportedOperationException("暂不支持,也不需要");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CellDataTypeEnum supportExcelTypeKey() {
|
||||
throw new UnsupportedOperationException("暂不支持,也不需要");
|
||||
}
|
||||
|
||||
@Override
|
||||
public WriteCellData<String> convertToExcelData(Object value, ExcelContentProperty contentProperty,
|
||||
GlobalConfiguration globalConfiguration) {
|
||||
// 生成 Excel 小表格
|
||||
return new WriteCellData<>(JsonUtils.toJsonString(value));
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user