commit c0c8734e140e916e276b2f5a3f863665c039093a Author: wangxiang <1827945911@qq.com> Date: Sat Sep 16 16:20:12 2023 +0800 chore: withdrawal tool diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6e25c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,60 @@ +### gradle ### +.gradle +!gradle/wrapper/gradle-wrapper.jar + +### STS ### +.settings/ +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +rebel.xml + +### NetBeans ### +nbproject/private/ +nbbuild/ +nbdist/ +.nb-gradle/ + +### maven ### +target/ +*.war +*.ear +*.zip +*.tar +*.tar.gz +*.versionsBackup + +### vscode ### +.vscode + +### logs ### +/logs/ +*.log + +### temp ignore ### +*.cache +*.diff +*.patch +*.tmp +*.java~ +*.properties~ +*.xml~ + +### system ignore ### +.DS_Store +Thumbs.db +Servers +.metadata + +# ui ignore +kicc-ui/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..2f611fd --- /dev/null +++ b/README.md @@ -0,0 +1,107 @@ +
一个快速开发软件的平台
+ ++ Website • + Documentation +
+ + + +## 🐬 介绍 +海豚生态计划-打造一个web端,安卓端,ios端的一个海豚开发平台生态圈,不接收任何商业化,并且完全免费开源(包含高级功能)。 + +## 💪 愿景 +让人人都可以快速高效的开发软件 + +## ✨ 特性 +- 主体框架:采用最新的`Spring Cloud 2021.0.1`, `Spring Boot 2.6.4`, `Spring Cloud Alibaba 2021.1`版本进行系统设计。 +- 统一注册:支持`Nacos`作为注册中心,实现多配置、分群组、分命名空间、多业务模块的注册和发现功能。 +- 统一认证:统一`Oauth2`认证协议,并支持自定义grant_type实现手机号码登录,第三方登录集成JustAuth实现微信、支付宝等多种登录模式。 +- 业务监控:利用`Spring Boot Admin`来监控各个独立微服务运行状态。 +- 内部调用:集成了`Feign`与自定义内部注解,支持内部调用。 +- 业务熔断:采用`Sentinel`实现业务熔断处理,避免服务之间出现雪崩。 +- 在线文档:通过接入`Knife4j`,实现在线API文档的查看与调试。 +- 业务分离:采用前后端分离的框架设计,提高开发效率、降低维护成本、增强系统稳定性和灵活性。 +- 多租户功能:集成`Mybatis Plus`,自定义sql执行拦截器,实现SAAS多租户。 +- 消息中间件:采用`RocketMQ`,实现服务之间消息转发。 +- 分布式事物方案:采用`seata`,实现多个微服务分布式事物一致。 +- 分布式定时器:采用`XXL-JOB`,实现多个微服务分布式任务调度。 +- 微服务网关:采用`Spring Gateway`实现流量配置动态化、API管理和路由、负载均衡和容错、解决跨域问题、鉴权,限流,熔断,防火墙等等。 + +##+ * 激活 Jackson 自动转换配置 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/19 + */ +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Import({ JacksonAutoConfiguration.class }) +public @interface EnableKiccJacksonAutoConvert { +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/api/R.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/api/R.java new file mode 100644 index 0000000..a7eb065 --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/api/R.java @@ -0,0 +1,114 @@ +package com.cloud.kicc.common.core.api; + +import io.swagger.annotations.ApiModelProperty; +import lombok.*; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + *+ * 响应信息主体 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +@ToString +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class R+ * 网关配置文件 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/17 + */ +@Data +@RefreshScope +@ConfigurationProperties("gateway") +public class GatewayConfigProperties { + + /** + * 网关解密登录前端密码 + */ + private String encodeKey; + + /** + * 网关忽略不需要校验验证码是否合法的客户端 + */ + private List+ * 配置全局JacksonConfig,影响mvc层的对象传输日期格式 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(ObjectMapper.class) +@AutoConfigureBefore(org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.class) +public class JacksonAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public Jackson2ObjectMapperBuilderCustomizer customizer() { + return builder -> { + builder.locale(Locale.CHINA); + builder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault())); + // 针对于Date类型,文本格式化,已经实现前端返回时间戳 + builder.simpleDateFormat(DatePattern.NORM_DATETIME_PATTERN); + // 解决返回给前端的Long类型数据失去精度,将Long转换为String + builder.serializerByType(Long.class, ToStringSerializer.instance); + // 针对于JDK新时间类,序列化时带有T的问题,自定义格式化字符串 + builder.modules(new KiccJavaTimeModule()); + }; + } + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/config/RestTemplateConfiguration.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/config/RestTemplateConfiguration.java new file mode 100644 index 0000000..bd08383 --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/config/RestTemplateConfiguration.java @@ -0,0 +1,23 @@ +package com.cloud.kicc.common.core.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +/** + *+ * Rest 配置 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +@Configuration(proxyBeanMethods = false) +public class RestTemplateConfiguration { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/config/WebMvcConfiguration.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/config/WebMvcConfiguration.java new file mode 100644 index 0000000..d3658ea --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/config/WebMvcConfiguration.java @@ -0,0 +1,45 @@ +package com.cloud.kicc.common.core.config; + +import cn.hutool.core.date.DatePattern; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.time.format.DateTimeFormatter; + +import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type.SERVLET; + +/** + *+ * 全局DateTimeFormat + * 针对GET请求传入参数转换 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnWebApplication(type = SERVLET) +public class WebMvcConfiguration implements WebMvcConfigurer { + + /** + * 增加GET请求参数中时间类型转换 {@link com.cloud.kicc.common.core.jackson.KiccJavaTimeModule} + *+ * 应用前缀 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/24 + */ +public interface AppConstants { + + String APP_SYSTEM = "/system"; + + String APP_MONITOR = "/monitor"; + + String APP_COMMON = "/common"; + + String APP_WORKFLOW = "/workflow"; + + String APP_REPORT = "/report"; + + String APP_BIGSCREEN = "/bigscreen"; + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/CacheConstants.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/CacheConstants.java new file mode 100644 index 0000000..7c4b6c0 --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/CacheConstants.java @@ -0,0 +1,53 @@ +package com.cloud.kicc.common.core.constant; + +/** + *+ * 缓存的key 常量 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +public interface CacheConstants { + + /** + * oauth 缓存前缀 + */ + String OAUTH_ACCESS = ServiceNameConstants.AUTH_SERVICE + ":access:"; + + /** + * oauth 缓存令牌前缀 + */ + String OAUTH_TOKEN = ServiceNameConstants.AUTH_SERVICE + ":token:"; + + /** + * 验证码前缀 + */ + String VERIFICATION_CODE = ServiceNameConstants.SYSTEM_SERVICE + ":verification_code:"; + + /** + * oauth 客户端信息缓存 + */ + String OAUTH_CLIENT_DETAILS = ServiceNameConstants.AUTH_SERVICE + ":client_details"; + + /** + * 菜单信息缓存 + */ + String MENU_DETAILS = ServiceNameConstants.AUTH_SERVICE + ":menu_details"; + + /** + * 用户信息缓存 + */ + String USER_DETAILS = ServiceNameConstants.AUTH_SERVICE + ":user_details"; + + /** + * 字典信息缓存 + */ + String DICT_DETAILS = ServiceNameConstants.AUTH_SERVICE + ":dict_details"; + + /** + * 全局配置缓存 + */ + String CONFIG_PARAM = ServiceNameConstants.AUTH_SERVICE + ":config_param"; + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/CommonConstants.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/CommonConstants.java new file mode 100644 index 0000000..a913f03 --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/CommonConstants.java @@ -0,0 +1,53 @@ +package com.cloud.kicc.common.core.constant; + +/** + *+ * 通用常量 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +public interface CommonConstants { + + /** + * 正常 + */ + String STATUS_NORMAL = "0"; + + /** + * 锁定 + */ + String STATUS_LOCK = "9"; + + /** + * 编码 + */ + String UTF8 = "UTF-8"; + + /** + * JSON 资源 + */ + String CONTENT_TYPE = "application/json; charset=utf-8"; + + /** + * 前端工程名 + */ + String FRONT_END_PROJECT = "kicc-ui"; + + /** + * 后端工程名 + */ + String BACK_END_PROJECT = "kicc"; + + /** + * 当前页 + */ + String CURRENT = "current"; + + /** + * size + */ + String SIZE = "size"; + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/RegexConstants.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/RegexConstants.java new file mode 100644 index 0000000..99110cf --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/RegexConstants.java @@ -0,0 +1,18 @@ +package com.cloud.kicc.common.core.constant; + +/** + *+ * 正则表达式常量 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/4/18 + */ +public interface RegexConstants { + + /** + * 匹配网址正则表达式 + */ + String MATCHER_URL = "(((^https?:(?:\\/\\/)?)(?:[-;:&=\\+\\$,\\w]+@)?[A-Za-z0-9.-]+(?::\\d+)?|(?:www.|[-;:&=\\+\\$,\\w]+@)[A-Za-z0-9.-]+)((?:\\/[\\+~%\\/.\\w-_]*)?\\??(?:[-\\+=&;%@.\\w_{\\s\\S}]*)#?(?:[\\w]*))?)$"; + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/SecurityConstants.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/SecurityConstants.java new file mode 100644 index 0000000..4cf5acb --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/SecurityConstants.java @@ -0,0 +1,125 @@ +package com.cloud.kicc.common.core.constant; + +/** + *+ * 安全常量 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +public interface SecurityConstants { + + /** + * 角色前缀 + */ + String ROLE = "ROLE_"; + + /** + * 项目的license + */ + String PROJECT_LICENSE = "长沙康来生物有限公司"; + + /** + * 内部接口调用密钥 + */ + String FROM_IN = "kG8qA6qG1aP5aR3g"; + + /** + * 内部接口调用Key标志 + */ + String FROM = "from"; + + /** + * 请求header + */ + String HEADER_FROM_IN = FROM + "=" + FROM_IN; + + /** + * 默认登录URL + */ + String OAUTH_TOKEN_URL = "/oauth/token"; + + /** + * grant_type + */ + String REFRESH_TOKEN = "refresh_token"; + + /** + * 手机号登录 + */ + String APP = "app"; + + /** + * {bcrypt} 加密的特征码 + */ + String BCRYPT = "{bcrypt}"; + + /** + * sys_oauth_client_details 表的字段,不包括client_id、client_secret + */ + String CLIENT_FIELDS = "client_id, CONCAT('{noop}',client_secret) as client_secret, resource_ids, scope, " + + "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, " + + "refresh_token_validity, additional_information, autoapprove"; + + /** + * JdbcClientDetailsService 查询语句 + */ + String BASE_FIND_STATEMENT = "select " + CLIENT_FIELDS + " from sys_oauth_client_details"; + + /** + * 默认的查询语句 + */ + String DEFAULT_FIND_STATEMENT = BASE_FIND_STATEMENT + " order by client_id"; + + /** + * 按条件client_id 查询 + */ + String DEFAULT_SELECT_STATEMENT = BASE_FIND_STATEMENT + " where client_id = ?"; + + /*** + * 资源服务器默认bean名称 + */ + String RESOURCE_SERVER_CONFIGURER = "resourceServerConfigurerAdapter"; + + /** + * 用户信息 + */ + String DETAILS_USER = "user_info"; + + /** + * 协议字段 + */ + String DETAILS_LICENSE = "license"; + + /** + * 验证码有效期,默认 60秒 + */ + long CODE_TIME = 60; + + /** + * 手机验证码长度 + */ + String PHONE_CODE_SIZE = "6"; + + /** + * 客户端模式 + */ + String CLIENT_CREDENTIALS = "client_credentials"; + + /** + * 客户端ID + */ + String CLIENT_ID = "clientId"; + + /** + * 模拟测试账户 + */ + String MOCK_USERNAME = "admin"; + + /** + * 模拟测试密码 + */ + String MOCK_PASSWORD = "kanglai123"; + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/ServiceNameConstants.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/ServiceNameConstants.java new file mode 100644 index 0000000..754cfcc --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/ServiceNameConstants.java @@ -0,0 +1,44 @@ +package com.cloud.kicc.common.core.constant; + +/** + *+ * 服务名称 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +public interface ServiceNameConstants { + + /** + * 认证服务的SERVICE_ID + */ + String AUTH_SERVICE = "kicc-auth"; + + /** + * SYSTEM模块 + */ + String SYSTEM_SERVICE = "kicc-system-biz"; + + /** + * MONITOR模块 + */ + String MONITOR_SERVICE = "kicc-monitor-biz"; + + /** + * COMMON模块 + */ + String COMMON_SERVICE = "kicc-common-biz"; + + /** + * seata分布式事务演示-订单模块 + */ + String SEATA_ORDER_SERVICE = "kicc-seata-order"; + + /** + * seata分布式事务演示-积分模块 + */ + String SEATA_POINT_SERVICE = "kicc-seata-point"; + + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/StringPool.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/StringPool.java new file mode 100644 index 0000000..439b711 --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/constant/StringPool.java @@ -0,0 +1,97 @@ +package com.cloud.kicc.common.core.constant; + +/** + * Copy to kicc.common.util + *
+ * Pool of String
constants to prevent repeating of
+ * hard-coded String
literals in the code.
+ * Due to fact that these are public static final
+ * they will be inlined by java compiler and
+ * reference to this class will be dropped.
+ * There is no performance gain of using this pool.
+ * Read: https://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.5
+ *
String
object.String
object.String
object.+ * API错误页面响应状态枚举 + *
+ * + * @Author: wangxiang4 + * @Since: 2023/8/16 + */ +@Getter +@RequiredArgsConstructor +public enum ExceptionEnum { + + UNAUTHORIZED_ACCESS(401, "禁止访问"), + PAGE_NOT_ACCESS(403, "页面无法访问"), + PAGE_NOT_FOUND(404, "网页未找到"), + ERROR(500, "错误"), + NET_WORK_ERROR(10000, "前端Js错误"), + PAGE_NOT_DATA(10100, "无数据页面"); + + /** + * 状态 + */ + private final int value; + + /** + * 描述 + */ + private final String description; + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/enums/LoginTypeEnum.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/enums/LoginTypeEnum.java new file mode 100644 index 0000000..d78b590 --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/enums/LoginTypeEnum.java @@ -0,0 +1,38 @@ +package com.cloud.kicc.common.core.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + *+ * 社交登录类型 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +@Getter +@RequiredArgsConstructor +public enum LoginTypeEnum { + + /** + * 账号密码登录 + */ + PWD("PWD", "账号密码登录"), + + /** + * 验证码登录 + */ + SMS("SMS", "验证码登录"); + + /** + * 类型 + */ + private final String type; + + /** + * 描述 + */ + private final String description; + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/exception/CheckedException.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/exception/CheckedException.java new file mode 100644 index 0000000..ba76ec4 --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/exception/CheckedException.java @@ -0,0 +1,34 @@ +package com.cloud.kicc.common.core.exception; + +import lombok.NoArgsConstructor; + +/** + *+ * 检查异常 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +@NoArgsConstructor +public class CheckedException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public CheckedException(String message) { + super(message); + } + + public CheckedException(Throwable cause) { + super(cause); + } + + public CheckedException(String message, Throwable cause) { + super(message, cause); + } + + public CheckedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/exception/ValidateCodeException.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/exception/ValidateCodeException.java new file mode 100644 index 0000000..2eeddd8 --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/exception/ValidateCodeException.java @@ -0,0 +1,22 @@ +package com.cloud.kicc.common.core.exception; + +/** + *+ * 验证码异常 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +public class ValidateCodeException extends RuntimeException { + + private static final long serialVersionUID = -7285211528095468156L; + + public ValidateCodeException() { + } + + public ValidateCodeException(String msg) { + super(msg); + } + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/factory/YamlPropertySourceFactory.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/factory/YamlPropertySourceFactory.java new file mode 100644 index 0000000..7d72189 --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/factory/YamlPropertySourceFactory.java @@ -0,0 +1,48 @@ +package com.cloud.kicc.common.core.factory; + +import lombok.AllArgsConstructor; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.support.EncodedResource; +import org.springframework.core.io.support.PropertySourceFactory; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Properties; + +/** + *+ * 加载yml格式的自定义配置文件 + * @link https://blog.csdn.net/zxl8899/article/details/106382719/ + *
+ * + * @Author: wangxiang4 + * @Date: 2022/3/7 + */ +@AllArgsConstructor +public class YamlPropertySourceFactory implements PropertySourceFactory { + + @Override + public PropertySource> createPropertySource(String name, EncodedResource resource) throws IOException { + Properties propertiesFromYaml = loadYamlIntoProperties(resource); + String sourceName = name != null ? name : resource.getResource().getFilename(); + assert sourceName != null; + return new PropertiesPropertySource(sourceName, propertiesFromYaml); + } + + private Properties loadYamlIntoProperties(EncodedResource resource) throws FileNotFoundException { + try { + YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); + factory.setResources(resource.getResource()); + factory.afterPropertiesSet(); + return factory.getObject(); + } catch (IllegalStateException e) { + Throwable cause = e.getCause(); + if (cause instanceof FileNotFoundException) { + throw (FileNotFoundException) e.getCause(); + } + throw e; + } + } +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/jackson/KiccJavaTimeModule.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/jackson/KiccJavaTimeModule.java new file mode 100644 index 0000000..7f9ff61 --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/jackson/KiccJavaTimeModule.java @@ -0,0 +1,55 @@ +package com.cloud.kicc.common.core.jackson; + +import cn.hutool.core.date.DatePattern; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.datatype.jsr310.PackageVersion; +import com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +/** + *+ * java 8 时间默认序列化 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/3/10 + */ +public class KiccJavaTimeModule extends SimpleModule { + + public KiccJavaTimeModule() { + super(PackageVersion.VERSION); + + // ======================= 时间序列化规则 =============================== + // LocalDateTime序列化时间戳 + this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN))); + // LocalDate序列化时间戳 + this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE)); + // LocalTime序列化时间戳 + this.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ISO_LOCAL_TIME)); + // Instant 类型序列化 + this.addSerializer(Instant.class, InstantSerializer.INSTANCE); + + // ======================= 时间反序列化规则 ============================== + // yyyy-MM-dd HH:mm:ss + this.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN))); + // yyyy-MM-dd + this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE)); + // HH:mm:ss + this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ISO_LOCAL_TIME)); + // Instant 反序列化 + this.addDeserializer(Instant.class, InstantDeserializer.INSTANT); + } + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/BaseUtil.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/BaseUtil.java new file mode 100644 index 0000000..e9d8549 --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/BaseUtil.java @@ -0,0 +1,27 @@ +package com.cloud.kicc.common.core.util; + +import cn.hutool.core.lang.generator.SnowflakeGenerator; +import lombok.experimental.UtilityClass; + +/** + *+ * 基础工具类 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/3/21 + */ +@UtilityClass +public class BaseUtil { + + /** + * 雪花算法生成全局ID + * @Param + * @return + */ + public Long snowflakeId () { + SnowflakeGenerator snowflakeGenerator = new SnowflakeGenerator(); + return snowflakeGenerator.next(); + } + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/ClassUtil.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/ClassUtil.java new file mode 100644 index 0000000..44f586f --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/ClassUtil.java @@ -0,0 +1,97 @@ +package com.cloud.kicc.common.core.util; + +import lombok.experimental.UtilityClass; +import org.springframework.core.BridgeMethodResolver; +import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.core.MethodParameter; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.SynthesizingMethodParameter; +import org.springframework.web.method.HandlerMethod; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + *+ * 扩展类工具类 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +@UtilityClass +public class ClassUtil extends org.springframework.util.ClassUtils { + + private final ParameterNameDiscoverer PARAMETERNAMEDISCOVERER = new DefaultParameterNameDiscoverer(); + + /** + * 获取方法参数信息 + * @param constructor 构造器 + * @param parameterIndex 参数序号 + * @return {MethodParameter} + */ + public MethodParameter getMethodParameter(Constructor> constructor, int parameterIndex) { + MethodParameter methodParameter = new SynthesizingMethodParameter(constructor, parameterIndex); + methodParameter.initParameterNameDiscovery(PARAMETERNAMEDISCOVERER); + return methodParameter; + } + + /** + * 获取方法参数信息 + * @param method 方法 + * @param parameterIndex 参数序号 + * @return {MethodParameter} + */ + public MethodParameter getMethodParameter(Method method, int parameterIndex) { + MethodParameter methodParameter = new SynthesizingMethodParameter(method, parameterIndex); + methodParameter.initParameterNameDiscovery(PARAMETERNAMEDISCOVERER); + return methodParameter; + } + + /** + * 获取Annotation + * @param method Method + * @param annotationType 注解类 + * @param 泛型标记 + * @return {Annotation} + */ + public A getAnnotation(Method method, Class annotationType) { + Class> targetClass = method.getDeclaringClass(); + // The method may be on an interface, but we need attributes from the target + // class. + // If the target class is null, the method will be unchanged. + Method specificMethod = ClassUtil.getMostSpecificMethod(method, targetClass); + // If we are dealing with method with generic parameters, find the original + // method. + specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); + // 先找方法,再找方法上的类 + A annotation = AnnotatedElementUtils.findMergedAnnotation(specificMethod, annotationType); + ; + if (null != annotation) { + return annotation; + } + // 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类 + return AnnotatedElementUtils.findMergedAnnotation(specificMethod.getDeclaringClass(), annotationType); + } + + /** + * 获取Annotation + * @param handlerMethod HandlerMethod + * @param annotationType 注解类 + * @param 泛型标记 + * @return {Annotation} + */ + public A getAnnotation(HandlerMethod handlerMethod, Class annotationType) { + // 先找方法,再找方法上的类 + A annotation = handlerMethod.getMethodAnnotation(annotationType); + if (null != annotation) { + return annotation; + } + // 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类 + Class> beanType = handlerMethod.getBeanType(); + return AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType); + } + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/DateUtil.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/DateUtil.java new file mode 100644 index 0000000..ad5696a --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/DateUtil.java @@ -0,0 +1,50 @@ +package com.cloud.kicc.common.core.util; + +import java.lang.management.ManagementFactory; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +/** + *+ * 时间 util + *
+ * + * @Author: wangxiang4 + * @since: 2023/6/13 + */ +public class DateUtil extends cn.hutool.core.date.DateUtil { + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + public static String formatDate(long dateTime, String pattern) { + return format(new Date(dateTime), pattern); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) { + if (str == null) { + return null; + } + return parse(str.toString()); + } + + public static final String parseDateToStr(final String format, final Date date) { + return new SimpleDateFormat(format).format(date); + } + + public static final String formatUTC(final Date date, final String format) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); + simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + return simpleDateFormat.format(date); + } + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/FileUtil.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/FileUtil.java new file mode 100644 index 0000000..4647805 --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/FileUtil.java @@ -0,0 +1,113 @@ +package com.cloud.kicc.common.core.util; + +import cn.hutool.core.io.IoUtil; + +import javax.servlet.http.HttpServletRequest; +import java.io.*; +import java.net.URLEncoder; +import java.text.DecimalFormat; + +/** + * @author yong + * @date 2020/3/22 + * @description 文件工具类 + */ +public class FileUtil extends cn.hutool.core.io.FileUtil { + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + /** + * 转换文件大小 + * + * @param fileS + * @return + */ + public static String fileSize(long fileS) { + DecimalFormat df = new DecimalFormat("#.00"); + String fileSizeString = ""; + String wrongSize = "0B"; + if (fileS == 0) { + return wrongSize; + } + if (fileS < 1024) { + fileSizeString = df.format((double) fileS) + "B"; + } else if (fileS < 1048576) { + fileSizeString = df.format((double) fileS / 1024) + "KB"; + } else if (fileS < 1073741824) { + fileSizeString = df.format((double) fileS / 1048576) + "MB"; + } else { + fileSizeString = df.format((double) fileS / 1073741824) + "GB"; + } + return fileSizeString; + } + + public static void copyInputStreamToFile(InputStream source, File destination) throws IOException { + try { + copyToFile(source, destination); + } finally { + IoUtil.close(source); + } + + } + + public static void copyToFile(InputStream source, File destination) throws IOException { + FileOutputStream output = openOutputStream(destination); + + try { + IoUtil.copy(source, output); + output.close(); + } finally { + IoUtil.close(output); + } + + } + + public static FileOutputStream openOutputStream(File file) throws IOException { + return openOutputStream(file, false); + } + + public static FileOutputStream openOutputStream(File file, boolean append) throws IOException { + if (file.exists()) { + if (file.isDirectory()) { + throw new IOException("File '" + file + "' exists but is a directory"); + } + + if (!file.canWrite()) { + throw new IOException("File '" + file + "' cannot be written to"); + } + } else { + File parent = file.getParentFile(); + if (parent != null && !parent.mkdirs() && !parent.isDirectory()) { + throw new IOException("Directory '" + parent + "' could not be created"); + } + } + + return new FileOutputStream(file, append); + } + /** + * 下载文件名重新编码 + * + * @param request 请求对象 + * @param fileName 文件名 + * @return 编码后的文件名 + */ + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) + throws UnsupportedEncodingException { + final String agent = request.getHeader("USER-AGENT"); + String filename = fileName; + if (agent.contains("MSIE")) { + // IE浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + filename = filename.replace("+", " "); + } else if (agent.contains("Firefox")) { + // 火狐浏览器 + filename = new String(fileName.getBytes(), "ISO8859-1"); + } else if (agent.contains("Chrome")) { + // google浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } else { + // 其它浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + return filename; + } +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/HTMLFilterUtil.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/HTMLFilterUtil.java new file mode 100644 index 0000000..776bef3 --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/HTMLFilterUtil.java @@ -0,0 +1,499 @@ +package com.cloud.kicc.common.core.util; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *+ * HTML过滤器,用于去除XSS漏洞隐患。 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/19 + */ +public final class HTMLFilterUtil { + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap+ * Jasypt加解密单元测试 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/24 + */ +public class JasyptUtil { + + public static void main(String[] args) { + testEnvironmentProperties(); + } + + public static void testEnvironmentProperties() { + System.setProperty("jasypt.encryptor.password", "kicc"); + + PasswordEncoder ENCODER = new BCryptPasswordEncoder(); + + System.out.println(ENCODER.encode("123456")); + + StringEncryptor stringEncryptor = new DefaultLazyEncryptor(new StandardEnvironment()); + //加密方法 + System.out.println(stringEncryptor.encrypt("kicc")); + + //解密方法 + System.out.println(stringEncryptor.decrypt("6GBMom2U/XAHuMG3OSkOMw==")); + } + +} diff --git a/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/PinyinUtil.java b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/PinyinUtil.java new file mode 100644 index 0000000..e4b8e33 --- /dev/null +++ b/kicc-common-core/src/main/java/com/cloud/kicc/common/core/util/PinyinUtil.java @@ -0,0 +1,203 @@ +package com.cloud.kicc.common.core.util; + +import cn.hutool.core.exceptions.UtilException; +import cn.hutool.core.text.StrBuilder; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; + +/** + *+ * 拼音工具类 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/24 + */ +public class PinyinUtil { + /** 汉字对应ascii范围 */ + private static int[] pinyinValue = new int[] { -20319, -20317, -20304, -20295, -20292, -20283, -20265, -20257, -20242, -20230, -20051, -20036, -20032, -20026, -20002, -19990, -19986, -19982, + -19976, -19805, -19784, -19775, -19774, -19763, -19756, -19751, -19746, -19741, -19739, -19728, -19725, -19715, -19540, -19531, -19525, -19515, -19500, -19484, -19479, -19467, -19289, + -19288, -19281, -19275, -19270, -19263, -19261, -19249, -19243, -19242, -19238, -19235, -19227, -19224, -19218, -19212, -19038, -19023, -19018, -19006, -19003, -18996, -18977, -18961, + -18952, -18783, -18774, -18773, -18763, -18756, -18741, -18735, -18731, -18722, -18710, -18697, -18696, -18526, -18518, -18501, -18490, -18478, -18463, -18448, -18447, -18446, -18239, + -18237, -18231, -18220, -18211, -18201, -18184, -18183, -18181, -18012, -17997, -17988, -17970, -17964, -17961, -17950, -17947, -17931, -17928, -17922, -17759, -17752, -17733, -17730, + -17721, -17703, -17701, -17697, -17692, -17683, -17676, -17496, -17487, -17482, -17468, -17454, -17433, -17427, -17417, -17202, -17185, -16983, -16970, -16942, -16915, -16733, -16708, + -16706, -16689, -16664, -16657, -16647, -16474, -16470, -16465, -16459, -16452, -16448, -16433, -16429, -16427, -16423, -16419, -16412, -16407, -16403, -16401, -16393, -16220, -16216, + -16212, -16205, -16202, -16187, -16180, -16171, -16169, -16158, -16155, -15959, -15958, -15944, -15933, -15920, -15915, -15903, -15889, -15878, -15707, -15701, -15681, -15667, -15661, + -15659, -15652, -15640, -15631, -15625, -15454, -15448, -15436, -15435, -15419, -15416, -15408, -15394, -15385, -15377, -15375, -15369, -15363, -15362, -15183, -15180, -15165, -15158, + -15153, -15150, -15149, -15144, -15143, -15141, -15140, -15139, -15128, -15121, -15119, -15117, -15110, -15109, -14941, -14937, -14933, -14930, -14929, -14928, -14926, -14922, -14921, + -14914, -14908, -14902, -14894, -14889, -14882, -14873, -14871, -14857, -14678, -14674, -14670, -14668, -14663, -14654, -14645, -14630, -14594, -14429, -14407, -14399, -14384, -14379, + -14368, -14355, -14353, -14345, -14170, -14159, -14151, -14149, -14145, -14140, -14137, -14135, -14125, -14123, -14122, -14112, -14109, -14099, -14097, -14094, -14092, -14090, -14087, + -14083, -13917, -13914, -13910, -13907, -13906, -13905, -13896, -13894, -13878, -13870, -13859, -13847, -13831, -13658, -13611, -13601, -13406, -13404, -13400, -13398, -13395, -13391, + -13387, -13383, -13367, -13359, -13356, -13343, -13340, -13329, -13326, -13318, -13147, -13138, -13120, -13107, -13096, -13095, -13091, -13076, -13068, -13063, -13060, -12888, -12875, + -12871, -12860, -12858, -12852, -12849, -12838, -12831, -12829, -12812, -12802, -12607, -12597, -12594, -12585, -12556, -12359, -12346, -12320, -12300, -12120, -12099, -12089, -12074, + -12067, -12058, -12039, -11867, -11861, -11847, -11831, -11798, -11781, -11604, -11589, -11536, -11358, -11340, -11339, -11324, -11303, -11097, -11077, -11067, -11055, -11052, -11045, + -11041, -11038, -11024, -11020, -11019, -11018, -11014, -10838, -10832, -10815, -10800, -10790, -10780, -10764, -10587, -10544, -10533, -10519, -10331, -10329, -10328, -10322, -10315, + -10309, -10307, -10296, -10281, -10274, -10270, -10262, -10260, -10256, -10254 }; + + private static String[] pinyinStr = new String[] { "a", "ai", "an", "ang", "ao", "ba", "bai", "ban", "bang", "bao", "bei", "ben", "beng", "bi", "bian", "biao", "bie", "bin", "bing", "bo", "bu", + "ca", "cai", "can", "cang", "cao", "ce", "ceng", "cha", "chai", "chan", "chang", "chao", "che", "chen", "cheng", "chi", "chong", "chou", "chu", "chuai", "chuan", "chuang", "chui", "chun", + "chuo", "ci", "cong", "cou", "cu", "cuan", "cui", "cun", "cuo", "da", "dai", "dan", "dang", "dao", "de", "deng", "di", "dian", "diao", "die", "ding", "diu", "dong", "dou", "du", "duan", + "dui", "dun", "duo", "e", "en", "er", "fa", "fan", "fang", "fei", "fen", "feng", "fo", "fou", "fu", "ga", "gai", "gan", "gang", "gao", "ge", "gei", "gen", "geng", "gong", "gou", "gu", + "gua", "guai", "guan", "guang", "gui", "gun", "guo", "ha", "hai", "han", "hang", "hao", "he", "hei", "hen", "heng", "hong", "hou", "hu", "hua", "huai", "huan", "huang", "hui", "hun", + "huo", "ji", "jia", "jian", "jiang", "jiao", "jie", "jin", "jing", "jiong", "jiu", "ju", "juan", "jue", "jun", "ka", "kai", "kan", "kang", "kao", "ke", "ken", "keng", "kong", "kou", "ku", + "kua", "kuai", "kuan", "kuang", "kui", "kun", "kuo", "la", "lai", "lan", "lang", "lao", "le", "lei", "leng", "li", "lia", "lian", "liang", "liao", "lie", "lin", "ling", "liu", "long", + "lou", "lu", "lv", "luan", "lue", "lun", "luo", "ma", "mai", "man", "mang", "mao", "me", "mei", "men", "meng", "mi", "mian", "miao", "mie", "min", "ming", "miu", "mo", "mou", "mu", "na", + "nai", "nan", "nang", "nao", "ne", "nei", "nen", "neng", "ni", "nian", "niang", "niao", "nie", "nin", "ning", "niu", "nong", "nu", "nv", "nuan", "nue", "nuo", "o", "ou", "pa", "pai", + "pan", "pang", "pao", "pei", "pen", "peng", "pi", "pian", "piao", "pie", "pin", "ping", "po", "pu", "qi", "qia", "qian", "qiang", "qiao", "qie", "qin", "qing", "qiong", "qiu", "qu", + "quan", "que", "qun", "ran", "rang", "rao", "re", "ren", "reng", "ri", "rong", "rou", "ru", "ruan", "rui", "run", "ruo", "sa", "sai", "san", "sang", "sao", "se", "sen", "seng", "sha", + "shai", "shan", "shang", "shao", "she", "shen", "sheng", "shi", "shou", "shu", "shua", "shuai", "shuan", "shuang", "shui", "shun", "shuo", "si", "song", "sou", "su", "suan", "sui", "sun", + "suo", "ta", "tai", "tan", "tang", "tao", "te", "teng", "ti", "tian", "tiao", "tie", "ting", "tong", "tou", "tu", "tuan", "tui", "tun", "tuo", "wa", "wai", "wan", "wang", "wei", "wen", + "weng", "wo", "wu", "xi", "xia", "xian", "xiang", "xiao", "xie", "xin", "xing", "xiong", "xiu", "xu", "xuan", "xue", "xun", "ya", "yan", "yang", "yao", "ye", "yi", "yin", "ying", "yo", + "yong", "you", "yu", "yuan", "yue", "yun", "za", "zai", "zan", "zang", "zao", "ze", "zei", "zen", "zeng", "zha", "zhai", "zhan", "zhang", "zhao", "zhe", "zhen", "zheng", "zhi", "zhong", + "zhou", "zhu", "zhua", "zhuai", "zhuan", "zhuang", "zhui", "zhun", "zhuo", "zi", "zong", "zou", "zu", "zuan", "zui", "zun", "zuo" }; + + /** + * 获取所给中文的每个汉字首字母组成首字母字符串 + * + * @param chinese 汉字字符串 + * @return 首字母字符串 + */ + public static String getAllFirstLetter(String chinese) { + if (StrUtil.isBlank(chinese)) { + return StrUtil.EMPTY; + } + + int len = chinese.length(); + final StrBuilder strBuilder = new StrBuilder(len); + for (int i = 0; i < len; i++) { + strBuilder.append(getFirstLetter(chinese.charAt(i))); + } + + return strBuilder.toString(); + } + + /** + * 获取拼音首字母+ * Spring工具类 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +@Slf4j +@Service +@Lazy(false) +public class SpringContextHolderUtil implements ApplicationContextAware, DisposableBean { + + private static ApplicationContext applicationContext = null; + + /** + * 取得存储在静态变量中的ApplicationContext. + */ + public static ApplicationContext getApplicationContext() { + return applicationContext; + } + + /** + * 实现ApplicationContextAware接口, 注入Context到静态变量中. + */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + SpringContextHolderUtil.applicationContext = applicationContext; + } + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + @SuppressWarnings("unchecked") + public static+ * 时间计算工具类 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/1/15 + */ +public class TimeUtils { + + public static String toTimeString(long time) { + TimeUtils t = new TimeUtils(time); + int day = t.get(TimeUtils.DAY); + int hour = t.get(TimeUtils.HOUR); + int minute = t.get(TimeUtils.MINUTE); + int second = t.get(TimeUtils.SECOND); + StringBuilder sb = new StringBuilder(); + if (day > 0){ + sb.append(day).append("天"); + } + if (hour > 0){ + sb.append(hour).append("时"); + } + if (minute > 0){ + sb.append(minute).append("分"); + } + if (second > 0){ + sb.append(second).append("秒"); + } + return sb.toString(); + } + + /** + * 时间字段常量,表示“秒” + */ + public final static int SECOND = 0; + + /** + * 时间字段常量,表示“分” + */ + public final static int MINUTE = 1; + + /** + * 时间字段常量,表示“时” + */ + public final static int HOUR = 2; + + /** + * 时间字段常量,表示“天” + */ + public final static int DAY = 3; + + /** + * 各常量允许的最大值 + */ + private final int[] maxFields = { 59, 59, 23, Integer.MAX_VALUE - 1 }; + + /** + * 各常量允许的最小值 + */ + private final int[] minFields = { 0, 0, 0, Integer.MIN_VALUE }; + + /** + * 默认的字符串格式时间分隔符 + */ + private String timeSeparator = ":"; + + /** + * 时间数据容器 + */ + private int[] fields = new int[4]; + + /** + * 无参构造,将各字段置为 0 + */ + public TimeUtils() { + this(0, 0, 0, 0); + } + + /** + * 使用时、分构造一个时间 + * @param hour 小时 + * @param minute 分钟 + */ + public TimeUtils(int hour, int minute) { + this(0, hour, minute, 0); + } + + /** + * 使用时、分、秒构造一个时间 + * @param hour 小时 + * @param minute 分钟 + * @param second 秒 + */ + public TimeUtils(int hour, int minute, int second) { + this(0, hour, minute, second); + } + + /** + * 使用一个字符串构造时间+ * 扩展用于 Web 应用程序的各种实用程序工具类 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +@Slf4j +@UtilityClass +public class WebUtil extends org.springframework.web.util.WebUtils { + + private final String BASIC_ = "Basic "; + + private final String UNKNOWN = "unknown"; + + /** + * 判断是否ajax请求 spring ajax 返回含有 ResponseBody 或者 RestController注解 + * @param handlerMethod HandlerMethod + * @return 是否ajax请求 + */ + public boolean isBody(HandlerMethod handlerMethod) { + ResponseBody responseBody = ClassUtil.getAnnotation(handlerMethod, ResponseBody.class); + return responseBody != null; + } + + /** + * 读取cookie + * @param name cookie name + * @return cookie value + */ + public String getCookieVal(String name) { + if (WebUtil.getRequest().isPresent()) { + return getCookieVal(WebUtil.getRequest().get(), name); + } + return null; + } + + /** + * 读取cookie + * @param request HttpServletRequest + * @param name cookie name + * @return cookie value + */ + public String getCookieVal(HttpServletRequest request, String name) { + Cookie cookie = getCookie(request, name); + return cookie != null ? cookie.getValue() : null; + } + + /** + * 清除 某个指定的cookie + * @param response HttpServletResponse + * @param key cookie key + */ + public void removeCookie(HttpServletResponse response, String key) { + setCookie(response, key, null, 0); + } + + /** + * 设置cookie + * @param response HttpServletResponse + * @param name cookie name + * @param value cookie value + * @param maxAgeInSeconds maxage + */ + public void setCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds) { + Cookie cookie = new Cookie(name, value); + cookie.setPath("/"); + cookie.setMaxAge(maxAgeInSeconds); + cookie.setHttpOnly(true); + response.addCookie(cookie); + } + + /** + * 获取 HttpServletRequest + * @return {HttpServletRequest} + */ + public Optional+ * 开启数据存储和访问层 + *
+ * + * @Author: wangxiang4 + * @Since: 2023/9/15 + */ +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Import({ MybatisConfiguration.class, RedisTemplateConfiguration.class }) +public @interface EnableKiccDataRepository { + +} diff --git a/kicc-common-data/src/main/java/com/cloud/kicc/common/data/config/MybatisConfiguration.java b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/config/MybatisConfiguration.java new file mode 100644 index 0000000..b8b7a42 --- /dev/null +++ b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/config/MybatisConfiguration.java @@ -0,0 +1,66 @@ +package com.cloud.kicc.common.data.config; + +import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.baomidou.mybatisplus.extension.MybatisMapWrapperFactory; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.cloud.kicc.common.core.factory.YamlPropertySourceFactory; +import com.cloud.kicc.common.data.handler.BaseMetaObjectHandler; +import com.cloud.kicc.common.data.handler.KiccTenantLineHandler; +import com.cloud.kicc.common.data.plugins.KiccPaginationInnerInterceptor; +import com.cloud.kicc.common.data.plugins.KiccTenantLineInnerInterceptor; +import com.cloud.kicc.common.data.properties.TenantProperties; +import lombok.RequiredArgsConstructor; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +/** + *+ * mybatis plus 统一配置 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +@Configuration(proxyBeanMethods = false) +@MapperScan("com.cloud.kicc.**.mapper") +@EnableConfigurationProperties(TenantProperties.class) +@PropertySource(factory = YamlPropertySourceFactory.class, value = "classpath:kicc-tenant.yml") +@RequiredArgsConstructor +public class MybatisConfiguration { + + private final TenantProperties tenantProperties; + + /** + * 插件配置 + * 多租户插件, 自动拼接多租户id进行增删改查 + * 分页插件, 对于单一数据库类型来说,都建议配置该值,避免每次分页都去抓取数据库类型 + */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new KiccTenantLineInnerInterceptor(new KiccTenantLineHandler(tenantProperties))); + interceptor.addInnerInterceptor(new KiccPaginationInnerInterceptor()); + interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); + return interceptor; + } + + /** + * 审计字段自动填充 + * @return {@link MetaObjectHandler} + */ + @Bean + public BaseMetaObjectHandler mybatisPlusMetaObjectHandler() { + return new BaseMetaObjectHandler(); + } + + @Bean + public ConfigurationCustomizer mybatisConfigurationCustomizer(){ + return configuration -> configuration.setObjectWrapperFactory(new MybatisMapWrapperFactory()); + } + +} diff --git a/kicc-common-data/src/main/java/com/cloud/kicc/common/data/config/RedisTemplateConfiguration.java b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/config/RedisTemplateConfiguration.java new file mode 100644 index 0000000..99b312d --- /dev/null +++ b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/config/RedisTemplateConfiguration.java @@ -0,0 +1,60 @@ +package com.cloud.kicc.common.data.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.*; +import org.springframework.data.redis.serializer.RedisSerializer; + +/** + *+ * Redis 配置 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +@EnableCaching +@Configuration(proxyBeanMethods = false) +public class RedisTemplateConfiguration { + + @Bean + @ConditionalOnMissingBean + public RedisTemplate+ * 基础模型 + *
+ * + * @Author: wangxiang4 + * @Date: 2021/12/28 + */ +@Data +public class BaseEntity implements Serializable { + + protected static final long serialVersionUID = 1L; + + /** 多租户ID */ + @ApiModelProperty("多租户ID") + protected String tenantId; + + /** 当前用户 */ + @ApiModelProperty("当前用户") + @TableField(exist = false) + protected KiccUser currentUser; + + /** 自定义sql过滤 */ + @TableField(exist = false) + @JsonIgnore + private String sqlFilter; + +} diff --git a/kicc-common-data/src/main/java/com/cloud/kicc/common/data/entity/CasUser.java b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/entity/CasUser.java new file mode 100644 index 0000000..27544ec --- /dev/null +++ b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/entity/CasUser.java @@ -0,0 +1,185 @@ +package com.cloud.kicc.common.data.entity; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + *+ * CAS统一认证用户数据 + *
+ * + * @Author: wangxiang4 + * @Since: 2023/8/16 + */ +@Setter +@Getter +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +public class CasUser extends User { + + private static final long serialVersionUID = 1L; + + /** 用户ID */ + private String id; + + /** 昵称 */ + private String nickName; + + /** 邮箱 */ + private String email; + + /** 手机号 */ + private String phone; + + /** 性别 */ + private String sex; + + /** 头像地址 */ + private String avatar; + + /** 最后登陆ip */ + private String loginIp; + + /** 最后登陆时间 */ + private LocalDateTime loginTime; + + /** 状态 */ + private String ssoStatus; + + /** 创建ID */ + private String ssoCreateById; + + /** 创建人 */ + private String ssoCreateByName; + + /** 创建时间 */ + private LocalDateTime ssoCreateTime; + + /** 更新ID */ + private String ssoUpdateById; + + /** 更新人 */ + private String ssoUpdateByName; + + /** 更新时间 */ + private LocalDateTime ssoUpdateTime; + + /** 备注 */ + private String remarks; + + /** 角色ID */ + private String roleId; + + /** 多租户ID */ + private String tenantId; + + /** sso扩展信息 */ + private Map+ * 通用模型 + *
+ * + * @Author: wangxiang4 + * @Date: 2021/12/28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class CommonEntity extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** 创建id */ + @ApiModelProperty("创建id") + @TableField(value = "create_by_id", fill = FieldFill.INSERT) + protected String createById; + + /** 创建者 */ + @ApiModelProperty("创建人") + @TableField(value = "create_by_name", fill = FieldFill.INSERT) + protected String createByName; + + /** 创建时间 */ + @ApiModelProperty("创建时间") + @TableField(value = "create_time", fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + protected LocalDateTime createTime; + + /** 更新id */ + @ApiModelProperty("更新id") + @TableField(value = "update_by_id", fill = FieldFill.UPDATE) + protected String updateById; + + /** 更新者 */ + @ApiModelProperty("更新者") + @TableField(value = "update_by_name", fill = FieldFill.UPDATE) + protected String updateByName; + + /** 更新时间 */ + @ApiModelProperty("更新时间") + @TableField(value = "update_time", fill = FieldFill.UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + protected LocalDateTime updateTime; + + /** 备注 */ + @ApiModelProperty("备注") + protected String remarks; + + /** 删除标志(0代表存在 1代表删除)*/ + @TableLogic + @JsonIgnore + protected String delFlag; + + /** 开始时间 */ + @TableField(exist = false) + @JsonIgnore + private String beginTime; + + /** 结束时间 */ + @TableField(exist = false) + @JsonIgnore + private String endTime; + +} diff --git a/kicc-common-data/src/main/java/com/cloud/kicc/common/data/entity/KiccUser.java b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/entity/KiccUser.java new file mode 100644 index 0000000..064e6a1 --- /dev/null +++ b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/entity/KiccUser.java @@ -0,0 +1,109 @@ +package com.cloud.kicc.common.data.entity; + +import cn.hutool.core.util.ObjectUtil; +import com.cloud.kicc.common.core.constant.SecurityConstants; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +import java.time.LocalDateTime; +import java.util.List; + +/** + *+ * 扩展用户数据 + *
+ * + * @Author: wangxiang4 + * @Since: 2023/8/16 + */ +@Getter +@Setter +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@ApiModel(description = "完整用户信息") +public class KiccUser extends CasUser { + + @ApiModelProperty("扩展用户ID") + private String id; + + @ApiModelProperty("CAS用户ID") + private String casUserId; + + @ApiModelProperty("用户类型 {@link com.cloud.kicc.system.api.enums.UserTypeEnum }") + private String userType; + + @ApiModelProperty("机构ID") + private String deptId; + + @ApiModelProperty("机构名称") + private String deptName; + + @ApiModelProperty("地图标记点位置图片旋转值") + private Double mapOrientation; + + @ApiModelProperty("地图设计器默认中心点位置") + private String mapCenter; + + @ApiModelProperty("帐号状态(0正常 1停用)") + private String status; + + @ApiModelProperty("指定登录后首页跳转") + private String homePath; + + @ApiModelProperty("角色ID集合") + private String[] roleIds; + + @ApiModelProperty("菜单按钮权限") + private String[] permissions; + + @ApiModelProperty("多租户ID集合") + private String[] tenantIds; + + @ApiModelProperty("创建ID") + private String createById; + + @ApiModelProperty("创建人") + private String createByName; + + @ApiModelProperty("创建时间") + private LocalDateTime createTime; + + @ApiModelProperty("更新ID") + private String updateById; + + @ApiModelProperty("更新人") + private String updateByName; + + @ApiModelProperty("更新时间") + private LocalDateTime updateTime; + + public KiccUser() { + super(SecurityConstants.MOCK_USERNAME, SecurityConstants.MOCK_PASSWORD, AuthorityUtils.createAuthorityList()); + } + + @JsonCreator + public KiccUser(@JsonProperty("username") String username, + @JsonProperty("password") String password, + @JsonProperty("enabled") boolean enabled, + @JsonProperty("accountNonExpired") boolean accountNonExpired, + @JsonProperty("credentialsNonExpired") boolean credentialsNonExpired, + @JsonProperty("accountNonLocked") boolean accountNonLocked, + @JsonProperty("authorities") List+ * SSO用户统一表 + *
+ * + * @Author: wangxiang4 + * @Since: 2023/8/6 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("cas_sso_user") +@ApiModel(description = "SSO用户表") +public class SsoUser extends CommonEntity { + + private static final long serialVersionUID = 1L; + + /** 用户ID */ + private String id; + + /** 用户名 */ + private String userName; + + /** 昵称 */ + private String nickName; + + /** 密码 */ + private String password; + + /** 用户邮箱 */ + private String email; + + /** 手机号码 */ + private String phone; + + /** 用户性别(0男 1女 2未知)*/ + private String sex; + + /** 头像路径 */ + private String avatar; + + /** 最后登陆IP */ + private String loginIp; + + /** 帐号状态(0正常 1停用)*/ + private String status; + + /** 最后登陆时间 */ + private LocalDateTime loginTime; + + /** 新密码 */ + @TableField(exist = false) + private String newPassword; +} diff --git a/kicc-common-data/src/main/java/com/cloud/kicc/common/data/entity/TreeEntity.java b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/entity/TreeEntity.java new file mode 100644 index 0000000..fd7c1d9 --- /dev/null +++ b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/entity/TreeEntity.java @@ -0,0 +1,46 @@ +package com.cloud.kicc.common.data.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + *+ * 树结构模型 + *
+ * + * @Author: wangxiang4 + * @Date: 2021/12/28 + */ +@Data +public class TreeEntity+ * 数据类型 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +@Getter +@AllArgsConstructor +public enum DataTypeEnum { + /** + * mysql + */ + MYSQL("mysql", "com.mysql.cj.jdbc.Driver"), + + /** + * sqlserver + */ + SQLSERVER("sqlserver", "com.microsoft.sqlserver.jdbc.SQLServerDriver"), + + /** + * oracle + */ + ORACLE("oracle", "oracle.jdbc.driver.OracleDriver"), + + /** + * Postgresql + */ + POSTGRESQL("postgresql", "org.postgresql.Driver"), + + /** + * sqlite + */ + SQLITE("sqlite", "org.sqlite.JDBC"); + + /** + * 类型 + */ + private final String type; + + /** + * 驱动 + */ + private final String driverClassName; + +} diff --git a/kicc-common-data/src/main/java/com/cloud/kicc/common/data/handler/BaseMetaObjectHandler.java b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/handler/BaseMetaObjectHandler.java new file mode 100644 index 0000000..c1b9b5e --- /dev/null +++ b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/handler/BaseMetaObjectHandler.java @@ -0,0 +1,75 @@ +package com.cloud.kicc.common.data.handler; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.cloud.kicc.common.data.entity.CasUser; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.time.LocalDateTime; +import java.util.Optional; + +/** + *+ * 公共字段自动填充 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +public class BaseMetaObjectHandler implements MetaObjectHandler { + + @Override + public void insertFill(MetaObject metaObject) { + Object createTime = getFieldValByName("createTime", metaObject); + if (createTime == null) { + setFieldValByName("createTime", LocalDateTime.now(), metaObject); + } + + if (Optional.ofNullable(getUser()).isPresent()) { + Object createById = getFieldValByName("createById", metaObject); + if (createById == null) { + setFieldValByName("createById", getUser().getId(), metaObject); + } + + Object createByName = getFieldValByName("createByName", metaObject); + if (createByName == null) { + setFieldValByName("createByName", getUser().getUsername(), metaObject); + } + } + } + + @Override + public void updateFill(MetaObject metaObject) { + Object fieldValue = getFieldValByName("updateTime", metaObject); + if (fieldValue == null) { + setFieldValByName("updateTime", LocalDateTime.now(), metaObject); + } + + if (Optional.ofNullable(getUser()).isPresent()) { + Object updateById = getFieldValByName("updateById", metaObject); + if (updateById == null) { + setFieldValByName("updateById", getUser().getId(), metaObject); + } + + Object updateByName = getFieldValByName("updateByName", metaObject); + if (updateByName == null) { + setFieldValByName("updateByName", getUser().getUsername(), metaObject); + } + } + } + + /** + * 获取用户 + */ + protected CasUser getUser() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (Optional.ofNullable(authentication).isPresent()) { + Object principal = authentication.getPrincipal(); + if (principal instanceof CasUser) { + return (CasUser) principal; + } + } + return null; + } +} diff --git a/kicc-common-data/src/main/java/com/cloud/kicc/common/data/handler/KiccTenantLineHandler.java b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/handler/KiccTenantLineHandler.java new file mode 100644 index 0000000..5573d0a --- /dev/null +++ b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/handler/KiccTenantLineHandler.java @@ -0,0 +1,77 @@ +package com.cloud.kicc.common.data.handler; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; +import com.cloud.kicc.common.data.entity.CasUser; +import com.cloud.kicc.common.data.override.TenantLikeExpression; +import com.cloud.kicc.common.data.properties.TenantProperties; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.schema.Column; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.util.List; +import java.util.Optional; + +/** + *+ * 多租户拦截处理 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/4/2 + */ +public class KiccTenantLineHandler implements TenantLineHandler { + + private TenantProperties tenantProperties; + + public KiccTenantLineHandler(TenantProperties tenantProperties) { + this.tenantProperties = tenantProperties; + } + + /** + * 默认为tenant_id字段,尽量不要去改动,因为会牵扯到实体类中的TenantId字段 + * @return String: 表中的多租户字段 + */ + @Override + public String getTenantIdColumn() { + return TenantLineHandler.super.getTenantIdColumn(); + } + + @Override + public boolean ignoreTable(String tableName) { + return tenantProperties.getExclusionTable().contains(tableName); + } + + /** + * 新增数据字段中存在多租户字段是否忽略此多租户字段拼接新增 + * @Param columns: 当前新增表所有字段 + * @Param tenantIdColumn: 多租户字段 + * @return boolean: 是否忽略此多租户字段拼接新增 + */ + @Override + public boolean ignoreInsert(List+ * 重写StringValue,支持多租户like拼接查询 + * 由于内部StringExpression会拼接默认会加'' + * 而多租户like条件经过处理不许需要加'',如果加上会导致数据查不出 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/5/11 + */ +public class TenantLikeExpression extends ASTNodeAccessImpl implements Expression { + + private String value = ""; + + public TenantLikeExpression() { + } + + public TenantLikeExpression(String escapedValue) { + this.value = escapedValue; + } + + public String getValue() { + return this.value; + } + + + public void setValue(String string) { + this.value = string; + } + + + @Override + public void accept(ExpressionVisitor expressionVisitor) { + } + + @Override + public String toString() { + return this.value; + } + + public TenantLikeExpression withValue(String value) { + this.setValue(value); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + TenantLikeExpression that = (TenantLikeExpression)o; + return Objects.equals(this.value, that.value); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash(new Object[]{this.value}); + } +} diff --git a/kicc-common-data/src/main/java/com/cloud/kicc/common/data/plugins/KiccPaginationInnerInterceptor.java b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/plugins/KiccPaginationInnerInterceptor.java new file mode 100644 index 0000000..7790214 --- /dev/null +++ b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/plugins/KiccPaginationInnerInterceptor.java @@ -0,0 +1,38 @@ +package com.cloud.kicc.common.data.plugins; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.ParameterUtils; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; + +import java.sql.SQLException; + +/** + *+ * 分页拦截器 + * 重构分页插件, 当 size 小于 0 时, 直接设置为 0, 防止错误查询全表 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +public class KiccPaginationInnerInterceptor extends PaginationInnerInterceptor { + + @Override + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, + ResultHandler resultHandler, BoundSql boundSql) throws SQLException { + IPage> page = ParameterUtils.findPage(parameter).orElse(null); + // size 小于 0 直接设置为 0 , 即不查询任何数据 + if (null != page && page.getSize() < 0) { + page.setSize(0); + } + super.beforeQuery(executor, ms, page, rowBounds, resultHandler, boundSql); + } + +} diff --git a/kicc-common-data/src/main/java/com/cloud/kicc/common/data/plugins/KiccTenantLineInnerInterceptor.java b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/plugins/KiccTenantLineInnerInterceptor.java new file mode 100644 index 0000000..4136ca6 --- /dev/null +++ b/kicc-common-data/src/main/java/com/cloud/kicc/common/data/plugins/KiccTenantLineInnerInterceptor.java @@ -0,0 +1,209 @@ +package com.cloud.kicc.common.data.plugins; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils; +import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; +import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; +import com.cloud.kicc.common.core.exception.CheckedException; +import com.cloud.kicc.common.data.override.TenantLikeExpression; +import lombok.NoArgsConstructor; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.Parenthesis; +import net.sf.jsqlparser.expression.StringValue; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.expression.operators.conditional.OrExpression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.ItemsList; +import net.sf.jsqlparser.expression.operators.relational.LikeExpression; +import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.update.Update; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import java.util.stream.Collectors; + +/** + *+ * 租户线路内部拦截器 + * 重构租户线路内部拦截器,支持查询多个多租户ID,还支持查询多个租户的共享数据,默认只支持一个多租户id查询 + * 支持多租户ID不存在时,查询所有租户ID数据 + *
+ * + * @Author: wangxiang4 + * @Date: 2022/2/18 + */ +@NoArgsConstructor +public class KiccTenantLineInnerInterceptor extends TenantLineInnerInterceptor { + + private TenantLineHandler tenantLineHandler; + + public KiccTenantLineInnerInterceptor(final TenantLineHandler tenantLineHandler) { + super(tenantLineHandler); + this.tenantLineHandler = tenantLineHandler; + } + + @Override + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { + if (ObjectUtil.isNotEmpty(tenantLineHandler.getTenantId())){ + super.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql); + } + } + + @Override + public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { + if (ObjectUtil.isNotEmpty(tenantLineHandler.getTenantId())) { + super.beforePrepare(sh, connection, transactionTimeout); + } + } + + @Override + protected void processInsert(Insert insert, int index, String sql, Object obj) { + if (!this.tenantLineHandler.ignoreTable(insert.getTable().getName())) { + List