diff --git a/README.md b/README.md index 018de152..9e6aef9c 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ yarn dev | [Liquibase](https://github.com/liquibase/liquibase) | 4.9.1 | 用于管理数据库版本,跟踪、管理和应用数据库变化。 | | [Redis](https://redis.io/) | 6.2.7 | 高性能的 key-value 数据库。 | | [Redisson](https://github.com/redisson/redisson/wiki/Redisson%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D) | 3.19.0 | 不仅仅是一个 Redis Java 客户端,同其他 Redis Java 客户端有着很大的区别,相比之下其他客户端提供的功能还仅仅停留在作为数据库驱动层面上,比如仅针对 Redis 提供连接方式,发送命令和处理返回结果等。而 Redisson 充分的利用了 Redis 键值数据库提供的一系列优势,基于 Java 实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低了设计和研发大规模分布式系统的难度。同时结合各富特色的分布式服务,更进一步简化了分布式环境中程序相互之间的协作。 | +| [Easy Excel](https://easyexcel.opensource.alibaba.com/) | 3.2.0 | 一个基于 Java 的、快速、简洁、解决大文件内存溢出的 Excel 处理工具。 | | Easy Captcha | 1.6.2 | Java 图形验证码,支持 gif、中文、算术等类型,可用于 Java Web、JavaSE 等项目。 | | [Knife4j](https://doc.xiaominfo.com/) | 4.0.0 | 前身是 swagger-bootstrap-ui,集 Swagger2 和 OpenAPI3 为一体的增强解决方案。本项目使用的是 [knife4j-openapi3-spring-boot-starter](https://gitee.com/xiaoym/swagger-bootstrap-ui-demo/tree/master/knife4j-springdoc-openapi-demo) 基于 OpenAPI3 规范,在 Spring Boot < 3.0.0-M1 的单体架构下可以直接引用此 starter,该模块包含了 UI 部分,底层基于 springdoc-openapi 项目。 | | [Hutool](https://www.hutool.cn/) | 5.8.11 | 小而全的 Java 工具类库,通过静态方法封装,降低相关 API 的学习成本,提高工作效率,使 Java 拥有函数式语言般的优雅,让 Java 语言也可以“甜甜的”。 | @@ -214,6 +215,7 @@ continew-admin # 全局通用项目配置及依赖版本管理 │ ├─ annotation # 公共注解 │ ├─ base # 公共基类 │ ├─ config # 公共配置 + │ │ ├─ easyexcel # Easy Excel 配置 │ │ ├─ jackson # Jackson 配置 │ │ ├─ mybatis # MyBatis Plus 配置 │ │ ├─ threadpool # 线程池配置 diff --git a/continew-admin-common/pom.xml b/continew-admin-common/pom.xml index 8a0b50c8..dc6ac7b5 100644 --- a/continew-admin-common/pom.xml +++ b/continew-admin-common/pom.xml @@ -115,6 +115,12 @@ limitations under the License. + + + com.alibaba + easyexcel + + net.dreamlu diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseDetailVO.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseDetailVO.java index 73f963ff..9920904d 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseDetailVO.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseDetailVO.java @@ -22,6 +22,7 @@ import lombok.Data; import io.swagger.v3.oas.annotations.media.Schema; +import com.alibaba.excel.annotation.ExcelProperty; import com.fasterxml.jackson.annotation.JsonIgnore; /** @@ -45,11 +46,13 @@ public class BaseDetailVO extends BaseVO { * 修改人 */ @Schema(description = "修改人") + @ExcelProperty(value = "修改人") private String updateUserString; /** * 修改时间 */ @Schema(description = "修改时间") + @ExcelProperty(value = "修改时间") private LocalDateTime updateTime; } diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseEnum.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseEnum.java new file mode 100644 index 00000000..33e216da --- /dev/null +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseEnum.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.charles7c.cnadmin.common.base; + +import java.io.Serializable; + +import com.baomidou.mybatisplus.annotation.IEnum; + +/** + * 枚举基类 + * + * @param + * value 类型 + * @param + * description 类型 + * @author Charles7c + * @since 2023/2/5 20:44 + */ +public interface BaseEnum extends IEnum { + + /** + * 枚举描述 + */ + D getDescription(); +} diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseVO.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseVO.java index f60a42ec..29bd1d0c 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseVO.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseVO.java @@ -23,6 +23,7 @@ import lombok.Data; import io.swagger.v3.oas.annotations.media.Schema; +import com.alibaba.excel.annotation.ExcelProperty; import com.fasterxml.jackson.annotation.JsonIgnore; /** @@ -46,11 +47,13 @@ public class BaseVO implements Serializable { * 创建人 */ @Schema(description = "创建人") + @ExcelProperty(value = "创建人") private String createUserString; /** * 创建时间 */ @Schema(description = "创建时间") + @ExcelProperty(value = "创建时间") private LocalDateTime createTime; } diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/WebMvcConfiguration.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/WebMvcConfiguration.java index 8167804b..659d084a 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/WebMvcConfiguration.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/WebMvcConfiguration.java @@ -86,6 +86,8 @@ public class WebMvcConfiguration implements WebMvcConfigurer { corsProperties.getAllowedMethods().forEach(config::addAllowedMethod); // 配置允许跨域的请求头 corsProperties.getAllowedHeaders().forEach(config::addAllowedHeader); + // 配置允许跨域的响应头 + corsProperties.getExposedHeaders().forEach(config::addExposedHeader); // 添加映射路径,拦截一切请求 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/easyexcel/ExcelBaseEnumConverter.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/easyexcel/ExcelBaseEnumConverter.java new file mode 100644 index 00000000..237fe30b --- /dev/null +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/easyexcel/ExcelBaseEnumConverter.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.charles7c.cnadmin.common.config.easyexcel; + +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 cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ObjectUtil; + +import top.charles7c.cnadmin.common.base.BaseEnum; + +/** + * Easy Excel 枚举基类转换器 + * + * @author Charles7c + * @since 2023/2/5 19:29 + */ +public class ExcelBaseEnumConverter implements Converter> { + + @Override + public Class supportJavaTypeKey() { + return BaseEnum.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + /** + * 转换为 Java 数据(读取 Excel) + */ + @Override + public BaseEnum convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return this.getEnum(BaseEnum.class, Convert.toStr(cellData.getData())); + } + + /** + * 转换为 Excel 数据(写入 Excel) + */ + @Override + public WriteCellData convertToExcelData(BaseEnum value, + ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (ObjectUtil.isNull(value)) { + return new WriteCellData<>(""); + } + return new WriteCellData<>(value.getDescription()); + } + + /** + * 通过 value 获取枚举对象,获取不到时为 {@code null} + * + * @param enumType + * 枚举类型 + * @param description + * 描述 + * @return 对应枚举 ,获取不到时为 {@code null} + */ + private BaseEnum getEnum(Class enumType, String description) { + Object[] enumConstants = enumType.getEnumConstants(); + for (Object enumConstant : enumConstants) { + if (ClassUtil.isAssignable(BaseEnum.class, enumType)) { + BaseEnum baseEnum = (BaseEnum)enumConstant; + if (baseEnum.getDescription().equals(description)) { + return baseEnum; + } + } + } + return null; + } +} diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/easyexcel/ExcelBigNumberConverter.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/easyexcel/ExcelBigNumberConverter.java new file mode 100644 index 00000000..d3d22a25 --- /dev/null +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/easyexcel/ExcelBigNumberConverter.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.charles7c.cnadmin.common.config.easyexcel; + +import java.math.BigDecimal; + +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 cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; + +/** + * Easy Excel 大数值转换器(Excel 中对长度超过 15 位的数值输入是有限制的,从 16 位开始无论录入什么数字均会变为 0,因此输入时只能以文本的形式进行录入) + * + * @author Charles7c + * @since 2023/2/5 19:29 + */ +public class ExcelBigNumberConverter implements Converter { + + /** + * Excel 输入数值长度限制 + */ + private static final int MAX_LENGTH = 15; + + @Override + public Class supportJavaTypeKey() { + return Long.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + /** + * 转换为 Java 数据(读取 Excel) + */ + @Override + public Long convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return Convert.toLong(cellData.getData()); + } + + /** + * 转换为 Excel 数据(写入 Excel) + */ + @Override + public WriteCellData convertToExcelData(Long value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (ObjectUtil.isNotNull(value)) { + String str = Long.toString(value); + if (str.length() > MAX_LENGTH) { + return new WriteCellData<>(str); + } + } + WriteCellData writeCellData = new WriteCellData<>(new BigDecimal(value)); + writeCellData.setType(CellDataTypeEnum.NUMBER); + return writeCellData; + } +} diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/IEnumDeserializer.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/BaseEnumDeserializer.java similarity index 68% rename from continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/IEnumDeserializer.java rename to continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/BaseEnumDeserializer.java index 46b86949..65ab0465 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/IEnumDeserializer.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/BaseEnumDeserializer.java @@ -19,7 +19,6 @@ package top.charles7c.cnadmin.common.config.jackson; import java.io.IOException; import java.lang.reflect.Field; -import com.baomidou.mybatisplus.annotation.IEnum; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; @@ -28,20 +27,23 @@ import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ReflectUtil; +import top.charles7c.cnadmin.common.base.BaseEnum; + /** - * 通用枚举接口 IEnum 反序列化器 + * 通用枚举基类 BaseEnum 反序列化器 * * @author Charles7c * @since 2023/1/8 13:56 */ @JacksonStdImpl -public class IEnumDeserializer extends JsonDeserializer { +public class BaseEnumDeserializer extends JsonDeserializer { /** 静态实例 */ - public static final IEnumDeserializer SERIALIZER_INSTANCE = new IEnumDeserializer(); + public static final BaseEnumDeserializer SERIALIZER_INSTANCE = new BaseEnumDeserializer(); @Override - public IEnum deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + public BaseEnum deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException { Class targetClass = jsonParser.getCurrentValue().getClass(); String fieldName = jsonParser.getCurrentName(); String value = jsonParser.getText(); @@ -49,7 +51,7 @@ public class IEnumDeserializer extends JsonDeserializer { } /** - * 通过某字段对应值获取枚举,获取不到时为 {@code null} + * 通过某字段对应值获取枚举实例,获取不到时为 {@code null} * * @param targetClass * 目标类型 @@ -57,17 +59,17 @@ public class IEnumDeserializer extends JsonDeserializer { * 字段值 * @param fieldName * 字段名 - * @return 对应枚举 ,获取不到时为 {@code null} + * @return 对应枚举实例 ,获取不到时为 {@code null} */ - public IEnum getEnum(Class targetClass, String value, String fieldName) { + private BaseEnum getEnum(Class targetClass, String value, String fieldName) { Field field = ReflectUtil.getField(targetClass, fieldName); Class fieldTypeClass = field.getType(); Object[] enumConstants = fieldTypeClass.getEnumConstants(); for (Object enumConstant : enumConstants) { - if (ClassUtil.isAssignable(IEnum.class, fieldTypeClass)) { - IEnum iEnum = (IEnum)enumConstant; - if (iEnum.getValue().equals(Integer.valueOf(value))) { - return iEnum; + if (ClassUtil.isAssignable(BaseEnum.class, fieldTypeClass)) { + BaseEnum baseEnum = (BaseEnum)enumConstant; + if (baseEnum.getValue().equals(Integer.valueOf(value))) { + return baseEnum; } } } diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/IEnumSerializer.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/BaseEnumSerializer.java similarity index 74% rename from continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/IEnumSerializer.java rename to continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/BaseEnumSerializer.java index 1681ff69..37d8473b 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/IEnumSerializer.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/BaseEnumSerializer.java @@ -18,26 +18,27 @@ package top.charles7c.cnadmin.common.config.jackson; import java.io.IOException; -import com.baomidou.mybatisplus.annotation.IEnum; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import top.charles7c.cnadmin.common.base.BaseEnum; + /** - * 通用枚举接口 IEnum 序列化器 + * 通用枚举接口 BaseEnum 序列化器 * * @author Charles7c * @since 2023/1/8 13:56 */ @JacksonStdImpl -public class IEnumSerializer extends JsonSerializer { +public class BaseEnumSerializer extends JsonSerializer { /** 静态实例 */ - public static final IEnumSerializer SERIALIZER_INSTANCE = new IEnumSerializer(); + public static final BaseEnumSerializer SERIALIZER_INSTANCE = new BaseEnumSerializer(); @Override - public void serialize(IEnum value, JsonGenerator generator, SerializerProvider serializers) throws IOException { + public void serialize(BaseEnum value, JsonGenerator generator, SerializerProvider serializers) throws IOException { generator.writeObject(value.getValue()); } } diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/JacksonConfiguration.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/JacksonConfiguration.java index c95502f1..99f7d6a8 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/JacksonConfiguration.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/JacksonConfiguration.java @@ -31,7 +31,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; -import com.baomidou.mybatisplus.annotation.IEnum; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; @@ -43,6 +42,8 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; +import top.charles7c.cnadmin.common.base.BaseEnum; + /** * Jackson 配置 * @@ -89,15 +90,15 @@ public class JacksonConfiguration { } /** - * 针对通用枚举接口 IEnum 的序列化和反序列化 + * 针对枚举基类 BaseEnum 的序列化和反序列化 */ @Bean public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) { SimpleModule simpleModule = new SimpleModule(); - simpleModule.addSerializer(IEnum.class, IEnumSerializer.SERIALIZER_INSTANCE); + simpleModule.addSerializer(BaseEnum.class, BaseEnumSerializer.SERIALIZER_INSTANCE); SimpleDeserializersWrapper deserializers = new SimpleDeserializersWrapper(); - deserializers.addDeserializer(IEnum.class, IEnumDeserializer.SERIALIZER_INSTANCE); + deserializers.addDeserializer(BaseEnum.class, BaseEnumDeserializer.SERIALIZER_INSTANCE); simpleModule.setDeserializers(deserializers); ObjectMapper objectMapper = builder.createXmlMapper(false).build(); diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/SimpleDeserializersWrapper.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/SimpleDeserializersWrapper.java index aa9629dd..0d9cce92 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/SimpleDeserializersWrapper.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/jackson/SimpleDeserializersWrapper.java @@ -38,7 +38,7 @@ import com.fasterxml.jackson.databind.type.ClassKey; * 重写增强后:
* 1. 同默认 1;
* 2. 同默认 2;
- * 3. 如果也找不到 Enum 类型(所有枚举父类)的反序列化器,开始查找指定枚举类型的接口的反序列化器(例如:GenderEnum 枚举类型,则是找它的接口 IEnum 的反序列化器);
+ * 3. 如果也找不到 Enum 类型(所有枚举父类)的反序列化器,开始查找指定枚举类型的接口的反序列化器(例如:GenderEnum 枚举类型,则是找它的接口 BaseEnum 的反序列化器);
* 4. 同默认 3。 *

* @@ -56,7 +56,7 @@ public class SimpleDeserializersWrapper extends SimpleDeserializers { return deser; } - // 重写增强:开始查找指定枚举类型的接口的反序列化器(例如:GenderEnum 枚举类型,则是找它的接口 IEnum 的反序列化器) + // 重写增强:开始查找指定枚举类型的接口的反序列化器(例如:GenderEnum 枚举类型,则是找它的接口 BaseEnum 的反序列化器) for (Class typeInterface : type.getInterfaces()) { deser = this._classMappings.get(new ClassKey(typeInterface)); if (deser != null) { diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/properties/CorsProperties.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/properties/CorsProperties.java index 33321236..219d9abd 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/properties/CorsProperties.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/properties/CorsProperties.java @@ -49,4 +49,9 @@ public class CorsProperties { * 允许跨域的请求头 */ private List allowedHeaders = new ArrayList<>(); + + /** + * 允许跨域的响应头 + */ + private List exposedHeaders = new ArrayList<>(); } diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/enums/DisEnableStatusEnum.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/enums/DisEnableStatusEnum.java index 0b7b1278..aadf9355 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/enums/DisEnableStatusEnum.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/enums/DisEnableStatusEnum.java @@ -19,7 +19,7 @@ package top.charles7c.cnadmin.common.enums; import lombok.Getter; import lombok.RequiredArgsConstructor; -import com.baomidou.mybatisplus.annotation.IEnum; +import top.charles7c.cnadmin.common.base.BaseEnum; /** * 启用/禁用状态枚举 @@ -29,7 +29,7 @@ import com.baomidou.mybatisplus.annotation.IEnum; */ @Getter @RequiredArgsConstructor -public enum DisEnableStatusEnum implements IEnum { +public enum DisEnableStatusEnum implements BaseEnum { /** 启用 */ ENABLE(1, "启用"), diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/enums/GenderEnum.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/enums/GenderEnum.java index b7537c3d..86839d6a 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/enums/GenderEnum.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/enums/GenderEnum.java @@ -19,7 +19,7 @@ package top.charles7c.cnadmin.common.enums; import lombok.Getter; import lombok.RequiredArgsConstructor; -import com.baomidou.mybatisplus.annotation.IEnum; +import top.charles7c.cnadmin.common.base.BaseEnum; /** * 性别枚举 @@ -29,7 +29,7 @@ import com.baomidou.mybatisplus.annotation.IEnum; */ @Getter @RequiredArgsConstructor -public enum GenderEnum implements IEnum { +public enum GenderEnum implements BaseEnum { /** 未知 */ UNKNOWN(0, "未知"), diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/ExcelUtils.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/ExcelUtils.java new file mode 100644 index 00000000..d4d79090 --- /dev/null +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/ExcelUtils.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.charles7c.cnadmin.common.util; + +import java.util.Date; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; +import com.esotericsoftware.minlog.Log; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.URLUtil; + +import top.charles7c.cnadmin.common.config.easyexcel.ExcelBigNumberConverter; +import top.charles7c.cnadmin.common.exception.ServiceException; + +/** + * Excel 工具类 + * + * @author Charles7c + * @since 2023/2/5 18:00 + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ExcelUtils { + + /** + * 导出 + * + * @param list + * 导出数据集合 + * @param fileName + * 文件名 + * @param clazz + * 导出数据类型 + * @param response + * 响应对象 + */ + public static void export(List list, String fileName, Class clazz, HttpServletResponse response) { + export(list, fileName, "Sheet1", clazz, response); + } + + /** + * 导出 + * + * @param list + * 导出数据集合 + * @param fileName + * 文件名 + * @param sheetName + * 工作表名称 + * @param clazz + * 导出数据类型 + * @param response + * 响应对象 + */ + public static void export(List list, String fileName, String sheetName, Class clazz, + HttpServletResponse response) { + try { + fileName = String.format("%s_%s.xlsx", fileName, DateUtil.format(new Date(), "yyyyMMddHHmmss")); + fileName = URLUtil.encode(fileName); + response.setHeader("Content-disposition", "attachment;filename=" + fileName); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); + EasyExcel.write(response.getOutputStream(), clazz).autoCloseStream(false) + // 自动适配宽度 + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + // 自动转换大数值 + .registerConverter(new ExcelBigNumberConverter()).sheet(sheetName).doWrite(list); + } catch (Exception e) { + Log.error("Export excel occurred an error.", e); + throw new ServiceException("导出 Excel 出现错误"); + } + } +} diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/ExceptionUtils.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/ExceptionUtils.java index 38dbe272..3f1849e2 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/ExceptionUtils.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/ExceptionUtils.java @@ -91,6 +91,17 @@ public class ExceptionUtils { return exToDefault(supplier, null, exConsumer); } + /** + * 如果有异常,返回空字符串 + * + * @param exSupplier + * 可能会出现异常的方法执行 + * @return / + */ + public static String exToBlank(ExSupplier exSupplier) { + return exToDefault(exSupplier, ""); + } + /** * 如果有异常,返回默认值 * diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/enums/LogStatusEnum.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/enums/LogStatusEnum.java index 2c533e87..13bc1147 100644 --- a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/enums/LogStatusEnum.java +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/enums/LogStatusEnum.java @@ -19,7 +19,7 @@ package top.charles7c.cnadmin.monitor.enums; import lombok.Getter; import lombok.RequiredArgsConstructor; -import com.baomidou.mybatisplus.annotation.IEnum; +import top.charles7c.cnadmin.common.base.BaseEnum; /** * 操作状态枚举 @@ -29,7 +29,7 @@ import com.baomidou.mybatisplus.annotation.IEnum; */ @Getter @RequiredArgsConstructor -public enum LogStatusEnum implements IEnum { +public enum LogStatusEnum implements BaseEnum { /** 成功 */ SUCCESS(1, "成功"), diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/vo/DeptDetailVO.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/vo/DeptDetailVO.java index 163da626..4425cb4e 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/vo/DeptDetailVO.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/vo/DeptDetailVO.java @@ -17,11 +17,14 @@ package top.charles7c.cnadmin.system.model.vo; import lombok.Data; -import lombok.experimental.Accessors; import io.swagger.v3.oas.annotations.media.Schema; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; + import top.charles7c.cnadmin.common.base.BaseDetailVO; +import top.charles7c.cnadmin.common.config.easyexcel.ExcelBaseEnumConverter; import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum; /** @@ -31,7 +34,7 @@ import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum; * @since 2023/2/1 22:19 */ @Data -@Accessors(chain = true) +@ExcelIgnoreUnannotated @Schema(description = "部门详情信息") public class DeptDetailVO extends BaseDetailVO { @@ -41,12 +44,14 @@ public class DeptDetailVO extends BaseDetailVO { * 部门 ID */ @Schema(description = "部门 ID") + @ExcelProperty(value = "部门ID") private Long deptId; /** * 部门名称 */ @Schema(description = "部门名称") + @ExcelProperty(value = "部门名称") private String deptName; /** @@ -65,11 +70,13 @@ public class DeptDetailVO extends BaseDetailVO { * 描述 */ @Schema(description = "描述") + @ExcelProperty(value = "描述") private String description; /** * 状态(1启用 2禁用) */ @Schema(description = "状态(1启用 2禁用)") + @ExcelProperty(value = "状态", converter = ExcelBaseEnumConverter.class) private DisEnableStatusEnum status; } diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/DeptService.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/DeptService.java index 468f2d50..5ac0863c 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/DeptService.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/DeptService.java @@ -18,6 +18,8 @@ package top.charles7c.cnadmin.system.service; import java.util.List; +import javax.servlet.http.HttpServletResponse; + import cn.hutool.core.lang.tree.Tree; import top.charles7c.cnadmin.common.base.BaseService; @@ -64,4 +66,14 @@ public interface DeptService extends BaseService list(DeptQuery query) { + List deptList = this.listDept(query); + List list = BeanUtil.copyToList(deptList, DeptVO.class); + list.forEach(this::fill); + return list; + } + + /** + * 查询列表 + * + * @param query + * 查询条件 + * @return 列表信息 + */ + private List listDept(DeptQuery query) { QueryWrapper queryWrapper = QueryHelper.build(query); queryWrapper.lambda().orderByAsc(DeptDO::getParentId).orderByAsc(DeptDO::getDeptSort) .orderByDesc(DeptDO::getCreateTime); List deptList = baseMapper.selectList(queryWrapper); - List list = BeanUtil.copyToList(deptList, DeptVO.class); - list.forEach(this::fill); - return list; + return CollUtil.isNotEmpty(deptList) ? deptList : Collections.emptyList(); } @Override @@ -170,6 +186,14 @@ public class DeptServiceImpl extends BaseServiceImpl deptList = this.listDept(query); + List list = BeanUtil.copyToList(deptList, DeptDetailVO.class); + list.forEach(this::fillDetail); + ExcelUtils.export(list, "部门数据", DeptDetailVO.class, response); + } + /** * 填充数据 * diff --git a/continew-admin-ui/src/api/common/index.ts b/continew-admin-ui/src/api/common/index.ts index f07a5788..aee542b2 100644 --- a/continew-admin-ui/src/api/common/index.ts +++ b/continew-admin-ui/src/api/common/index.ts @@ -1,9 +1,9 @@ import axios from 'axios'; import qs from 'query-string'; -import { DeptParams } from '@/api/system/dept'; +import { DeptParam } from '@/api/system/dept'; import { TreeNodeData } from '@arco-design/web-vue'; -export default function listDeptTree(params: DeptParams) { +export default function listDeptTree(params: DeptParam) { return axios.get('/common/tree/dept', { params, paramsSerializer: (obj) => { diff --git a/continew-admin-ui/src/api/system/dept.ts b/continew-admin-ui/src/api/system/dept.ts index 0a43a641..d78c4b71 100644 --- a/continew-admin-ui/src/api/system/dept.ts +++ b/continew-admin-ui/src/api/system/dept.ts @@ -17,12 +17,12 @@ export interface DeptRecord { children?: Array, } -export interface DeptParams { +export interface DeptParam { deptName?: string; status?: number; } -export function listDept(params: DeptParams) { +export function listDept(params: DeptParam) { return axios.get(`${BASE_URL}/all`, { params, paramsSerializer: (obj) => { @@ -45,4 +45,14 @@ export function updateDept(req: DeptRecord) { export function deleteDept(ids: number | Array) { return axios.delete(`${BASE_URL}/${ids}`); +} + +export function exportDept(params: DeptParam) { + return axios.get(`${BASE_URL}/export`, { + params, + paramsSerializer: (obj) => { + return qs.stringify(obj); + }, + responseType: 'blob', + }); } \ No newline at end of file diff --git a/continew-admin-ui/src/utils/request.ts b/continew-admin-ui/src/utils/request.ts index 75373d20..b8591a0f 100644 --- a/continew-admin-ui/src/utils/request.ts +++ b/continew-admin-ui/src/utils/request.ts @@ -36,11 +36,17 @@ export interface HttpResponse { // response interceptors axios.interceptors.response.use((response: AxiosResponse) => { + // 二进制数据则直接返回 + if(response.request.responseType === 'blob' || response.request.responseType === 'arraybuffer'){ + return response; + } + + // 操作成功则直接返回 const res = response.data; if (res.success) { return res; } - + // 操作失败,弹出错误提示 Message.error({ content: res.msg, duration: 3000 diff --git a/continew-admin-ui/src/views/system/dept/index.vue b/continew-admin-ui/src/views/system/dept/index.vue index 42695e89..586cd997 100644 --- a/continew-admin-ui/src/views/system/dept/index.vue +++ b/continew-admin-ui/src/views/system/dept/index.vue @@ -51,12 +51,12 @@ 删除 + + 导出 + - - 导出 - @@ -239,12 +239,13 @@ import { SelectOptionData, TreeNodeData } from '@arco-design/web-vue'; import { DeptRecord, - DeptParams, + DeptParam, listDept, getDept, createDept, updateDept, deleteDept, + exportDept, } from '@/api/system/dept'; import listDeptTree from '@/api/common'; @@ -267,6 +268,7 @@ const multiple = ref(true); const loading = ref(false); const detailLoading = ref(false); + const exportLoading = ref(false); const visible = ref(false); const detailVisible = ref(false); const statusOptions = ref([ @@ -296,7 +298,7 @@ * * @param params 查询参数 */ - const getList = (params: DeptParams = { ...queryParams.value }) => { + const getList = (params: DeptParam = { ...queryParams.value }) => { loading.value = true; listDept(params).then((res) => { deptList.value = res.data; @@ -309,29 +311,6 @@ }; getList(); - /** - * 确定 - */ - const handleOk = () => { - proxy.$refs.formRef.validate((valid: any) => { - if (!valid) { - if (form.value.deptId !== undefined) { - updateDept(form.value).then((res) => { - handleCancel(); - getList(); - proxy.$message.success(res.msg); - }); - } else { - createDept(form.value).then((res) => { - handleCancel(); - getList(); - proxy.$message.success(res.msg); - }); - } - } - }); - }; - /** * 打开新增对话框 */ @@ -384,6 +363,29 @@ proxy.$refs.formRef.resetFields(); }; + /** + * 确定 + */ + const handleOk = () => { + proxy.$refs.formRef.validate((valid: any) => { + if (!valid) { + if (form.value.deptId !== undefined) { + updateDept(form.value).then((res) => { + handleCancel(); + getList(); + proxy.$message.success(res.msg); + }); + } else { + createDept(form.value).then((res) => { + handleCancel(); + getList(); + proxy.$message.success(res.msg); + }); + } + } + }); + }; + /** * 查看详情 * @@ -407,20 +409,6 @@ detailVisible.value = false; }; - /** - * 修改状态 - * - * @param record 记录信息 - */ - const handleChangeStatus = (record: DeptRecord) => { - const tip = record.status === 1 ? '启用' : '禁用'; - updateDept(record).then((res) => { - proxy.$message.success(`${tip}成功`); - }).catch(() => { - record.status = (record.status === 1) ? 2 : 1; - }); - }; - /** * 批量删除 */ @@ -463,6 +451,52 @@ multiple.value = !rowKeys.length; }; + /** + * 导出 + */ + const handleExport = () => { + if (exportLoading.value) return; + exportLoading.value = true; + exportDept({ ...queryParams.value }).then(async(res) => { + const blob = new Blob([res.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8'}); + const contentDisposition = res.headers['content-disposition'] + const pattern = new RegExp('filename=([^;]+\\.[^\\.;]+);*') + const result = pattern.exec(contentDisposition) || ''; + // 对名字进行解码 + const fileName = window.decodeURI(result[1]) + // 创建下载的链接 + const downloadElement = document.createElement('a'); + const href = window.URL.createObjectURL(blob); + downloadElement.style.display = 'none'; + downloadElement.href = href; + // 下载后文件名 + downloadElement.download = fileName; + document.body.appendChild(downloadElement); + // 点击下载 + downloadElement.click(); + // 下载完成,移除元素 + document.body.removeChild(downloadElement); + // 释放掉 blob 对象 + window.URL.revokeObjectURL(href); + }).finally(() => { + exportLoading.value = false; + }); + }; + + /** + * 修改状态 + * + * @param record 记录信息 + */ + const handleChangeStatus = (record: DeptRecord) => { + const tip = record.status === 1 ? '启用' : '禁用'; + updateDept(record).then((res) => { + proxy.$message.success(`${tip}成功`); + }).catch(() => { + record.status = (record.status === 1) ? 2 : 1; + }); + }; + /** * 过滤部门树 * diff --git a/continew-admin-ui/src/views/system/user/center/components/security-settings/update-email.vue b/continew-admin-ui/src/views/system/user/center/components/security-settings/update-email.vue index c6da5418..219ce67e 100644 --- a/continew-admin-ui/src/views/system/user/center/components/security-settings/update-email.vue +++ b/continew-admin-ui/src/views/system/user/center/components/security-settings/update-email.vue @@ -148,7 +148,7 @@ captchaLoading.value = false; captchaDisable.value = true; captchaBtnNameKey.value = `${t('userCenter.securitySettings.updateEmail.form.reSendCaptcha')}(${captchaTime.value -= 1}s)`; - captchaTimer.value = window.setInterval(function() { + captchaTimer.value = window.setInterval(() => { captchaTime.value -= 1; captchaBtnNameKey.value = `${t('userCenter.securitySettings.updateEmail.form.reSendCaptcha')}(${captchaTime.value}s)`; if (captchaTime.value < 0) { diff --git a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/system/DeptController.java b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/system/DeptController.java index 5c77b06d..354bf037 100644 --- a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/system/DeptController.java +++ b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/system/DeptController.java @@ -20,6 +20,8 @@ import static top.charles7c.cnadmin.common.annotation.CrudRequestMapping.Api; import java.util.List; +import javax.servlet.http.HttpServletResponse; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -52,4 +54,10 @@ public class DeptController extends BaseController list = baseService.list(query); return R.ok(baseService.buildListTree(list)); } + + @Operation(summary = "导出部门数据") + @GetMapping("/export") + public void export(@Validated DeptQuery query, HttpServletResponse response) { + baseService.export(query, response); + } } diff --git a/continew-admin-webapi/src/main/resources/application-dev.yml b/continew-admin-webapi/src/main/resources/application-dev.yml index 3c812369..62148783 100644 --- a/continew-admin-webapi/src/main/resources/application-dev.yml +++ b/continew-admin-webapi/src/main/resources/application-dev.yml @@ -187,3 +187,5 @@ cors: allowedMethods: '*' # 配置允许跨域的请求头 allowedHeaders: '*' + # 配置允许跨域的响应头 + exposedHeaders: '*' \ No newline at end of file diff --git a/continew-admin-webapi/src/main/resources/application-prod.yml b/continew-admin-webapi/src/main/resources/application-prod.yml index 962f748b..461638e3 100644 --- a/continew-admin-webapi/src/main/resources/application-prod.yml +++ b/continew-admin-webapi/src/main/resources/application-prod.yml @@ -178,3 +178,5 @@ cors: allowedMethods: '*' # 配置允许跨域的请求头 allowedHeaders: '*' + # 配置允许跨域的响应头 + exposedHeaders: '*' diff --git a/pom.xml b/pom.xml index c5995b6c..8a49ce2a 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,7 @@ limitations under the License. 3.9.1 + 3.2.0 2.7.6 4.0.0 3.19.0 @@ -113,6 +114,13 @@ limitations under the License. + + + com.alibaba + easyexcel + ${easyexcel.version} + + net.dreamlu