mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-10-31 10:57:13 +08:00 
			
		
		
		
	refactor: 💥 项目包结构 top.charles7c.cnadmin => top.charles7c.continew.admin
This commit is contained in:
		| @@ -0,0 +1,36 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.annotation; | ||||
|  | ||||
| import java.lang.annotation.*; | ||||
|  | ||||
| /** | ||||
|  * 数据权限注解 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/3/6 23:34 | ||||
|  */ | ||||
| @Target(ElementType.METHOD) | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| @Documented | ||||
| public @interface DataPermission { | ||||
|  | ||||
|     /** | ||||
|      * 表别名 | ||||
|      */ | ||||
|     String value() default ""; | ||||
| } | ||||
| @@ -0,0 +1,47 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.base; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import org.apache.ibatis.annotations.Param; | ||||
|  | ||||
| import com.baomidou.mybatisplus.core.conditions.Wrapper; | ||||
| import com.baomidou.mybatisplus.core.metadata.IPage; | ||||
| import com.baomidou.mybatisplus.core.toolkit.Constants; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.annotation.DataPermission; | ||||
| import top.charles7c.continew.starter.extension.crud.base.BaseMapper; | ||||
|  | ||||
| /** | ||||
|  * 数据权限 Mapper 基类 | ||||
|  * | ||||
|  * @param <T> | ||||
|  *            实体类 | ||||
|  * @author Charles7c | ||||
|  * @since 2023/9/3 21:50 | ||||
|  */ | ||||
| public interface DataPermissionMapper<T> extends BaseMapper<T> { | ||||
|  | ||||
|     @Override | ||||
|     @DataPermission | ||||
|     List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); | ||||
|  | ||||
|     @Override | ||||
|     @DataPermission | ||||
|     List<T> selectList(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); | ||||
| } | ||||
| @@ -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.continew.admin.common.config; | ||||
|  | ||||
| import java.util.Map; | ||||
|  | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
|  | ||||
| import org.springframework.boot.autoconfigure.cache.CacheProperties; | ||||
| import org.springframework.cache.annotation.CachingConfigurerSupport; | ||||
| import org.springframework.cache.annotation.EnableCaching; | ||||
| import org.springframework.cache.interceptor.KeyGenerator; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.data.redis.cache.RedisCacheConfiguration; | ||||
| import org.springframework.data.redis.serializer.*; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonTypeInfo; | ||||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||||
|  | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import cn.hutool.crypto.digest.DigestUtil; | ||||
| import cn.hutool.json.JSONUtil; | ||||
|  | ||||
| /** | ||||
|  * Spring Cache 配置 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/28 23:17 | ||||
|  */ | ||||
| @Slf4j | ||||
| @EnableCaching | ||||
| @Configuration | ||||
| @RequiredArgsConstructor | ||||
| public class SpringCacheConfiguration extends CachingConfigurerSupport { | ||||
|  | ||||
|     private final ObjectMapper objectMapper; | ||||
|  | ||||
|     /** | ||||
|      * 解决 Spring Cache(@Cacheable)缓存乱码问题 | ||||
|      */ | ||||
|     @Bean | ||||
|     public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) { | ||||
|         ObjectMapper objectMapperCopy = objectMapper.copy(); | ||||
|         objectMapperCopy.activateDefaultTyping(objectMapperCopy.getPolymorphicTypeValidator(), | ||||
|             ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); | ||||
|         RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() | ||||
|             .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) | ||||
|             .serializeValuesWith(RedisSerializationContext.SerializationPair | ||||
|                 .fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapperCopy))); | ||||
|         CacheProperties.Redis redisCacheProperties = cacheProperties.getRedis(); | ||||
|         if (null != redisCacheProperties.getTimeToLive()) { | ||||
|             redisCacheConfiguration = redisCacheConfiguration.entryTtl(redisCacheProperties.getTimeToLive()); | ||||
|         } | ||||
|         if (!redisCacheProperties.isCacheNullValues()) { | ||||
|             redisCacheConfiguration = redisCacheConfiguration.disableCachingNullValues(); | ||||
|         } | ||||
|         return redisCacheConfiguration; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 自定义缓存 key 生成策略(如果 @Cacheable 不指定 key,则默认使用该策略) | ||||
|      */ | ||||
|     @Bean | ||||
|     @Override | ||||
|     public KeyGenerator keyGenerator() { | ||||
|         return (target, method, params) -> { | ||||
|             String key = StrUtil.toUnderlineCase(method.getName()).toUpperCase(); | ||||
|             Map<String, Object> paramMap = MapUtil.newHashMap(params.length); | ||||
|             for (int i = 0; i < params.length; i++) { | ||||
|                 paramMap.put(String.valueOf(i), params[i]); | ||||
|             } | ||||
|             return String.format("%s:%s", key, DigestUtil.sha256Hex(JSONUtil.toJsonStr(paramMap))); | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,82 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.config; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
|  | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.http.converter.ByteArrayHttpMessageConverter; | ||||
| import org.springframework.http.converter.HttpMessageConverter; | ||||
| import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; | ||||
| import org.springframework.web.servlet.config.annotation.EnableWebMvc; | ||||
| import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; | ||||
| import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.config.properties.LocalStorageProperties; | ||||
| import top.charles7c.continew.starter.core.constant.StringConstants; | ||||
|  | ||||
| /** | ||||
|  * Web MVC 配置 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/11 19:40 | ||||
|  */ | ||||
| @EnableWebMvc | ||||
| @Configuration | ||||
| @RequiredArgsConstructor | ||||
| public class WebMvcConfiguration implements WebMvcConfigurer { | ||||
|  | ||||
|     private final LocalStorageProperties localStorageProperties; | ||||
|     private final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter; | ||||
|  | ||||
|     /** | ||||
|      * 静态资源处理器配置 | ||||
|      */ | ||||
|     @Override | ||||
|     public void addResourceHandlers(ResourceHandlerRegistry registry) { | ||||
|         LocalStorageProperties.LocalStoragePath path = localStorageProperties.getPath(); | ||||
|         String avatarUtl = "file:" + path.getAvatar().replace(StringConstants.BACKSLASH, StringConstants.SLASH); | ||||
|         String fileUrl = "file:" + path.getFile().replace(StringConstants.BACKSLASH, StringConstants.SLASH); | ||||
|         registry.addResourceHandler(localStorageProperties.getFilePattern()).addResourceLocations(fileUrl) | ||||
|             .setCachePeriod(0); | ||||
|         registry.addResourceHandler(localStorageProperties.getAvatarPattern()).addResourceLocations(avatarUtl) | ||||
|             .setCachePeriod(0); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 解决 Jackson2ObjectMapperBuilderCustomizer 配置不生效的问题 | ||||
|      * <p> | ||||
|      * MappingJackson2HttpMessageConverter 对象在程序启动时创建了多个,移除多余的,保证只有一个 | ||||
|      * </p> | ||||
|      */ | ||||
|     @Override | ||||
|     public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { | ||||
|         // 自定义 converters 时,需要手动在最前面添加 ByteArrayHttpMessageConverter | ||||
|         // 否则 Spring Doc OpenAPI 的 /*/api-docs/**(例如:/v3/api-docs/default)接口响应内容会变为 Base64 编码后的内容,最终导致接口文档解析失败 | ||||
|         // 详情请参阅:https://github.com/springdoc/springdoc-openapi/issues/2143 | ||||
|         converters.add(new ByteArrayHttpMessageConverter()); | ||||
|         converters.removeIf(MappingJackson2HttpMessageConverter.class::isInstance); | ||||
|         if (Objects.isNull(mappingJackson2HttpMessageConverter)) { | ||||
|             converters.add(new MappingJackson2HttpMessageConverter()); | ||||
|         } else { | ||||
|             converters.add(mappingJackson2HttpMessageConverter); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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.continew.admin.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 top.charles7c.continew.starter.core.constant.StringConstants; | ||||
| import top.charles7c.continew.starter.extension.crud.base.IBaseEnum; | ||||
|  | ||||
| /** | ||||
|  * Easy Excel 枚举基类转换器 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/2/5 19:29 | ||||
|  */ | ||||
| public class ExcelBaseEnumConverter implements Converter<IBaseEnum<Integer>> { | ||||
|  | ||||
|     @Override | ||||
|     public Class<IBaseEnum> supportJavaTypeKey() { | ||||
|         return IBaseEnum.class; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public CellDataTypeEnum supportExcelTypeKey() { | ||||
|         return CellDataTypeEnum.STRING; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 转换为 Java 数据(读取 Excel) | ||||
|      */ | ||||
|     @Override | ||||
|     public IBaseEnum convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, | ||||
|         GlobalConfiguration globalConfiguration) { | ||||
|         return this.getEnum(IBaseEnum.class, Convert.toStr(cellData.getData())); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 转换为 Excel 数据(写入 Excel) | ||||
|      */ | ||||
|     @Override | ||||
|     public WriteCellData<String> convertToExcelData(IBaseEnum<Integer> value, ExcelContentProperty contentProperty, | ||||
|         GlobalConfiguration globalConfiguration) { | ||||
|         if (null == value) { | ||||
|             return new WriteCellData<>(StringConstants.EMPTY); | ||||
|         } | ||||
|         return new WriteCellData<>(value.getDescription()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 通过 value 获取枚举对象,获取不到时为 {@code null} | ||||
|      * | ||||
|      * @param enumType | ||||
|      *            枚举类型 | ||||
|      * @param description | ||||
|      *            描述 | ||||
|      * @return 对应枚举 ,获取不到时为 {@code null} | ||||
|      */ | ||||
|     private IBaseEnum<Integer> getEnum(Class<?> enumType, String description) { | ||||
|         Object[] enumConstants = enumType.getEnumConstants(); | ||||
|         for (Object enumConstant : enumConstants) { | ||||
|             if (ClassUtil.isAssignable(IBaseEnum.class, enumType)) { | ||||
|                 IBaseEnum<Integer> baseEnum = (IBaseEnum<Integer>)enumConstant; | ||||
|                 if (baseEnum.getDescription().equals(description)) { | ||||
|                     return baseEnum; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,78 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.config.jackson; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.lang.reflect.Field; | ||||
|  | ||||
| import com.fasterxml.jackson.core.*; | ||||
| import com.fasterxml.jackson.databind.DeserializationContext; | ||||
| import com.fasterxml.jackson.databind.JsonDeserializer; | ||||
| import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; | ||||
|  | ||||
| import cn.hutool.core.util.ClassUtil; | ||||
| import cn.hutool.core.util.ReflectUtil; | ||||
|  | ||||
| import top.charles7c.continew.starter.extension.crud.base.IBaseEnum; | ||||
|  | ||||
| /** | ||||
|  * 通用枚举基类 BaseEnum 反序列化器 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/1/8 13:56 | ||||
|  */ | ||||
| @JacksonStdImpl | ||||
| public class BaseEnumDeserializer extends JsonDeserializer<IBaseEnum> { | ||||
|  | ||||
|     /** 静态实例 */ | ||||
|     public static final BaseEnumDeserializer SERIALIZER_INSTANCE = new BaseEnumDeserializer(); | ||||
|  | ||||
|     @Override | ||||
|     public IBaseEnum deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) | ||||
|         throws IOException { | ||||
|         Class<?> targetClass = jsonParser.getCurrentValue().getClass(); | ||||
|         String fieldName = jsonParser.getCurrentName(); | ||||
|         String value = jsonParser.getText(); | ||||
|         return this.getEnum(targetClass, value, fieldName); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 通过某字段对应值获取枚举实例,获取不到时为 {@code null} | ||||
|      * | ||||
|      * @param targetClass | ||||
|      *            目标类型 | ||||
|      * @param value | ||||
|      *            字段值 | ||||
|      * @param fieldName | ||||
|      *            字段名 | ||||
|      * @return 对应枚举实例 ,获取不到时为 {@code null} | ||||
|      */ | ||||
|     private IBaseEnum 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(IBaseEnum.class, fieldTypeClass)) { | ||||
|                 IBaseEnum baseEnum = (IBaseEnum)enumConstant; | ||||
|                 if (baseEnum.getValue().equals(Integer.valueOf(value))) { | ||||
|                     return baseEnum; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,44 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.config.jackson; | ||||
|  | ||||
| import java.io.IOException; | ||||
|  | ||||
| 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.continew.starter.extension.crud.base.IBaseEnum; | ||||
|  | ||||
| /** | ||||
|  * 通用枚举接口 BaseEnum 序列化器 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/1/8 13:56 | ||||
|  */ | ||||
| @JacksonStdImpl | ||||
| public class BaseEnumSerializer extends JsonSerializer<IBaseEnum> { | ||||
|  | ||||
|     /** 静态实例 */ | ||||
|     public static final BaseEnumSerializer SERIALIZER_INSTANCE = new BaseEnumSerializer(); | ||||
|  | ||||
|     @Override | ||||
|     public void serialize(IBaseEnum value, JsonGenerator generator, SerializerProvider serializers) throws IOException { | ||||
|         generator.writeObject(value.getValue()); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,56 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.config.jackson; | ||||
|  | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
|  | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; | ||||
|  | ||||
| import com.fasterxml.jackson.databind.*; | ||||
| import com.fasterxml.jackson.databind.module.SimpleModule; | ||||
|  | ||||
| import top.charles7c.continew.starter.extension.crud.base.IBaseEnum; | ||||
|  | ||||
| /** | ||||
|  * Jackson 配置 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/11 13:23 | ||||
|  */ | ||||
| @Slf4j | ||||
| @Configuration | ||||
| public class JacksonConfiguration { | ||||
|  | ||||
|     /** | ||||
|      * 针对枚举基类 BaseEnum 的序列化和反序列化 | ||||
|      */ | ||||
|     @Bean | ||||
|     public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) { | ||||
|         SimpleModule simpleModule = new SimpleModule(); | ||||
|         simpleModule.addSerializer(IBaseEnum.class, BaseEnumSerializer.SERIALIZER_INSTANCE); | ||||
|  | ||||
|         SimpleDeserializersWrapper deserializers = new SimpleDeserializersWrapper(); | ||||
|         deserializers.addDeserializer(IBaseEnum.class, BaseEnumDeserializer.SERIALIZER_INSTANCE); | ||||
|         simpleModule.setDeserializers(deserializers); | ||||
|  | ||||
|         ObjectMapper objectMapper = builder.createXmlMapper(false).build(); | ||||
|         objectMapper.registerModule(simpleModule); | ||||
|         return objectMapper; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,68 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.config.jackson; | ||||
|  | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
|  | ||||
| import com.fasterxml.jackson.databind.BeanDescription; | ||||
| import com.fasterxml.jackson.databind.DeserializationConfig; | ||||
| import com.fasterxml.jackson.databind.JsonDeserializer; | ||||
| import com.fasterxml.jackson.databind.JsonMappingException; | ||||
| import com.fasterxml.jackson.databind.module.SimpleDeserializers; | ||||
| import com.fasterxml.jackson.databind.type.ClassKey; | ||||
|  | ||||
| /** | ||||
|  * 反序列化器包装类(重写 Jackson 反序列化枚举方法,参阅:FasterXML/jackson-databind#2842) | ||||
|  * | ||||
|  * <p> | ||||
|  * 默认处理:<br> | ||||
|  * 1. Jackson 会先查找指定枚举类型对应的反序列化器(例如:GenderEnum 枚举类型,则是找 GenderEnum 枚举类型的对应反序列化器);<br> | ||||
|  * 2. 如果找不到则开始查找 Enum 类型(所有枚举父类)的反序列化器;<br> | ||||
|  * 3. 如果都找不到则会采用默认的枚举反序列化器(它仅能根据枚举类型的 name、ordinal 来进行反序列化)。 | ||||
|  * </p> | ||||
|  * <p> | ||||
|  * 重写增强后:<br> | ||||
|  * 1. 同默认 1;<br> | ||||
|  * 2. 同默认 2;<br> | ||||
|  * 3. 如果也找不到 Enum 类型(所有枚举父类)的反序列化器,开始查找指定枚举类型的接口的反序列化器(例如:GenderEnum 枚举类型,则是找它的接口 BaseEnum 的反序列化器);<br> | ||||
|  * 4. 同默认 3。 | ||||
|  * </p> | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/1/8 13:28 | ||||
|  */ | ||||
| @Slf4j | ||||
| public class SimpleDeserializersWrapper extends SimpleDeserializers { | ||||
|  | ||||
|     @Override | ||||
|     public JsonDeserializer<?> findEnumDeserializer(Class<?> type, DeserializationConfig config, | ||||
|         BeanDescription beanDesc) throws JsonMappingException { | ||||
|         JsonDeserializer<?> deser = super.findEnumDeserializer(type, config, beanDesc); | ||||
|         if (null != deser) { | ||||
|             return deser; | ||||
|         } | ||||
|  | ||||
|         // 重写增强:开始查找指定枚举类型的接口的反序列化器(例如:GenderEnum 枚举类型,则是找它的接口 BaseEnum 的反序列化器) | ||||
|         for (Class<?> typeInterface : type.getInterfaces()) { | ||||
|             deser = this._classMappings.get(new ClassKey(typeInterface)); | ||||
|             if (null != deser) { | ||||
|                 return deser; | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,186 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.config.mybatis; | ||||
|  | ||||
| import java.lang.reflect.Method; | ||||
| import java.util.Collections; | ||||
|  | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
|  | ||||
| import com.baomidou.mybatisplus.core.toolkit.StringUtils; | ||||
| import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.annotation.DataPermission; | ||||
| import top.charles7c.continew.admin.common.enums.DataScopeEnum; | ||||
| import top.charles7c.continew.admin.common.model.dto.LoginUser; | ||||
| import top.charles7c.continew.admin.common.model.dto.RoleDTO; | ||||
| import top.charles7c.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.charles7c.continew.starter.core.constant.StringConstants; | ||||
|  | ||||
| import net.sf.jsqlparser.expression.Expression; | ||||
| import net.sf.jsqlparser.expression.Function; | ||||
| import net.sf.jsqlparser.expression.LongValue; | ||||
| import net.sf.jsqlparser.expression.Parenthesis; | ||||
| import net.sf.jsqlparser.expression.operators.conditional.AndExpression; | ||||
| import net.sf.jsqlparser.expression.operators.conditional.OrExpression; | ||||
| import net.sf.jsqlparser.expression.operators.relational.EqualsTo; | ||||
| import net.sf.jsqlparser.expression.operators.relational.ExpressionList; | ||||
| import net.sf.jsqlparser.expression.operators.relational.InExpression; | ||||
| import net.sf.jsqlparser.schema.Column; | ||||
| import net.sf.jsqlparser.schema.Table; | ||||
| import net.sf.jsqlparser.statement.select.PlainSelect; | ||||
| import net.sf.jsqlparser.statement.select.SelectExpressionItem; | ||||
| import net.sf.jsqlparser.statement.select.SubSelect; | ||||
|  | ||||
| /** | ||||
|  * 数据权限处理器实现 | ||||
|  * <p> | ||||
|  * 来源:<a href="https://gitee.com/baomidou/mybatis-plus/issues/I37I90">DataPermissionInterceptor 如何使用?</a> | ||||
|  * </p> | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/3/6 23:19 | ||||
|  */ | ||||
| @Slf4j | ||||
| public class DataPermissionHandlerImpl implements DataPermissionHandler { | ||||
|  | ||||
|     /** ID */ | ||||
|     private static final String ID = "id"; | ||||
|     /** 部门 ID */ | ||||
|     private static final String DEPT_ID = "dept_id"; | ||||
|     /** 创建人 */ | ||||
|     private static final String CREATE_USER = "create_user"; | ||||
|     /** 部门表 */ | ||||
|     private static final String DEPT_TABLE = "sys_dept"; | ||||
|     /** 角色和部门关联表 */ | ||||
|     private static final String ROLE_DEPT_TABLE = "sys_role_dept"; | ||||
|     /** 角色和部门关联表:角色 ID */ | ||||
|     private static final String ROLE_ID = "role_id"; | ||||
|  | ||||
|     @Override | ||||
|     public Expression getSqlSegment(Expression where, String mappedStatementId) { | ||||
|         try { | ||||
|             Class<?> clazz = | ||||
|                 Class.forName(mappedStatementId.substring(0, mappedStatementId.lastIndexOf(StringConstants.DOT))); | ||||
|             String methodName = mappedStatementId.substring(mappedStatementId.lastIndexOf(StringConstants.DOT) + 1); | ||||
|             Method[] methodArr = clazz.getMethods(); | ||||
|             for (Method method : methodArr) { | ||||
|                 DataPermission dataPermission = method.getAnnotation(DataPermission.class); | ||||
|                 if (null != dataPermission | ||||
|                     && (method.getName().equals(methodName) || (method.getName() + "_COUNT").equals(methodName))) { | ||||
|                     LoginUser loginUser = LoginHelper.getLoginUser(); | ||||
|                     if (null != loginUser && !loginUser.isAdmin()) { | ||||
|                         return buildDataScopeFilter(loginUser, dataPermission.value(), where); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } catch (ClassNotFoundException e) { | ||||
|             log.error("Data permission handler build data scope filter occurred an error: {}.", e.getMessage(), e); | ||||
|         } | ||||
|         return where; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 构建数据范围过滤条件 | ||||
|      * | ||||
|      * @param user | ||||
|      *            当前登录用户 | ||||
|      * @param tableAlias | ||||
|      *            表别名 | ||||
|      * @param where | ||||
|      *            当前查询条件 | ||||
|      * @return 构建后查询条件 | ||||
|      */ | ||||
|     private Expression buildDataScopeFilter(LoginUser user, String tableAlias, Expression where) { | ||||
|         Expression expression = null; | ||||
|         for (RoleDTO role : user.getRoles()) { | ||||
|             DataScopeEnum dataScope = role.getDataScope(); | ||||
|             if (DataScopeEnum.ALL.equals(dataScope)) { | ||||
|                 return where; | ||||
|             } | ||||
|             if (DataScopeEnum.DEPT_AND_CHILD.equals(dataScope)) { | ||||
|                 // select t1.* from table as t1 where t1.`dept_id` in (select `id` from `sys_dept` where `id` = xxx or | ||||
|                 // find_in_set(xxx, `ancestors`)); | ||||
|                 // 构建子查询 | ||||
|                 SubSelect subSelect = new SubSelect(); | ||||
|                 PlainSelect select = new PlainSelect(); | ||||
|                 select.setSelectItems(Collections.singletonList(new SelectExpressionItem(new Column(ID)))); | ||||
|                 select.setFromItem(new Table(DEPT_TABLE)); | ||||
|                 EqualsTo equalsTo = new EqualsTo(); | ||||
|                 equalsTo.setLeftExpression(new Column(ID)); | ||||
|                 equalsTo.setRightExpression(new LongValue(user.getDeptId())); | ||||
|                 Function function = new Function(); | ||||
|                 function.setName("find_in_set"); | ||||
|                 function.setParameters(new ExpressionList(new LongValue(user.getDeptId()), new Column("ancestors"))); | ||||
|                 select.setWhere(new OrExpression(equalsTo, function)); | ||||
|                 subSelect.setSelectBody(select); | ||||
|                 // 构建父查询 | ||||
|                 InExpression inExpression = new InExpression(); | ||||
|                 inExpression.setLeftExpression(this.buildColumn(tableAlias, DEPT_ID)); | ||||
|                 inExpression.setRightExpression(subSelect); | ||||
|                 expression = null != expression ? new OrExpression(expression, inExpression) : inExpression; | ||||
|             } else if (DataScopeEnum.DEPT.equals(dataScope)) { | ||||
|                 // select t1.* from table as t1 where t1.`dept_id` = xxx; | ||||
|                 EqualsTo equalsTo = new EqualsTo(); | ||||
|                 equalsTo.setLeftExpression(this.buildColumn(tableAlias, DEPT_ID)); | ||||
|                 equalsTo.setRightExpression(new LongValue(user.getDeptId())); | ||||
|                 expression = null != expression ? new OrExpression(expression, equalsTo) : equalsTo; | ||||
|             } else if (DataScopeEnum.SELF.equals(dataScope)) { | ||||
|                 // select t1.* from table as t1 where t1.`create_user` = xxx; | ||||
|                 EqualsTo equalsTo = new EqualsTo(); | ||||
|                 equalsTo.setLeftExpression(this.buildColumn(tableAlias, CREATE_USER)); | ||||
|                 equalsTo.setRightExpression(new LongValue(user.getId())); | ||||
|                 expression = null != expression ? new OrExpression(expression, equalsTo) : equalsTo; | ||||
|             } else if (DataScopeEnum.CUSTOM.equals(dataScope)) { | ||||
|                 // select t1.* from table as t1 where t1.`dept_id` in (select `dept_id` from `sys_role_dept` where | ||||
|                 // `role_id` = xxx); | ||||
|                 // 构建子查询 | ||||
|                 SubSelect subSelect = new SubSelect(); | ||||
|                 PlainSelect select = new PlainSelect(); | ||||
|                 select.setSelectItems(Collections.singletonList(new SelectExpressionItem(new Column(DEPT_ID)))); | ||||
|                 select.setFromItem(new Table(ROLE_DEPT_TABLE)); | ||||
|                 EqualsTo equalsTo = new EqualsTo(); | ||||
|                 equalsTo.setLeftExpression(new Column(ROLE_ID)); | ||||
|                 equalsTo.setRightExpression(new LongValue(role.getId())); | ||||
|                 select.setWhere(equalsTo); | ||||
|                 subSelect.setSelectBody(select); | ||||
|                 // 构建父查询 | ||||
|                 InExpression inExpression = new InExpression(); | ||||
|                 inExpression.setLeftExpression(this.buildColumn(tableAlias, DEPT_ID)); | ||||
|                 inExpression.setRightExpression(subSelect); | ||||
|                 expression = null != expression ? new OrExpression(expression, inExpression) : inExpression; | ||||
|             } | ||||
|         } | ||||
|         return null != where ? new AndExpression(where, new Parenthesis(expression)) : expression; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 构建 Column | ||||
|      * | ||||
|      * @param tableAlias | ||||
|      *            表别名 | ||||
|      * @param columnName | ||||
|      *            字段名称 | ||||
|      * @return 带表别名字段 | ||||
|      */ | ||||
|     private Column buildColumn(String tableAlias, String columnName) { | ||||
|         if (StringUtils.isNotEmpty(tableAlias)) { | ||||
|             columnName = String.format("%s.%s", tableAlias, columnName); | ||||
|         } | ||||
|         return new Column(columnName); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,124 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.config.mybatis; | ||||
|  | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| import org.apache.ibatis.reflection.MetaObject; | ||||
|  | ||||
| import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; | ||||
|  | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.charles7c.continew.starter.extension.crud.base.BaseDO; | ||||
| import top.charles7c.continew.starter.extension.crud.exception.BusinessException; | ||||
|  | ||||
| /** | ||||
|  * MyBatis Plus 元对象处理器配置(插入或修改时自动填充) | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/22 19:52 | ||||
|  */ | ||||
| public class MyBatisPlusMetaObjectHandler implements MetaObjectHandler { | ||||
|  | ||||
|     /** 创建人 */ | ||||
|     private static final String CREATE_USER = "createUser"; | ||||
|     /** 创建时间 */ | ||||
|     private static final String CREATE_TIME = "createTime"; | ||||
|     /** 修改人 */ | ||||
|     private static final String UPDATE_USER = "updateUser"; | ||||
|     /** 修改时间 */ | ||||
|     private static final String UPDATE_TIME = "updateTime"; | ||||
|  | ||||
|     /** | ||||
|      * 插入数据时填充 | ||||
|      * | ||||
|      * @param metaObject | ||||
|      *            元对象 | ||||
|      */ | ||||
|     @Override | ||||
|     public void insertFill(MetaObject metaObject) { | ||||
|         try { | ||||
|             if (null == metaObject) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             Long createUser = LoginHelper.getUserId(); | ||||
|             LocalDateTime createTime = LocalDateTime.now(); | ||||
|             if (metaObject.getOriginalObject() instanceof BaseDO baseDO) { | ||||
|                 // 继承了 BaseDO 的类,填充创建信息 | ||||
|                 baseDO.setCreateUser(ObjectUtil.defaultIfNull(baseDO.getCreateUser(), createUser)); | ||||
|                 baseDO.setCreateTime(ObjectUtil.defaultIfNull(baseDO.getCreateTime(), createTime)); | ||||
|             } else { | ||||
|                 // 未继承 BaseDO 的类,如存在创建信息则进行填充 | ||||
|                 this.fillFieldValue(metaObject, CREATE_USER, createUser, false); | ||||
|                 this.fillFieldValue(metaObject, CREATE_TIME, createTime, false); | ||||
|             } | ||||
|         } catch (Exception e) { | ||||
|             throw new BusinessException("插入数据时自动填充异常:" + e.getMessage()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 修改数据时填充 | ||||
|      * | ||||
|      * @param metaObject | ||||
|      *            元对象 | ||||
|      */ | ||||
|     @Override | ||||
|     public void updateFill(MetaObject metaObject) { | ||||
|         try { | ||||
|             if (null == metaObject) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             Long updateUser = LoginHelper.getUserId(); | ||||
|             LocalDateTime updateTime = LocalDateTime.now(); | ||||
|             if (metaObject.getOriginalObject() instanceof BaseDO baseDO) { | ||||
|                 // 继承了 BaseDO 的类,填充修改信息 | ||||
|                 baseDO.setUpdateUser(updateUser); | ||||
|                 baseDO.setUpdateTime(updateTime); | ||||
|             } else { | ||||
|                 // 未继承 BaseDO 的类,根据类中拥有的修改信息进行填充,不存在修改信息不进行填充 | ||||
|                 this.fillFieldValue(metaObject, UPDATE_USER, updateUser, true); | ||||
|                 this.fillFieldValue(metaObject, UPDATE_TIME, updateTime, true); | ||||
|             } | ||||
|         } catch (Exception e) { | ||||
|             throw new BusinessException("修改数据时自动填充异常:" + e.getMessage()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 填充字段值 | ||||
|      * | ||||
|      * @param metaObject | ||||
|      *            元数据对象 | ||||
|      * @param fieldName | ||||
|      *            要填充的字段名 | ||||
|      * @param fillFieldValue | ||||
|      *            要填充的字段值 | ||||
|      * @param isOverride | ||||
|      *            如果字段值不为空,是否覆盖(true 覆盖、false 不覆盖) | ||||
|      */ | ||||
|     private void fillFieldValue(MetaObject metaObject, String fieldName, Object fillFieldValue, boolean isOverride) { | ||||
|         if (metaObject.hasSetter(fieldName)) { | ||||
|             Object fieldValue = metaObject.getValue(fieldName); | ||||
|             setFieldValByName(fieldName, null != fieldValue && !isOverride ? fieldValue : fillFieldValue, metaObject); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,40 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.config.mybatis; | ||||
|  | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
|  | ||||
| import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; | ||||
|  | ||||
| /** | ||||
|  * MyBatis Plus 配置 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/22 19:51 | ||||
|  */ | ||||
| @Configuration | ||||
| public class MybatisPlusConfiguration { | ||||
|  | ||||
|     /** | ||||
|      * 元对象处理器配置(插入或修改时自动填充) | ||||
|      */ | ||||
|     @Bean | ||||
|     public MetaObjectHandler metaObjectHandler() { | ||||
|         return new MyBatisPlusMetaObjectHandler(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,107 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.config.properties; | ||||
|  | ||||
| import java.awt.*; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.boot.context.properties.ConfigurationProperties; | ||||
| import org.springframework.stereotype.Component; | ||||
|  | ||||
| import com.wf.captcha.*; | ||||
|  | ||||
| /** | ||||
|  * 验证码配置属性 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/11 13:35 | ||||
|  */ | ||||
| @Data | ||||
| @Component | ||||
| @ConfigurationProperties(prefix = "captcha") | ||||
| public class CaptchaProperties { | ||||
|  | ||||
|     /** | ||||
|      * 图形验证码过期时间 | ||||
|      */ | ||||
|     @Value("${captcha.graphic.expirationInMinutes}") | ||||
|     private long expirationInMinutes; | ||||
|  | ||||
|     /** | ||||
|      * 邮箱验证码配置 | ||||
|      */ | ||||
|     private CaptchaMail mail; | ||||
|  | ||||
|     /** | ||||
|      * 短信验证码配置 | ||||
|      */ | ||||
|     private CaptchaSms sms; | ||||
|  | ||||
|     /** | ||||
|      * 邮箱验证码配置 | ||||
|      */ | ||||
|     @Data | ||||
|     public static class CaptchaMail { | ||||
|         /** | ||||
|          * 内容长度 | ||||
|          */ | ||||
|         private int length; | ||||
|  | ||||
|         /** | ||||
|          * 过期时间 | ||||
|          */ | ||||
|         private long expirationInMinutes; | ||||
|  | ||||
|         /** | ||||
|          * 限制时间 | ||||
|          */ | ||||
|         private long limitInSeconds; | ||||
|  | ||||
|         /** | ||||
|          * 模板路径 | ||||
|          */ | ||||
|         private String templatePath; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 短信验证码配置 | ||||
|      */ | ||||
|     @Data | ||||
|     public static class CaptchaSms { | ||||
|         /** | ||||
|          * 内容长度 | ||||
|          */ | ||||
|         private int length; | ||||
|  | ||||
|         /** | ||||
|          * 过期时间 | ||||
|          */ | ||||
|         private long expirationInMinutes; | ||||
|  | ||||
|         /** | ||||
|          * 限制时间 | ||||
|          */ | ||||
|         private long limitInSeconds; | ||||
|  | ||||
|         /** | ||||
|          * 模板 ID | ||||
|          */ | ||||
|         private String templateId; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,87 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.config.properties; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import org.springframework.boot.context.properties.ConfigurationProperties; | ||||
| import org.springframework.stereotype.Component; | ||||
|  | ||||
| import cn.hutool.system.OsInfo; | ||||
| import cn.hutool.system.SystemUtil; | ||||
|  | ||||
| /** | ||||
|  * 本地存储配置属性 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/1/2 19:43 | ||||
|  */ | ||||
| @Data | ||||
| @Component | ||||
| @ConfigurationProperties(prefix = "local-storage") | ||||
| public class LocalStorageProperties { | ||||
|  | ||||
|     /** 文件模式 */ | ||||
|     private String filePattern; | ||||
|  | ||||
|     /** 头像模式 */ | ||||
|     private String avatarPattern; | ||||
|  | ||||
|     /** 文件大小限制 */ | ||||
|     private Long maxSizeInMb; | ||||
|  | ||||
|     /** 头像大小限制 */ | ||||
|     private Long avatarMaxSizeInMb; | ||||
|  | ||||
|     /** Windows 系统本地存储路径 */ | ||||
|     private LocalStoragePath windows; | ||||
|  | ||||
|     /** Linux 系统本地存储路径 */ | ||||
|     private LocalStoragePath linux; | ||||
|  | ||||
|     /** MAC 系统本地存储路径 */ | ||||
|     private LocalStoragePath mac; | ||||
|  | ||||
|     /** | ||||
|      * 获取存储路径 | ||||
|      *  | ||||
|      * @return / | ||||
|      */ | ||||
|     public LocalStoragePath getPath() { | ||||
|         OsInfo osInfo = SystemUtil.getOsInfo(); | ||||
|         if (osInfo.isWindows()) { | ||||
|             return windows; | ||||
|         } | ||||
|         if (osInfo.isMac()) { | ||||
|             return mac; | ||||
|         } | ||||
|         return linux; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 本地存储路径 | ||||
|      */ | ||||
|     @Data | ||||
|     public static class LocalStoragePath { | ||||
|  | ||||
|         /** 文件存储路径 */ | ||||
|         private String file; | ||||
|  | ||||
|         /** 头像存储路径 */ | ||||
|         private String avatar; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,40 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.config.properties; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import cn.hutool.extra.spring.SpringUtil; | ||||
|  | ||||
| /** | ||||
|  * RSA 配置属性 | ||||
|  * | ||||
|  * @author Zheng Jie(ELADMIN) | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/21 20:21 | ||||
|  */ | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class RsaProperties { | ||||
|  | ||||
|     /** 私钥 */ | ||||
|     public static final String PRIVATE_KEY; | ||||
|  | ||||
|     static { | ||||
|         PRIVATE_KEY = SpringUtil.getProperty("rsa.privateKey"); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,70 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.constant; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| /** | ||||
|  * 缓存相关常量 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/22 19:30 | ||||
|  */ | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class CacheConstants { | ||||
|  | ||||
|     /** | ||||
|      * 登录用户键 | ||||
|      */ | ||||
|     public static final String LOGIN_USER_KEY = "LOGIN_USER"; | ||||
|  | ||||
|     /** | ||||
|      * 验证码键前缀 | ||||
|      */ | ||||
|     public static final String CAPTCHA_KEY_PREFIX = "CAPTCHA"; | ||||
|  | ||||
|     /** | ||||
|      * 限流键前缀 | ||||
|      */ | ||||
|     public static final String LIMIT_KEY_PREFIX = "LIMIT"; | ||||
|  | ||||
|     /** | ||||
|      * 用户缓存键前缀 | ||||
|      */ | ||||
|     public static final String USER_KEY_PREFIX = "USER"; | ||||
|  | ||||
|     /** | ||||
|      * 菜单缓存键前缀 | ||||
|      */ | ||||
|     public static final String MENU_KEY_PREFIX = "MENU"; | ||||
|  | ||||
|     /** | ||||
|      * 字典缓存键前缀 | ||||
|      */ | ||||
|     public static final String DICT_KEY_PREFIX = "DICT"; | ||||
|  | ||||
|     /** | ||||
|      * 参数缓存键前缀 | ||||
|      */ | ||||
|     public static final String OPTION_KEY_PREFIX = "OPTION"; | ||||
|  | ||||
|     /** | ||||
|      * 仪表盘缓存键前缀 | ||||
|      */ | ||||
|     public static final String DASHBOARD_KEY_PREFIX = "DASHBOARD"; | ||||
| } | ||||
| @@ -0,0 +1,35 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.constant; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| /** | ||||
|  * 文件相关常量 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/1/2 21:19 | ||||
|  */ | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class FileConstants { | ||||
|  | ||||
|     /** | ||||
|      * 头像支持的图片类型 | ||||
|      */ | ||||
|     public static final String[] AVATAR_SUPPORTED_IMG_TYPES = {"jpg", "png", "gif", "jpeg"}; | ||||
| } | ||||
| @@ -0,0 +1,57 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.constant; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import cn.hutool.core.lang.RegexPool; | ||||
|  | ||||
| /** | ||||
|  * 正则相关常量 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/1/10 20:06 | ||||
|  */ | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class RegexConstants implements RegexPool { | ||||
|  | ||||
|     /** | ||||
|      * 用户名正则(长度为 4 到 64 位,可以包含字母、数字,下划线,以字母开头) | ||||
|      */ | ||||
|     public static final String USERNAME = "^[a-zA-Z][a-zA-Z0-9_]{3,64}$"; | ||||
|  | ||||
|     /** | ||||
|      * 密码正则(长度为 6 到 32 位,可以包含字母、数字、下划线,特殊字符,同时包含字母和数字) | ||||
|      */ | ||||
|     public static final String PASSWORD = "^(?=.*\\d)(?=.*[a-z]).{6,32}$"; | ||||
|  | ||||
|     /** | ||||
|      * 通用编码正则(长度为 2 到 30 位,可以包含字母、数字,下划线,以字母开头) | ||||
|      */ | ||||
|     public static final String GENERAL_CODE = "^[a-zA-Z][a-zA-Z0-9_]{1,29}$"; | ||||
|  | ||||
|     /** | ||||
|      * 通用名称正则(长度为 2 到 30 位,可以包含中文、字母、数字、下划线,短横线) | ||||
|      */ | ||||
|     public static final String GENERAL_NAME = "^[\\u4e00-\\u9fa5a-zA-Z0-9_-]{2,30}$"; | ||||
|  | ||||
|     /** | ||||
|      * 包名正则(可以包含大小写字母、数字、下划线,每一级包名不能以数字开头) | ||||
|      */ | ||||
|     public static final String PACKAGE_NAME = "^(?:[a-zA-Z_][a-zA-Z0-9_]*\\.)*[a-zA-Z_][a-zA-Z0-9_]*$"; | ||||
| } | ||||
| @@ -0,0 +1,72 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.constant; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import top.charles7c.continew.starter.core.constant.StringConstants; | ||||
|  | ||||
| /** | ||||
|  * 系统相关常量 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/2/9 22:11 | ||||
|  */ | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class SysConstants { | ||||
|  | ||||
|     /** | ||||
|      * 管理员角色编码 | ||||
|      */ | ||||
|     public static final String ADMIN_ROLE_CODE = "admin"; | ||||
|  | ||||
|     /** | ||||
|      * 顶级部门 ID | ||||
|      */ | ||||
|     public static final Long SUPER_DEPT_ID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 顶级父 ID | ||||
|      */ | ||||
|     public static final Long SUPER_PARENT_ID = 0L; | ||||
|  | ||||
|     /** | ||||
|      * 全部权限标识 | ||||
|      */ | ||||
|     public static final String ALL_PERMISSION = StringConstants.ASTERISK; | ||||
|  | ||||
|     /** | ||||
|      * 默认密码 | ||||
|      */ | ||||
|     public static final String DEFAULT_PASSWORD = "123456"; | ||||
|  | ||||
|     /** | ||||
|      * 登录 URI | ||||
|      */ | ||||
|     public static final String LOGIN_URI = "/auth/login"; | ||||
|  | ||||
|     /** | ||||
|      * 退出 URI | ||||
|      */ | ||||
|     public static final String LOGOUT_URI = "/auth/logout"; | ||||
|  | ||||
|     /** | ||||
|      * 描述类字段后缀 | ||||
|      */ | ||||
|     public static final String DESCRIPTION_FIELD_SUFFIX = "String"; | ||||
| } | ||||
| @@ -0,0 +1,55 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.constant; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| /** | ||||
|  * UI 相关常量 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/9/17 14:12 | ||||
|  */ | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class UIConstants { | ||||
|  | ||||
|     /** | ||||
|      * 主色(极致蓝) | ||||
|      */ | ||||
|     public static final String COLOR_PRIMARY = "arcoblue"; | ||||
|  | ||||
|     /** | ||||
|      * 成功色(仙野绿) | ||||
|      */ | ||||
|     public static final String COLOR_SUCCESS = "green"; | ||||
|  | ||||
|     /** | ||||
|      * 警告色(活力橙) | ||||
|      */ | ||||
|     public static final String COLOR_WARNING = "orangered"; | ||||
|  | ||||
|     /** | ||||
|      * 错误色(浪漫红) | ||||
|      */ | ||||
|     public static final String COLOR_ERROR = "red"; | ||||
|  | ||||
|     /** | ||||
|      * 默认色(中性灰) | ||||
|      */ | ||||
|     public static final String COLOR_DEFAULT = "gray"; | ||||
| } | ||||
| @@ -0,0 +1,51 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.enums; | ||||
|  | ||||
| import lombok.Getter; | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import top.charles7c.continew.starter.extension.crud.base.IBaseEnum; | ||||
|  | ||||
| /** | ||||
|  * 数据权限枚举 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/2/8 22:58 | ||||
|  */ | ||||
| @Getter | ||||
| @RequiredArgsConstructor | ||||
| public enum DataScopeEnum implements IBaseEnum<Integer> { | ||||
|  | ||||
|     /** 全部数据权限 */ | ||||
|     ALL(1, "全部数据权限"), | ||||
|  | ||||
|     /** 本部门及以下数据权限 */ | ||||
|     DEPT_AND_CHILD(2, "本部门及以下数据权限"), | ||||
|  | ||||
|     /** 本部门数据权限 */ | ||||
|     DEPT(3, "本部门数据权限"), | ||||
|  | ||||
|     /** 仅本人数据权限 */ | ||||
|     SELF(4, "仅本人数据权限"), | ||||
|  | ||||
|     /** 自定义数据权限 */ | ||||
|     CUSTOM(5, "自定义数据权限"),; | ||||
|  | ||||
|     private final Integer value; | ||||
|     private final String description; | ||||
| } | ||||
| @@ -0,0 +1,44 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.enums; | ||||
|  | ||||
| import lombok.Getter; | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.constant.UIConstants; | ||||
| import top.charles7c.continew.starter.extension.crud.base.IBaseEnum; | ||||
|  | ||||
| /** | ||||
|  * 启用/禁用状态枚举 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/29 22:38 | ||||
|  */ | ||||
| @Getter | ||||
| @RequiredArgsConstructor | ||||
| public enum DisEnableStatusEnum implements IBaseEnum<Integer> { | ||||
|  | ||||
|     /** 启用 */ | ||||
|     ENABLE(1, "启用", UIConstants.COLOR_SUCCESS), | ||||
|  | ||||
|     /** 禁用 */ | ||||
|     DISABLE(2, "禁用", UIConstants.COLOR_ERROR),; | ||||
|  | ||||
|     private final Integer value; | ||||
|     private final String description; | ||||
|     private final String color; | ||||
| } | ||||
| @@ -0,0 +1,45 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.enums; | ||||
|  | ||||
| import lombok.Getter; | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import top.charles7c.continew.starter.extension.crud.base.IBaseEnum; | ||||
|  | ||||
| /** | ||||
|  * 性别枚举 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/29 21:59 | ||||
|  */ | ||||
| @Getter | ||||
| @RequiredArgsConstructor | ||||
| public enum GenderEnum implements IBaseEnum<Integer> { | ||||
|  | ||||
|     /** 未知 */ | ||||
|     UNKNOWN(0, "未知"), | ||||
|  | ||||
|     /** 男 */ | ||||
|     MALE(1, "男"), | ||||
|  | ||||
|     /** 女 */ | ||||
|     FEMALE(2, "女"),; | ||||
|  | ||||
|     private final Integer value; | ||||
|     private final String description; | ||||
| } | ||||
| @@ -0,0 +1,45 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.enums; | ||||
|  | ||||
| import lombok.Getter; | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import top.charles7c.continew.starter.extension.crud.base.IBaseEnum; | ||||
|  | ||||
| /** | ||||
|  * 菜单类型枚举 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/2/15 20:12 | ||||
|  */ | ||||
| @Getter | ||||
| @RequiredArgsConstructor | ||||
| public enum MenuTypeEnum implements IBaseEnum<Integer> { | ||||
|  | ||||
|     /** 目录 */ | ||||
|     DIR(1, "目录"), | ||||
|  | ||||
|     /** 菜单 */ | ||||
|     MENU(2, "菜单"), | ||||
|  | ||||
|     /** 按钮 */ | ||||
|     BUTTON(3, "按钮"),; | ||||
|  | ||||
|     private final Integer value; | ||||
|     private final String description; | ||||
| } | ||||
| @@ -0,0 +1,41 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.enums; | ||||
|  | ||||
| import lombok.Getter; | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.constant.UIConstants; | ||||
| import top.charles7c.continew.starter.extension.crud.base.IBaseEnum; | ||||
|  | ||||
| /** | ||||
|  * 消息类型枚举 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/11/2 20:08 | ||||
|  */ | ||||
| @Getter | ||||
| @RequiredArgsConstructor | ||||
| public enum MessageTypeEnum implements IBaseEnum<Integer> { | ||||
|  | ||||
|     /** 系统消息 */ | ||||
|     SYSTEM(1, "系统消息", UIConstants.COLOR_PRIMARY),; | ||||
|  | ||||
|     private final Integer value; | ||||
|     private final String description; | ||||
|     private final String color; | ||||
| } | ||||
| @@ -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.continew.admin.common.enums; | ||||
|  | ||||
| import lombok.Getter; | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| /** | ||||
|  * 第三方账号平台枚举 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/10/19 21:22 | ||||
|  */ | ||||
| @Getter | ||||
| @RequiredArgsConstructor | ||||
| public enum SocialSourceEnum { | ||||
|  | ||||
|     /** 码云 */ | ||||
|     GITEE("码云"), | ||||
|  | ||||
|     /** GitHub */ | ||||
|     GITHUB("GitHub"),; | ||||
|  | ||||
|     private final String description; | ||||
| } | ||||
| @@ -0,0 +1,44 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.enums; | ||||
|  | ||||
| import lombok.Getter; | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.constant.UIConstants; | ||||
| import top.charles7c.continew.starter.extension.crud.base.IBaseEnum; | ||||
|  | ||||
| /** | ||||
|  * 成功/失败状态枚举 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/2/26 21:35 | ||||
|  */ | ||||
| @Getter | ||||
| @RequiredArgsConstructor | ||||
| public enum SuccessFailureStatusEnum implements IBaseEnum<Integer> { | ||||
|  | ||||
|     /** 成功 */ | ||||
|     SUCCESS(1, "成功", UIConstants.COLOR_SUCCESS), | ||||
|  | ||||
|     /** 失败 */ | ||||
|     FAILURE(2, "失败", UIConstants.COLOR_ERROR),; | ||||
|  | ||||
|     private final Integer value; | ||||
|     private final String description; | ||||
|     private final String color; | ||||
| } | ||||
| @@ -0,0 +1,201 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.handler; | ||||
|  | ||||
| import java.util.Objects; | ||||
|  | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
| import jakarta.validation.ConstraintViolation; | ||||
| import jakarta.validation.ConstraintViolationException; | ||||
|  | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
|  | ||||
| import org.springframework.context.support.DefaultMessageSourceResolvable; | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.springframework.validation.BindException; | ||||
| import org.springframework.web.HttpRequestMethodNotSupportedException; | ||||
| import org.springframework.web.bind.MethodArgumentNotValidException; | ||||
| import org.springframework.web.bind.annotation.ExceptionHandler; | ||||
| import org.springframework.web.bind.annotation.RestControllerAdvice; | ||||
| import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; | ||||
| import org.springframework.web.multipart.MaxUploadSizeExceededException; | ||||
|  | ||||
| import cn.dev33.satoken.exception.NotLoginException; | ||||
| import cn.dev33.satoken.exception.NotPermissionException; | ||||
| import cn.dev33.satoken.exception.NotRoleException; | ||||
| import cn.hutool.core.util.NumberUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.util.StreamUtils; | ||||
| import top.charles7c.continew.admin.common.util.holder.LogContextHolder; | ||||
| import top.charles7c.continew.starter.core.util.ExceptionUtils; | ||||
| import top.charles7c.continew.starter.extension.crud.exception.BadRequestException; | ||||
| import top.charles7c.continew.starter.extension.crud.exception.BusinessException; | ||||
| import top.charles7c.continew.starter.extension.crud.model.resp.R; | ||||
|  | ||||
| /** | ||||
|  * 全局异常处理器 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @author Lion Li(<a href="https://gitee.com/dromara/RuoYi-Vue-Plus">RuoYi-Vue-Plus</a>) | ||||
|  * @since 2022/12/21 21:01 | ||||
|  */ | ||||
| @Slf4j | ||||
| @RestControllerAdvice | ||||
| public class GlobalExceptionHandler { | ||||
|  | ||||
|     /** | ||||
|      * 拦截自定义验证异常-错误请求 | ||||
|      */ | ||||
|     @ExceptionHandler(BadRequestException.class) | ||||
|     public R handleBadRequestException(BadRequestException e, HttpServletRequest request) { | ||||
|         log.warn("请求地址 [{}],自定义验证失败。", request.getRequestURI(), e); | ||||
|         LogContextHolder.setErrorMsg(e.getMessage()); | ||||
|         return R.fail(HttpStatus.BAD_REQUEST.value(), e.getMessage()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截校验异常-违反约束异常 | ||||
|      */ | ||||
|     @ExceptionHandler(ConstraintViolationException.class) | ||||
|     public R constraintViolationException(ConstraintViolationException e, HttpServletRequest request) { | ||||
|         log.warn("请求地址 [{}],参数验证失败。", request.getRequestURI(), e); | ||||
|         String errorMsg = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ","); | ||||
|         LogContextHolder.setErrorMsg(errorMsg); | ||||
|         return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截校验异常-绑定异常 | ||||
|      */ | ||||
|     @ExceptionHandler(BindException.class) | ||||
|     public R handleBindException(BindException e, HttpServletRequest request) { | ||||
|         log.warn("请求地址 [{}],参数验证失败。", request.getRequestURI(), e); | ||||
|         String errorMsg = StreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ","); | ||||
|         LogContextHolder.setErrorMsg(errorMsg); | ||||
|         return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截校验异常-方法参数无效异常 | ||||
|      */ | ||||
|     @ExceptionHandler(MethodArgumentNotValidException.class) | ||||
|     public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) { | ||||
|         log.warn("请求地址 [{}],参数验证失败。", request.getRequestURI(), e); | ||||
|         String errorMsg = ExceptionUtils | ||||
|             .exToNull(() -> Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage()); | ||||
|         LogContextHolder.setErrorMsg(errorMsg); | ||||
|         return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截校验异常-方法参数类型不匹配异常 | ||||
|      */ | ||||
|     @ExceptionHandler(MethodArgumentTypeMismatchException.class) | ||||
|     public R handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, | ||||
|         HttpServletRequest request) { | ||||
|         String errorMsg = StrUtil.format("参数名:[{}],期望参数类型:[{}]", e.getName(), e.getParameter().getParameterType()); | ||||
|         log.warn("请求地址 [{}],参数转换失败,{}。", request.getRequestURI(), errorMsg, e); | ||||
|         LogContextHolder.setErrorMsg(errorMsg); | ||||
|         return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截文件上传异常-超过上传大小限制 | ||||
|      */ | ||||
|     @ExceptionHandler(MaxUploadSizeExceededException.class) | ||||
|     public R handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e, HttpServletRequest request) { | ||||
|         log.warn("请求地址 [{}],上传文件失败,文件大小超过限制。", request.getRequestURI(), e); | ||||
|         String sizeLimit = StrUtil.subBetween(e.getMessage(), "The maximum size ", " for"); | ||||
|         String errorMsg = String.format("请上传小于 %sMB 的文件", NumberUtil.parseLong(sizeLimit) / 1024 / 1024); | ||||
|         LogContextHolder.setErrorMsg(errorMsg); | ||||
|         return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 认证异常-登录认证 | ||||
|      */ | ||||
|     @ExceptionHandler(NotLoginException.class) | ||||
|     public R handleNotLoginException(NotLoginException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],认证失败,无法访问系统资源。", request.getRequestURI(), e); | ||||
|         String errorMsg = switch (e.getType()) { | ||||
|             case NotLoginException.KICK_OUT -> "您已被踢下线。"; | ||||
|             case NotLoginException.BE_REPLACED_MESSAGE -> "您已被顶下线。"; | ||||
|             default -> "您的登录状态已过期,请重新登录。"; | ||||
|         }; | ||||
|         LogContextHolder.setErrorMsg(errorMsg); | ||||
|         return R.fail(HttpStatus.UNAUTHORIZED.value(), errorMsg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 认证异常-权限认证 | ||||
|      */ | ||||
|     @ExceptionHandler(NotPermissionException.class) | ||||
|     public R handleNotPermissionException(NotPermissionException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],权限码校验失败。", request.getRequestURI(), e); | ||||
|         return R.fail(HttpStatus.FORBIDDEN.value(), "没有访问权限,请联系管理员授权"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 认证异常-角色认证 | ||||
|      */ | ||||
|     @ExceptionHandler(NotRoleException.class) | ||||
|     public R handleNotRoleException(NotRoleException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],角色权限校验失败。", request.getRequestURI(), e); | ||||
|         return R.fail(HttpStatus.FORBIDDEN.value(), "没有访问权限,请联系管理员授权"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截校验异常-请求方式不支持异常 | ||||
|      */ | ||||
|     @ExceptionHandler(HttpRequestMethodNotSupportedException.class) | ||||
|     public R handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, HttpServletRequest request) { | ||||
|         LogContextHolder.setErrorMsg(e.getMessage()); | ||||
|         log.error("请求地址 [{}],不支持 [{}] 请求。", request.getRequestURI(), e.getMethod()); | ||||
|         return R.fail(HttpStatus.METHOD_NOT_ALLOWED.value(), e.getMessage()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截业务异常 | ||||
|      */ | ||||
|     @ExceptionHandler(BusinessException.class) | ||||
|     public R handleServiceException(BusinessException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],发生业务异常。", request.getRequestURI(), e); | ||||
|         LogContextHolder.setErrorMsg(e.getMessage()); | ||||
|         return R.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截未知的运行时异常 | ||||
|      */ | ||||
|     @ExceptionHandler(RuntimeException.class) | ||||
|     public R handleRuntimeException(RuntimeException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],发生系统异常。", request.getRequestURI(), e); | ||||
|         LogContextHolder.setException(e); | ||||
|         return R.fail(e.getMessage()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 拦截未知的系统异常 | ||||
|      */ | ||||
|     @ExceptionHandler(Throwable.class) | ||||
|     public R handleException(Throwable e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],发生未知异常。", request.getRequestURI(), e); | ||||
|         LogContextHolder.setException(e); | ||||
|         return R.fail(e.getMessage()); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,56 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.model.dto; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| /** | ||||
|  * 系统日志上下文 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/25 8:59 | ||||
|  */ | ||||
| @Data | ||||
| public class LogContext implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 创建人 | ||||
|      */ | ||||
|     private Long createUser; | ||||
|  | ||||
|     /** | ||||
|      * 创建时间 | ||||
|      */ | ||||
|     private LocalDateTime createTime; | ||||
|  | ||||
|     /** | ||||
|      * 错误信息 | ||||
|      */ | ||||
|     private String errorMsg; | ||||
|  | ||||
|     /** | ||||
|      * 异常信息 | ||||
|      */ | ||||
|     private Throwable exception; | ||||
| } | ||||
| @@ -0,0 +1,108 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.model.dto; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
| import java.time.LocalDateTime; | ||||
| import java.util.Set; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.constant.SysConstants; | ||||
|  | ||||
| /** | ||||
|  * 登录用户信息 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/24 13:01 | ||||
|  */ | ||||
| @Data | ||||
| public class LoginUser implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * ID | ||||
|      */ | ||||
|     private Long id; | ||||
|  | ||||
|     /** | ||||
|      * 用户名 | ||||
|      */ | ||||
|     private String username; | ||||
|  | ||||
|     /** | ||||
|      * 部门 ID | ||||
|      */ | ||||
|     private Long deptId; | ||||
|  | ||||
|     /** | ||||
|      * 权限码集合 | ||||
|      */ | ||||
|     private Set<String> permissions; | ||||
|  | ||||
|     /** | ||||
|      * 角色编码集合 | ||||
|      */ | ||||
|     private Set<String> roleCodes; | ||||
|  | ||||
|     /** | ||||
|      * 角色集合 | ||||
|      */ | ||||
|     private Set<RoleDTO> roles; | ||||
|  | ||||
|     /** | ||||
|      * 令牌 | ||||
|      */ | ||||
|     private String token; | ||||
|  | ||||
|     /** | ||||
|      * 登录 IP | ||||
|      */ | ||||
|     private String clientIp; | ||||
|  | ||||
|     /** | ||||
|      * 登录地点 | ||||
|      */ | ||||
|     private String location; | ||||
|  | ||||
|     /** | ||||
|      * 浏览器 | ||||
|      */ | ||||
|     private String browser; | ||||
|  | ||||
|     /** | ||||
|      * 登录时间 | ||||
|      */ | ||||
|     private LocalDateTime loginTime; | ||||
|  | ||||
|     /** | ||||
|      * 是否为管理员 | ||||
|      * | ||||
|      * @return true:是;false:否 | ||||
|      */ | ||||
|     public boolean isAdmin() { | ||||
|         if (CollUtil.isEmpty(roleCodes)) { | ||||
|             return false; | ||||
|         } | ||||
|         return roleCodes.contains(SysConstants.ADMIN_ROLE_CODE); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,52 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.model.dto; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.enums.DataScopeEnum; | ||||
|  | ||||
| /** | ||||
|  * 角色信息 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/3/7 22:08 | ||||
|  */ | ||||
| @Data | ||||
| public class RoleDTO implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * ID | ||||
|      */ | ||||
|     private Long id; | ||||
|  | ||||
|     /** | ||||
|      * 角色编码 | ||||
|      */ | ||||
|     private String code; | ||||
|  | ||||
|     /** | ||||
|      * 数据权限 | ||||
|      */ | ||||
|     private DataScopeEnum dataScope; | ||||
| } | ||||
| @@ -0,0 +1,49 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.model.req; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| import jakarta.validation.constraints.NotNull; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.enums.DisEnableStatusEnum; | ||||
|  | ||||
| /** | ||||
|  * 修改状态信息 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/1/24 19:51 | ||||
|  */ | ||||
| @Data | ||||
| @Schema(description = "修改状态信息") | ||||
| public class UpdateStatusReq implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 状态 | ||||
|      */ | ||||
|     @Schema(description = "状态(1:启用;2:禁用)", type = "Integer", allowableValues = {"1", "2"}, example = "1") | ||||
|     @NotNull(message = "状态非法") | ||||
|     private DisEnableStatusEnum status; | ||||
| } | ||||
| @@ -0,0 +1,53 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.model.resp; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| /** | ||||
|  * 验证码信息 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/11 13:55 | ||||
|  */ | ||||
| @Data | ||||
| @Builder | ||||
| @Schema(description = "验证码信息") | ||||
| public class CaptchaResp implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 验证码标识 | ||||
|      */ | ||||
|     @Schema(description = "验证码标识", example = "090b9a2c-1691-4fca-99db-e4ed0cff362f") | ||||
|     private String uuid; | ||||
|  | ||||
|     /** | ||||
|      * 验证码图片(Base64编码,带图片格式:data:image/gif;base64) | ||||
|      */ | ||||
|     @Schema(description = "验证码图片(Base64编码,带图片格式:data:image/gif;base64)", | ||||
|         example = "data:image/png;base64,iVBORw0KGgoAAAAN...") | ||||
|     private String img; | ||||
| } | ||||
| @@ -0,0 +1,73 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.model.resp; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||
|  | ||||
| /** | ||||
|  * 键值对信息 | ||||
|  * | ||||
|  * @param <T> | ||||
|  * @author Charles7c | ||||
|  * @since 2023/2/24 22:02 | ||||
|  */ | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @Schema(description = "键值对信息") | ||||
| public class LabelValueResp<T> implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 标签 | ||||
|      */ | ||||
|     @Schema(description = "标签", example = "男") | ||||
|     private String label; | ||||
|  | ||||
|     /** | ||||
|      * 值 | ||||
|      */ | ||||
|     @Schema(description = "值", example = "1") | ||||
|     private T value; | ||||
|  | ||||
|     /** | ||||
|      * 颜色 | ||||
|      */ | ||||
|     @Schema(description = "颜色", example = "#165DFF") | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     private String color; | ||||
|  | ||||
|     public LabelValueResp(String label, T value) { | ||||
|         this.label = label; | ||||
|         this.value = value; | ||||
|     } | ||||
|  | ||||
|     public LabelValueResp(String label, T value, String color) { | ||||
|         this.label = label; | ||||
|         this.value = value; | ||||
|         this.color = color; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,84 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.util; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
|  | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
|  | ||||
| import cn.hutool.core.date.DatePattern; | ||||
| import cn.hutool.core.date.DateUtil; | ||||
| import cn.hutool.core.io.file.FileNameUtil; | ||||
| import cn.hutool.core.util.IdUtil; | ||||
|  | ||||
| /** | ||||
|  * 文件工具类 | ||||
|  * | ||||
|  * @author Zheng Jie(ELADMIN) | ||||
|  * @author Charles7c | ||||
|  * @since 2023/1/2 21:34 | ||||
|  */ | ||||
| @Slf4j | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class FileUtils { | ||||
|  | ||||
|     /** | ||||
|      * 上传文件 | ||||
|      * | ||||
|      * @param multipartFile | ||||
|      *            源文件对象 | ||||
|      * @param filePath | ||||
|      *            文件路径 | ||||
|      * @param isKeepOriginalFilename | ||||
|      *            是否保留原文件名 | ||||
|      * @return 目标文件对象 | ||||
|      */ | ||||
|     public static File upload(MultipartFile multipartFile, String filePath, boolean isKeepOriginalFilename) { | ||||
|         String originalFilename = multipartFile.getOriginalFilename(); | ||||
|         String extensionName = FileNameUtil.extName(originalFilename); | ||||
|  | ||||
|         String fileName; | ||||
|         if (isKeepOriginalFilename) { | ||||
|             fileName = String.format("%s-%s.%s", FileNameUtil.getPrefix(originalFilename), | ||||
|                 DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_MS_PATTERN), extensionName); | ||||
|         } else { | ||||
|             fileName = String.format("%s.%s", IdUtil.fastSimpleUUID(), extensionName); | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             String pathname = filePath + fileName; | ||||
|             File dest = new File(pathname).getCanonicalFile(); | ||||
|             // 如果父路径不存在,自动创建 | ||||
|             if (!dest.getParentFile().exists()) { | ||||
|                 if (!dest.getParentFile().mkdirs()) { | ||||
|                     log.error("Create upload file parent path failed."); | ||||
|                 } | ||||
|             } | ||||
|             // 文件写入 | ||||
|             multipartFile.transferTo(dest); | ||||
|             return dest; | ||||
|         } catch (Exception e) { | ||||
|             log.error("Upload file occurred an error: {}. fileName: {}.", e.getMessage(), fileName, e); | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,89 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.util; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import cn.hutool.core.codec.Base64; | ||||
| import cn.hutool.crypto.SecureUtil; | ||||
| import cn.hutool.crypto.asymmetric.KeyType; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.config.properties.RsaProperties; | ||||
| import top.charles7c.continew.starter.extension.crud.util.validate.ValidationUtils; | ||||
|  | ||||
| /** | ||||
|  * 加密/解密工具类 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/21 21:41 | ||||
|  */ | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class SecureUtils { | ||||
|  | ||||
|     /** | ||||
|      * 公钥加密 | ||||
|      * | ||||
|      * @param data | ||||
|      *            要加密的内容 | ||||
|      * @param publicKey | ||||
|      *            公钥 | ||||
|      * @return 公钥加密并 Base64 加密后的内容 | ||||
|      */ | ||||
|     public static String encryptByRsaPublicKey(String data, String publicKey) { | ||||
|         return Base64.encode(SecureUtil.rsa(null, publicKey).encrypt(data, KeyType.PublicKey)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 私钥解密 | ||||
|      * | ||||
|      * @param data | ||||
|      *            要解密的内容(Base64 加密过) | ||||
|      * @return 解密后的内容 | ||||
|      */ | ||||
|     public static String decryptByRsaPrivateKey(String data) { | ||||
|         String privateKey = RsaProperties.PRIVATE_KEY; | ||||
|         ValidationUtils.throwIfBlank(privateKey, "请配置 RSA 私钥"); | ||||
|         return decryptByRsaPrivateKey(data, privateKey); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 私钥解密 | ||||
|      * | ||||
|      * @param data | ||||
|      *            要解密的内容(Base64 加密过) | ||||
|      * @param privateKey | ||||
|      *            私钥 | ||||
|      * @return 解密后的内容 | ||||
|      */ | ||||
|     public static String decryptByRsaPrivateKey(String data, String privateKey) { | ||||
|         return new String(SecureUtil.rsa(privateKey, null).decrypt(Base64.decode(data), KeyType.PrivateKey)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * MD5 加密 | ||||
|      * | ||||
|      * @param data | ||||
|      *            要加密的内容 | ||||
|      * @param salt | ||||
|      *            盐 | ||||
|      * @return 加密后的内容 | ||||
|      */ | ||||
|     public static String md5Salt(String data, String salt) { | ||||
|         return SecureUtil.md5(SecureUtil.md5(data) + salt); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,78 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.util; | ||||
|  | ||||
| import java.util.*; | ||||
|  | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
| import jakarta.servlet.http.HttpServletResponse; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import org.springframework.web.context.request.RequestContextHolder; | ||||
| import org.springframework.web.context.request.ServletRequestAttributes; | ||||
|  | ||||
| import cn.hutool.http.useragent.UserAgent; | ||||
| import cn.hutool.http.useragent.UserAgentUtil; | ||||
|  | ||||
| /** | ||||
|  * Servlet 工具类 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/23 20:00 | ||||
|  */ | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class ServletUtils { | ||||
|  | ||||
|     /** | ||||
|      * 获取请求对象 | ||||
|      * | ||||
|      * @return / | ||||
|      */ | ||||
|     public static HttpServletRequest getRequest() { | ||||
|         return getServletRequestAttributes().getRequest(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取响应对象 | ||||
|      * | ||||
|      * @return / | ||||
|      */ | ||||
|     public static HttpServletResponse getResponse() { | ||||
|         return getServletRequestAttributes().getResponse(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取浏览器及其版本信息 | ||||
|      * | ||||
|      * @param request | ||||
|      *            请求对象 | ||||
|      * @return 浏览器及其版本信息 | ||||
|      */ | ||||
|     public static String getBrowser(HttpServletRequest request) { | ||||
|         if (null == request) { | ||||
|             return null; | ||||
|         } | ||||
|         UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); | ||||
|         return userAgent.getBrowser().getName() + " " + userAgent.getVersion(); | ||||
|     } | ||||
|  | ||||
|     private static ServletRequestAttributes getServletRequestAttributes() { | ||||
|         return (ServletRequestAttributes)Objects.requireNonNull(RequestContextHolder.getRequestAttributes()); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,60 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.util; | ||||
|  | ||||
| import java.util.Collection; | ||||
| import java.util.Objects; | ||||
| import java.util.function.Function; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
|  | ||||
| import top.charles7c.continew.starter.core.constant.StringConstants; | ||||
|  | ||||
| /** | ||||
|  * Stream 工具类 | ||||
|  * | ||||
|  * @author Lion Li(<a href="https://gitee.com/dromara/RuoYi-Vue-Plus">RuoYi-Vue-Plus</a>) | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/22 19:51 | ||||
|  */ | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class StreamUtils { | ||||
|  | ||||
|     /** | ||||
|      * 将集合中的指定字段使用分隔符拼接成字符串 | ||||
|      * | ||||
|      * @param collection | ||||
|      *            集合 | ||||
|      * @param function | ||||
|      *            字段方法 | ||||
|      * @param delimiter | ||||
|      *            分隔符 | ||||
|      * @param <E> | ||||
|      *            / | ||||
|      * @return 拼接结果 | ||||
|      */ | ||||
|     public static <E> String join(Collection<E> collection, Function<E, String> function, CharSequence delimiter) { | ||||
|         if (CollUtil.isEmpty(collection)) { | ||||
|             return StringConstants.EMPTY; | ||||
|         } | ||||
|         return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter)); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,53 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.util; | ||||
|  | ||||
| import java.util.Map; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import cn.hutool.extra.template.Template; | ||||
| import cn.hutool.extra.template.TemplateConfig; | ||||
| import cn.hutool.extra.template.TemplateEngine; | ||||
| import cn.hutool.extra.template.TemplateUtil; | ||||
|  | ||||
| /** | ||||
|  * 模板工具类 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/1/13 20:37 | ||||
|  */ | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class TemplateUtils { | ||||
|  | ||||
|     private static final String TEMPLATE_PARENT_PATH = "templates"; | ||||
|  | ||||
|     /** | ||||
|      * 将模板与绑定参数融合后返回为字符串 | ||||
|      * | ||||
|      * @param bindingMap | ||||
|      *            绑定的参数,此Map中的参数会替换模板中的变量 | ||||
|      * @return 融合后的内容 | ||||
|      */ | ||||
|     public static String render(String templatePath, Map<?, ?> bindingMap) { | ||||
|         TemplateEngine engine = | ||||
|             TemplateUtil.createEngine(new TemplateConfig(TEMPLATE_PARENT_PATH, TemplateConfig.ResourceMode.CLASSPATH)); | ||||
|         Template template = engine.getTemplate(templatePath); | ||||
|         return template.render(bindingMap); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,43 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.util; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import cn.hutool.http.HttpUtil; | ||||
|  | ||||
| /** | ||||
|  * URL(Uniform Resource Locator)统一资源定位符相关工具类 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/3/20 21:27 | ||||
|  */ | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class URLUtils { | ||||
|  | ||||
|     /** | ||||
|      * 提供的 URL 是否为 HTTP URL(协议包括:"http","https") | ||||
|      * | ||||
|      * @param url | ||||
|      *            URL | ||||
|      * @return 是否为 HTTP URL | ||||
|      */ | ||||
|     public static boolean isHttpUrl(String url) { | ||||
|         return HttpUtil.isHttp(url) || HttpUtil.isHttps(url); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,146 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.util.helper; | ||||
|  | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import cn.dev33.satoken.context.SaHolder; | ||||
| import cn.dev33.satoken.session.SaSession; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.extra.servlet.JakartaServletUtil; | ||||
| import cn.hutool.extra.spring.SpringUtil; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.constant.CacheConstants; | ||||
| import top.charles7c.continew.admin.common.model.dto.LogContext; | ||||
| import top.charles7c.continew.admin.common.model.dto.LoginUser; | ||||
| import top.charles7c.continew.admin.common.util.ServletUtils; | ||||
| import top.charles7c.continew.admin.common.util.holder.LogContextHolder; | ||||
| import top.charles7c.continew.starter.core.util.ExceptionUtils; | ||||
| import top.charles7c.continew.starter.core.util.IpUtils; | ||||
| import top.charles7c.continew.starter.extension.crud.base.CommonUserService; | ||||
|  | ||||
| /** | ||||
|  * 登录助手 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @author Lion Li(<a href="https://gitee.com/dromara/RuoYi-Vue-Plus">RuoYi-Vue-Plus</a>) | ||||
|  * @since 2022/12/24 12:58 | ||||
|  */ | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class LoginHelper { | ||||
|  | ||||
|     /** | ||||
|      * 用户登录并缓存用户信息 | ||||
|      * | ||||
|      * @param loginUser | ||||
|      *            登录用户信息 | ||||
|      * @return 令牌 | ||||
|      */ | ||||
|     public static String login(LoginUser loginUser) { | ||||
|         // 记录登录信息 | ||||
|         HttpServletRequest request = ServletUtils.getRequest(); | ||||
|         loginUser.setClientIp(JakartaServletUtil.getClientIP(request)); | ||||
|         loginUser.setLocation(IpUtils.getCityInfo(loginUser.getClientIp())); | ||||
|         loginUser.setBrowser(ServletUtils.getBrowser(request)); | ||||
|         LogContext logContext = LogContextHolder.get(); | ||||
|         loginUser.setLoginTime(null != logContext ? logContext.getCreateTime() : LocalDateTime.now()); | ||||
|         // 登录并缓存用户信息 | ||||
|         StpUtil.login(loginUser.getId()); | ||||
|         SaHolder.getStorage().set(CacheConstants.LOGIN_USER_KEY, loginUser); | ||||
|         String tokenValue = StpUtil.getTokenValue(); | ||||
|         loginUser.setToken(tokenValue); | ||||
|         StpUtil.getTokenSession().set(CacheConstants.LOGIN_USER_KEY, loginUser); | ||||
|         return tokenValue; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取登录用户信息 | ||||
|      * | ||||
|      * @return 登录用户信息(获取 TokenSession 时如未登录,会抛出异常) | ||||
|      */ | ||||
|     public static LoginUser getLoginUser() { | ||||
|         LoginUser loginUser = (LoginUser)SaHolder.getStorage().get(CacheConstants.LOGIN_USER_KEY); | ||||
|         if (null != loginUser) { | ||||
|             return loginUser; | ||||
|         } | ||||
|         SaSession tokenSession = StpUtil.getTokenSession(); | ||||
|         if (null == tokenSession) { | ||||
|             return null; | ||||
|         } | ||||
|         loginUser = (LoginUser)tokenSession.get(CacheConstants.LOGIN_USER_KEY); | ||||
|         SaHolder.getStorage().set(CacheConstants.LOGIN_USER_KEY, loginUser); | ||||
|         return loginUser; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 根据 Token 获取登录用户信息 | ||||
|      * | ||||
|      * @param token | ||||
|      *            用户 Token | ||||
|      * @return 登录用户信息 | ||||
|      */ | ||||
|     public static LoginUser getLoginUser(String token) { | ||||
|         SaSession tokenSession = StpUtil.getTokenSessionByToken(token); | ||||
|         if (null == tokenSession) { | ||||
|             return null; | ||||
|         } | ||||
|         return (LoginUser)tokenSession.get(CacheConstants.LOGIN_USER_KEY); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取登录用户 ID | ||||
|      * | ||||
|      * @return 登录用户 ID | ||||
|      */ | ||||
|     public static Long getUserId() { | ||||
|         return ExceptionUtils.exToNull(() -> getLoginUser().getId()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取登录用户名 | ||||
|      * | ||||
|      * @return 登录用户名 | ||||
|      */ | ||||
|     public static String getUsername() { | ||||
|         return ExceptionUtils.exToNull(() -> getLoginUser().getUsername()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取登录用户昵称 | ||||
|      * | ||||
|      * @return 登录用户昵称 | ||||
|      */ | ||||
|     public static String getNickname() { | ||||
|         return getNickname(getUserId()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取登录用户昵称 | ||||
|      * | ||||
|      * @param userId | ||||
|      *            登录用户 ID | ||||
|      * @return 登录用户昵称 | ||||
|      */ | ||||
|     public static String getNickname(Long userId) { | ||||
|         return ExceptionUtils.exToNull(() -> SpringUtil.getBean(CommonUserService.class).getNicknameById(userId)); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,87 @@ | ||||
| /* | ||||
|  * 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.continew.admin.common.util.holder; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.model.dto.LogContext; | ||||
|  | ||||
| /** | ||||
|  * 系统日志上下文持有者 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/25 8:55 | ||||
|  */ | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class LogContextHolder { | ||||
|  | ||||
|     private static final ThreadLocal<LogContext> LOG_THREAD_LOCAL = new ThreadLocal<>(); | ||||
|  | ||||
|     /** | ||||
|      * 存储系统日志上下文 | ||||
|      * | ||||
|      * @param logContext | ||||
|      *            系统日志上下文信息 | ||||
|      */ | ||||
|     public static void set(LogContext logContext) { | ||||
|         LOG_THREAD_LOCAL.set(logContext); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取系统日志上下文 | ||||
|      * | ||||
|      * @return 系统日志上下文信息 | ||||
|      */ | ||||
|     public static LogContext get() { | ||||
|         return LOG_THREAD_LOCAL.get(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 移除系统日志上下文 | ||||
|      */ | ||||
|     public static void remove() { | ||||
|         LOG_THREAD_LOCAL.remove(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 在系统日志上下文中保存异常信息 | ||||
|      * | ||||
|      * @param e | ||||
|      *            异常信息 | ||||
|      */ | ||||
|     public static void setException(Throwable e) { | ||||
|         LogContext logContext = get(); | ||||
|         if (null != logContext) { | ||||
|             logContext.setErrorMsg(e.getMessage()); | ||||
|             logContext.setException(e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 在系统日志上下文中保存错误信息(非未知异常不记录异常信息,只记录错误信息) | ||||
|      * | ||||
|      * @param errorMsg | ||||
|      *            错误信息 | ||||
|      */ | ||||
|     public static void setErrorMsg(String errorMsg) { | ||||
|         LogContext logContext = get(); | ||||
|         if (null != logContext) { | ||||
|             logContext.setErrorMsg(errorMsg); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user