后台管理框架+文件上传首次提交

master
yqy 1 year ago
commit b71b0b2fc9

@ -0,0 +1,9 @@
root = true
[*.{adoc,bat,groovy,html,java,js,jsp,kt,kts,md,properties,py,rb,sh,sql,svg,txt,xml,xsd}]
charset = utf-8
[*.{groovy,java,kt,kts,xml,xsd}]
indent_style = tab
indent_size = 4
continuation_indent_size = 8

@ -0,0 +1 @@
java-baseline=8

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Hccake
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,5 @@
# ballcat-boot
此项目是 Ballcat 单体应用的模板项目。
用户可以基于此模板项目进行业务的定制开发。

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ad-distribute-admin</artifactId>
<groupId>com.baiye</groupId>
<version>1.1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>admin-core</artifactId>
<dependencies>
<!--mybatis plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- 脱敏工具 -->
<dependency>
<groupId>com.baiye</groupId>
<artifactId>common-desensitize</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.baiye</groupId>
<artifactId>common-model</artifactId>
<version>1.1.0</version>
</dependency>
<!-- 基于 spring authorization server 的授权服务器 -->
<dependency>
<groupId>com.baiye</groupId>
<artifactId>security-oauth2-authorization-server</artifactId>
<scope>compile</scope>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.baiye</groupId>
<artifactId>security-oauth2-resource-server</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.baiye</groupId>
<artifactId>system-controller</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>
</project>

@ -0,0 +1,108 @@
package com.hccake.ballcat.admin.upms;
//import com.hccake.ballcat.admin.upms.log.LogConfiguration;
import com.hccake.ballcat.system.authentication.BallcatOAuth2TokenResponseEnhancer;
import com.hccake.ballcat.system.authentication.DefaultUserInfoCoordinatorImpl;
import com.hccake.ballcat.system.authentication.SysUserDetailsServiceImpl;
import com.hccake.ballcat.system.authentication.UserInfoCoordinator;
import com.hccake.ballcat.system.properties.SystemProperties;
import com.hccake.ballcat.system.service.SysUserService;
import org.ballcat.security.properties.SecurityProperties;
import org.ballcat.springsecurity.oauth2.server.authorization.web.authentication.OAuth2TokenResponseEnhancer;
import org.ballcat.springsecurity.oauth2.server.resource.introspection.SpringAuthorizationServerSharedStoredOpaqueTokenIntrospector;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
/**
* @author Hccake 2020/5/25 21:01
*/
@EnableAsync
@AutoConfiguration
@MapperScan("com.hccake.ballcat.**.mapper")
// @ComponentScan({ "com.hccake.ballcat.admin.upms", "com.hccake.ballcat.system",
// "com.hccake.ballcat.log",
// "com.hccake.ballcat.file", "com.hccake.ballcat.notify" })
@ComponentScan({ "com.hccake.ballcat.admin.upms", "com.hccake.ballcat.system", "com.hccake.ballcat.file" })
@EnableConfigurationProperties({ SystemProperties.class, SecurityProperties.class })
// @Import(LogConfiguration.class)
public class UpmsAutoConfiguration {
/**
*
*
* @author hccake
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SysUserService.class)
@ConditionalOnMissingBean(UserDetailsService.class)
static class UserDetailsServiceConfiguration {
/**
*
* @return SysUserDetailsServiceImpl
*/
@Bean
@ConditionalOnMissingBean
public UserDetailsService userDetailsService(SysUserService sysUserService,
UserInfoCoordinator userInfoCoordinator) {
return new SysUserDetailsServiceImpl(sysUserService, userInfoCoordinator);
}
/**
*
* @return UserInfoCoordinator
*/
@Bean
@ConditionalOnMissingBean
public UserInfoCoordinator userInfoCoordinator() {
return new DefaultUserInfoCoordinatorImpl();
}
}
/**
* spring-security-oauth2-authorization-server 使
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OAuth2Authorization.class)
static class SpringOAuth2AuthorizationServerConfiguration {
/**
* token
* @return TokenEnhancer Token
*/
@Bean
@ConditionalOnMissingBean
public OAuth2TokenResponseEnhancer oAuth2TokenResponseEnhancer() {
return new BallcatOAuth2TokenResponseEnhancer();
}
/**
* token 使 OAuth2AuthorizationService token
* @return SpringAuthorizationServerSharedStoredOpaqueTokenIntrospector
*/
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "ballcat.security.oauth2.resourceserver", name = "shared-stored-token",
havingValue = "true", matchIfMissing = true)
public OpaqueTokenIntrospector sharedStoredOpaqueTokenIntrospector(
OAuth2AuthorizationService authorizationService) {
return new SpringAuthorizationServerSharedStoredOpaqueTokenIntrospector(authorizationService);
}
}
}

@ -0,0 +1,42 @@
package com.hccake.ballcat.admin.upms.config.mybatis;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.hccake.ballcat.common.core.constant.GlobalConstants;
import com.hccake.ballcat.common.security.userdetails.User;
import com.hccake.ballcat.common.security.util.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import java.time.LocalDateTime;
/**
* @author Hccake 2019/7/26 14:41
*/
@Slf4j
public class FillMetaObjectHandle implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 逻辑删除标识
this.strictInsertFill(metaObject, "deleted", Long.class, GlobalConstants.NOT_DELETED_FLAG);
// 创建时间
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
// 创建人
User user = SecurityUtils.getUser();
if (user != null) {
this.strictInsertFill(metaObject, "createBy", Long.class, user.getUserId());
}
}
@Override
public void updateFill(MetaObject metaObject) {
// 修改时间
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
// 修改人
User user = SecurityUtils.getUser();
if (user != null) {
this.strictUpdateFill(metaObject, "updateBy", Long.class, user.getUserId());
}
}
}

@ -0,0 +1,62 @@
package com.hccake.ballcat.admin.upms.config.mybatis;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.hccake.extend.mybatis.plus.injector.CustomSqlInjector;
import com.hccake.extend.mybatis.plus.methods.InsertBatchSomeColumnByCollection;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
/**
* @author hccake
* @date 2020/04/19 MybatisPlusconditional
*/
@Configuration
public class MybatisPlusConfig {
/**
* MybatisPlusInterceptor </br>
* MPBean
* @return MybatisPlusInterceptor
*/
@Bean
@ConditionalOnMissingBean(MybatisPlusInterceptor.class)
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
/**
*
* @return FillMetaObjectHandle
*/
@Bean
@ConditionalOnMissingBean(MetaObjectHandler.class)
public MetaObjectHandler fillMetaObjectHandle() {
return new FillMetaObjectHandle();
}
/**
*
* @return ISqlInjector
*/
@Bean
@ConditionalOnMissingBean(ISqlInjector.class)
public ISqlInjector customSqlInjector() {
List<AbstractMethod> list = new ArrayList<>();
// 对于只在更新时进行填充的字段不做插入处理
list.add(new InsertBatchSomeColumnByCollection(t -> t.getFieldFill() != FieldFill.UPDATE));
return new CustomSqlInjector(list);
}
}

@ -0,0 +1,34 @@
package com.hccake.ballcat.admin.upms.config.task;
import cn.hutool.core.map.MapUtil;
import org.slf4j.MDC;
import org.springframework.core.task.TaskDecorator;
import java.util.Map;
/**
* 使asynctraceId
*
* @author huyuanzhi 2021-11-06 23:14:27
*/
public class MdcTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
final Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
return () -> {
if (MapUtil.isNotEmpty(copyOfContextMap)) {
// 现在:@Async线程上下文 恢复Web线程上下文的MDC数据
MDC.setContextMap(copyOfContextMap);
}
try {
runnable.run();
}
finally {
MDC.clear();
}
};
}
}

@ -0,0 +1,35 @@
package com.hccake.ballcat.admin.upms.config.task;
import org.springframework.boot.task.TaskExecutorCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线线
*
* @see org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
* @author hccake
*/
@Configuration(proxyBeanMethods = false)
public class TaskExecutionConfiguration {
/**
* springboot taskExecutor 使线
* @return TaskExecutorCustomizer
*/
@Bean
public TaskExecutorCustomizer taskExecutorCustomizer() {
// AbortPolicy: 直接抛出java.util.concurrent.RejectedExecutionException异常
// CallerRunsPolicy: 主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度
// DiscardOldestPolicy: 抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行
// DiscardPolicy: 抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行
// 这里使用主线程直接执行该任务
return (taskExecutor -> {
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
taskExecutor.setTaskDecorator(new MdcTaskDecorator());
});
}
}

@ -0,0 +1,78 @@
// package com.hccake.ballcat.admin.upms.log;
//
// import com.hccake.ballcat.common.log.access.handler.AccessLogHandler;
// import com.hccake.ballcat.common.log.operation.handler.OperationLogHandler;
// import com.hccake.ballcat.log.handler.CustomAccessLogHandler;
// import com.hccake.ballcat.log.handler.CustomOperationLogHandler;
// import com.hccake.ballcat.log.model.entity.AccessLog;
// import com.hccake.ballcat.log.model.entity.OperationLog;
// import com.hccake.ballcat.log.service.AccessLogService;
// import com.hccake.ballcat.log.service.LoginLogService;
// import com.hccake.ballcat.log.service.OperationLogService;
// import com.hccake.ballcat.log.thread.AccessLogSaveThread;
// import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
// import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
// import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
// import org.springframework.context.annotation.Bean;
// import org.springframework.context.annotation.Configuration;
// import
// org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
// import
// org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
//
/// **
// * @author hccake
// */
// @Configuration(proxyBeanMethods = false)
// @ConditionalOnClass(LoginLogService.class)
// public class LogConfiguration {
//
// /**
// * 访问日志保存
// * @param accessLogService 访问日志Service
// * @return CustomAccessLogHandler
// */
// @Bean
// @ConditionalOnBean(AccessLogService.class)
// @ConditionalOnMissingBean(AccessLogHandler.class)
// public AccessLogHandler<AccessLog> customAccessLogHandler(AccessLogService
// accessLogService) {
// return new CustomAccessLogHandler(new AccessLogSaveThread(accessLogService));
// }
//
// /**
// * 操作日志处理器
// * @param operationLogService 操作日志Service
// * @return CustomOperationLogHandler
// */
// @Bean
// @ConditionalOnBean(OperationLogService.class)
// @ConditionalOnMissingBean(OperationLogHandler.class)
// public OperationLogHandler<OperationLog> customOperationLogHandler(OperationLogService
// operationLogService) {
// return new CustomOperationLogHandler(operationLogService);
// }
//
// @ConditionalOnClass(OAuth2AuthorizationServerConfigurer.class)
// @ConditionalOnBean(LoginLogService.class)
// @ConditionalOnMissingBean(LoginLogHandler.class)
// @Configuration(proxyBeanMethods = false)
// static class SpringAuthorizationServerLoginLogConfiguration {
//
// /**
// * Spring Authorization Server 的登录日志处理,监听登录事件记录登录登出
// * @param loginLogService 操作日志Service
// * @param authorizationServerSettings 授权服务器设置
// * @return SpringAuthorizationServerLoginLogHandler
// */
// @Bean
// public LoginLogHandler springAuthorizationServerLoginLogHandler(LoginLogService
// loginLogService,
// AuthorizationServerSettings authorizationServerSettings) {
// return new SpringAuthorizationServerLoginLogHandler(loginLogService,
// authorizationServerSettings);
// }
//
// }
//
// }

@ -0,0 +1,8 @@
package com.hccake.ballcat.admin.upms.log;
/**
* @author hccake
*/
public interface LoginLogHandler {
}

@ -0,0 +1,140 @@
// package com.hccake.ballcat.admin.upms.log;
//
// import com.hccake.ballcat.common.core.util.WebUtils;
//// import com.hccake.ballcat.common.log.operation.enums.LogStatusEnum;
// import com.hccake.ballcat.common.security.util.SecurityUtils;
//// import com.hccake.ballcat.log.enums.LoginEventTypeEnum;
//// import com.hccake.ballcat.log.model.entity.LoginLog;
//// import com.hccake.ballcat.log.service.LoginLogService;
// import lombok.RequiredArgsConstructor;
// import
// org.ballcat.springsecurity.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken;
// import org.springframework.context.event.EventListener;
// import org.springframework.security.authentication.ProviderNotFoundException;
// import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
// import
// org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
// import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
// import org.springframework.security.authentication.event.LogoutSuccessEvent;
// import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
// import
// org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
// import
// org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken;
// import
// org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
//
// import javax.servlet.http.HttpServletRequest;
//
//// import static com.hccake.ballcat.log.handler.LoginLogUtils.prodLoginLog;
//
/// **
// * spring 授权服务器的登录日志处理器
// *
// * @author hccake
// */
// @RequiredArgsConstructor
// public class SpringAuthorizationServerLoginLogHandler implements LoginLogHandler {
//
//// private final LoginLogService loginLogService;
//
// private final AuthorizationServerSettings authorizationServerSettings;
//
// /**
// * 登录成功事件监听 记录用户登录日志
// * @param event 登录成功 event
// */
// @EventListener(AuthenticationSuccessEvent.class)
// public void onAuthenticationSuccessEvent(AuthenticationSuccessEvent event) {
// Object source = event.getSource();
// String username = null;
//
// String tokenEndpoint = authorizationServerSettings.getTokenEndpoint();
// HttpServletRequest request = WebUtils.getRequest();
// boolean isOauth2LoginRequest = request.getRequestURI().equals(tokenEndpoint);
//
// // Oauth2登录 和表单登录 处理分开
// if (isOauth2LoginRequest && source instanceof OAuth2AccessTokenAuthenticationToken) {
// username = SecurityUtils.getAuthentication().getName();
// }
// else if (!isOauth2LoginRequest && source instanceof
// UsernamePasswordAuthenticationToken) {
// username = ((UsernamePasswordAuthenticationToken) source).getName();
// }
//
//// if (username != null) {
//// LoginLog loginLog = prodLoginLog(username).setMsg("登录成功")
//// .setStatus(LogStatusEnum.SUCCESS.getValue())
//// .setEventType(LoginEventTypeEnum.LOGIN.getValue());
//// loginLogService.save(loginLog);
//// }
// }
//
// /**
// * 监听鉴权失败事件,记录登录失败日志
// * @param event the event
// */
// @EventListener(AbstractAuthenticationFailureEvent.class)
// public void onAuthenticationFailureEvent(AbstractAuthenticationFailureEvent event) {
// if (event.getException().getClass().isAssignableFrom(ProviderNotFoundException.class))
// {
// return;
// }
//
// Object source = event.getSource();
// String username = null;
//
// String tokenEndpoint = authorizationServerSettings.getTokenEndpoint();
// HttpServletRequest request = WebUtils.getRequest();
// boolean isOauth2LoginRequest = request.getRequestURI().equals(tokenEndpoint);
//
// // Oauth2登录 和表单登录 处理分开
// if (isOauth2LoginRequest && source instanceof
// OAuth2AuthorizationGrantAuthenticationToken) {
// username = ((OAuth2AuthorizationGrantAuthenticationToken) source).getName();
// }
// else if (!isOauth2LoginRequest && source instanceof
// UsernamePasswordAuthenticationToken) {
// username = ((UsernamePasswordAuthenticationToken) source).getName();
// }
//
//// if (username != null) {
//// LoginLog loginLog = prodLoginLog(username).setMsg(event.getException().getMessage())
//// .setEventType(LoginEventTypeEnum.LOGIN.getValue())
//// .setStatus(LogStatusEnum.FAIL.getValue());
//// loginLogService.save(loginLog);
//// }
// }
//
// /**
// * 登出成功事件监听
// * @param event the event
// */
// @EventListener(LogoutSuccessEvent.class)
// public void onLogoutSuccessEvent(LogoutSuccessEvent event) {
// Object source = event.getSource();
// String username = null;
//
// String tokenRevocationEndpoint =
// authorizationServerSettings.getTokenRevocationEndpoint();
// HttpServletRequest request = WebUtils.getRequest();
// boolean isOauth2Login = request.getRequestURI().equals(tokenRevocationEndpoint);
//
// // Oauth2撤销令牌 和表单登出 处理分开
// if (isOauth2Login && source instanceof OAuth2TokenRevocationAuthenticationToken) {
// OAuth2Authorization authorization = ((OAuth2TokenRevocationAuthenticationToken)
// source).getAuthorization();
// username = authorization.getPrincipalName();
// }
// else if (!isOauth2Login && source instanceof UsernamePasswordAuthenticationToken) {
// username = ((UsernamePasswordAuthenticationToken) source).getName();
// }
//
//// if (username != null) {
//// LoginLog loginLog = prodLoginLog(username).setMsg("登出成功")
//// .setEventType(LoginEventTypeEnum.LOGOUT.getValue());
//// loginLogService.save(loginLog);
//// }
// }
//
// }

@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.hccake.ballcat.admin.upms.UpmsAutoConfiguration

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ad-distribute-admin</artifactId>
<groupId>com.baiye</groupId>
<version>1.1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>admin-websocket</artifactId>
<dependencies>
<dependency>
<groupId>com.baiye</groupId>
<artifactId>admin-core</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.baiye</groupId>
<artifactId>ad-distribute-starter-websocket</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>
</project>

@ -0,0 +1,34 @@
package com.hccake.ballcat.admin.websocket;
import com.hccake.ballcat.admin.websocket.component.UserAttributeHandshakeInterceptor;
import com.hccake.ballcat.admin.websocket.component.UserSessionKeyGenerator;
import com.hccake.ballcat.common.websocket.session.SessionKeyGenerator;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.socket.server.HandshakeInterceptor;
/**
* @author Hccake 2021/1/5
* @version 1.0
*/
@Import({ SystemWebsocketEventListenerConfiguration.class })
@Configuration
@RequiredArgsConstructor
public class AdminWebSocketAutoConfiguration {
@Bean
@ConditionalOnMissingBean(UserAttributeHandshakeInterceptor.class)
public HandshakeInterceptor authenticationHandshakeInterceptor() {
return new UserAttributeHandshakeInterceptor();
}
@Bean
@ConditionalOnMissingBean(SessionKeyGenerator.class)
public SessionKeyGenerator userSessionKeyGenerator() {
return new UserSessionKeyGenerator();
}
}

@ -0,0 +1,27 @@
// package com.hccake.ballcat.admin.websocket;
//
// import com.hccake.ballcat.admin.websocket.listener.NotifyWebsocketEventListener;
// import com.hccake.ballcat.common.websocket.distribute.MessageDistributor;
// import com.hccake.ballcat.notify.handler.NotifyInfoDelegateHandler;
// import com.hccake.ballcat.notify.model.domain.NotifyInfo;
// import com.hccake.ballcat.notify.service.UserAnnouncementService;
// import lombok.RequiredArgsConstructor;
// import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
// import org.springframework.context.annotation.Bean;
// import org.springframework.context.annotation.Configuration;
//
// @RequiredArgsConstructor
// @ConditionalOnClass({ NotifyWebsocketEventListener.class, UserAnnouncementService.class
// })
// @Configuration(proxyBeanMethods = false)
// public class NotifyWebsocketEventListenerConfiguration {
//
// private final MessageDistributor messageDistributor;
//
// @Bean
// public NotifyWebsocketEventListener notifyWebsocketEventListener(
// NotifyInfoDelegateHandler<? super NotifyInfo> notifyInfoDelegateHandler) {
// return new NotifyWebsocketEventListener(messageDistributor, notifyInfoDelegateHandler);
// }
//
// }

@ -0,0 +1,25 @@
package com.hccake.ballcat.admin.websocket;
import com.hccake.ballcat.admin.websocket.listener.SystemWebsocketEventListener;
import com.hccake.ballcat.common.websocket.distribute.MessageDistributor;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Hccake
*/
@RequiredArgsConstructor
@ConditionalOnClass(SystemWebsocketEventListener.class)
@Configuration(proxyBeanMethods = false)
public class SystemWebsocketEventListenerConfiguration {
private final MessageDistributor messageDistributor;
@Bean
public SystemWebsocketEventListener systemWebsocketEventListener() {
return new SystemWebsocketEventListener(messageDistributor);
}
}

@ -0,0 +1,65 @@
package com.hccake.ballcat.admin.websocket.component;
import com.hccake.ballcat.admin.websocket.constant.AdminWebSocketConstants;
import com.hccake.ballcat.common.security.userdetails.User;
import com.hccake.ballcat.common.security.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import java.util.Map;
/**
* WebSocket session Idtoken
*
* @author Hccake 2021/1/4
* @version 1.0
*/
@RequiredArgsConstructor
public class UserAttributeHandshakeInterceptor implements HandshakeInterceptor {
/**
* Invoked before the handshake is processed.
* @param request the current request
* @param response the current response
* @param wsHandler the target WebSocket handler
* @param attributes the attributes from the HTTP handshake to associate with the
* WebSocket session; the provided attributes are copied, the original map is not
* used.
* @return whether to proceed with the handshake ({@code true}) or abort
* ({@code false})
*/
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) {
String accessToken = null;
// 获得 accessToken
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
accessToken = serverRequest.getServletRequest().getParameter(AdminWebSocketConstants.TOKEN_ATTR_NAME);
}
// 由于 WebSocket 握手是由 http 升级的,携带 token 已经被 Security 拦截验证了,所以可以直接获取到用户
User user = SecurityUtils.getUser();
attributes.put(AdminWebSocketConstants.TOKEN_ATTR_NAME, accessToken);
attributes.put(AdminWebSocketConstants.USER_KEY_ATTR_NAME, user.getUserId());
return true;
}
/**
* Invoked after the handshake is done. The response status and headers indicate the
* results of the handshake, i.e. whether it was successful or not.
* @param request the current request
* @param response the current response
* @param wsHandler the target WebSocket handler
* @param exception an exception raised during the handshake, or {@code null} if none
*/
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception exception) {
// doNothing
}
}

@ -0,0 +1,32 @@
package com.hccake.ballcat.admin.websocket.component;
import com.hccake.ballcat.admin.websocket.constant.AdminWebSocketConstants;
import com.hccake.ballcat.common.websocket.session.SessionKeyGenerator;
import lombok.RequiredArgsConstructor;
import org.springframework.web.socket.WebSocketSession;
/**
* <p>
* WebSocketSession
* </p>
*
* 使 session session 便 session
*
* @author Hccake 2021/1/5
* @version 1.0
*/
@RequiredArgsConstructor
public class UserSessionKeyGenerator implements SessionKeyGenerator {
/**
* session
* @see UserAttributeHandshakeInterceptor session
* @param webSocketSession session
* @return session
*/
@Override
public Object sessionKey(WebSocketSession webSocketSession) {
return webSocketSession.getAttributes().get(AdminWebSocketConstants.USER_KEY_ATTR_NAME);
}
}

@ -0,0 +1,22 @@
package com.hccake.ballcat.admin.websocket.constant;
/**
* @author Hccake 2021/1/5
* @version 1.0
*/
public final class AdminWebSocketConstants {
private AdminWebSocketConstants() {
}
/**
* WebSocketSession Attribute token
*/
public static final String TOKEN_ATTR_NAME = "access_token";
/**
* WebSocketSession Attribute
*/
public static final String USER_KEY_ATTR_NAME = "userId";
}

@ -0,0 +1,60 @@
// package com.hccake.ballcat.admin.websocket.listener;
//
// import com.hccake.ballcat.common.util.JsonUtils;
// import com.hccake.ballcat.common.websocket.distribute.MessageDO;
// import com.hccake.ballcat.common.websocket.distribute.MessageDistributor;
// import com.hccake.ballcat.notify.event.AnnouncementCloseEvent;
// import com.hccake.ballcat.notify.event.StationNotifyPushEvent;
// import com.hccake.ballcat.notify.handler.NotifyInfoDelegateHandler;
// import com.hccake.ballcat.notify.model.domain.NotifyInfo;
// import com.hccake.ballcat.admin.websocket.message.AnnouncementCloseMessage;
// import com.hccake.ballcat.system.model.entity.SysUser;
// import lombok.RequiredArgsConstructor;
// import lombok.extern.slf4j.Slf4j;
// import org.springframework.context.event.EventListener;
// import org.springframework.scheduling.annotation.Async;
//
// import java.util.List;
//
/// **
// * @author Hccake 2021/1/5
// * @version 1.0
// */
// @Slf4j
// @RequiredArgsConstructor
// public class NotifyWebsocketEventListener {
//
// private final MessageDistributor messageDistributor;
//
// private final NotifyInfoDelegateHandler<? super NotifyInfo> notifyInfoDelegateHandler;
//
// /**
// * 公告关闭事件监听
// * @param event the AnnouncementCloseEvent
// */
// @Async
// @EventListener(AnnouncementCloseEvent.class)
// public void onAnnouncementCloseEvent(AnnouncementCloseEvent event) {
// // 构建公告关闭的消息体
// AnnouncementCloseMessage message = new AnnouncementCloseMessage();
// message.setId(event.getId());
// String msg = JsonUtils.toJson(message);
//
// // 广播公告关闭信息
// MessageDO messageDO = new MessageDO().setMessageText(msg).setNeedBroadcast(true);
// messageDistributor.distribute(messageDO);
// }
//
// /**
// * 站内通知推送事件
// * @param event the StationNotifyPushEvent
// */
// @Async
// @EventListener(StationNotifyPushEvent.class)
// public void onAnnouncementPublishEvent(StationNotifyPushEvent event) {
// NotifyInfo notifyInfo = event.getNotifyInfo();
// List<SysUser> userList = event.getUserList();
// notifyInfoDelegateHandler.handle(userList, notifyInfo);
// }
//
// }

@ -0,0 +1,34 @@
package com.hccake.ballcat.admin.websocket.listener;
import com.hccake.ballcat.common.util.JsonUtils;
import com.hccake.ballcat.common.websocket.distribute.MessageDO;
import com.hccake.ballcat.common.websocket.distribute.MessageDistributor;
import com.hccake.ballcat.system.event.DictChangeEvent;
import com.hccake.ballcat.admin.websocket.message.DictChangeMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
@RequiredArgsConstructor
public class SystemWebsocketEventListener {
private final MessageDistributor messageDistributor;
/**
*
* @param event the `DictChangeEvent`
*/
@Async
@EventListener(DictChangeEvent.class)
public void onDictChangeEvent(DictChangeEvent event) {
// 构建字典修改的消息体
DictChangeMessage dictChangeMessage = new DictChangeMessage();
dictChangeMessage.setDictCode(event.getDictCode());
String msg = JsonUtils.toJson(dictChangeMessage);
// 广播修改信息
MessageDO messageDO = new MessageDO().setMessageText(msg).setNeedBroadcast(true);
messageDistributor.distribute(messageDO);
}
}

@ -0,0 +1,24 @@
package com.hccake.ballcat.admin.websocket.message;
import com.hccake.ballcat.common.websocket.message.JsonWebSocketMessage;
import lombok.Getter;
import lombok.Setter;
/**
* @author Hccake 2021/1/7
* @version 1.0
*/
@Getter
@Setter
public class AnnouncementCloseMessage extends JsonWebSocketMessage {
public AnnouncementCloseMessage() {
super("announcement-close");
}
/**
* ID
*/
private Long id;
}

@ -0,0 +1,26 @@
package com.hccake.ballcat.admin.websocket.message;
import com.hccake.ballcat.common.websocket.message.JsonWebSocketMessage;
import lombok.Getter;
import lombok.Setter;
/**
*
*
* @author Hccake 2021/1/5
* @version 1.0
*/
@Getter
@Setter
public class DictChangeMessage extends JsonWebSocketMessage {
public DictChangeMessage() {
super("dict-change");
}
/**
*
*/
private String dictCode;
}

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ad-distribute</artifactId>
<groupId>com.baiye</groupId>
<version>1.1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ad-distribute-admin</artifactId>
<packaging>pom</packaging>
<modules>
<module>admin-core</module>
<module>admin-websocket</module>
</modules>
<!-- 不生成target文件 -->
<build>
<defaultGoal>install</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>clean</id>
<phase>none</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ad-distribute-common</artifactId>
<groupId>com.baiye</groupId>
<version>1.1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-core</artifactId>
<packaging>jar</packaging>
<dependencies>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-poi</artifactId>
</dependency>
<!--json模块-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>com.baiye</groupId>
<artifactId>common-model</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.baiye</groupId>
<artifactId>common-util</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>3.0.3</version>
<scope>test</scope>
</dependency>
<!-- slf4j日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- web相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,26 @@
package com.hccake.ballcat.common.core.compose;
/**
* , (: spring bean) 便
* <p>
* spring
* </p>
* <p>
* 线线
* </p>
*
* @author lingting 2022/10/15 17:55
*/
public interface ContextComponent {
/**
* , 线线
*/
void onApplicationStart();
/**
* , 线
*/
void onApplicationStop();
}

@ -0,0 +1,32 @@
package com.hccake.ballcat.common.core.constant;
/**
* @author Hccake 2020/6/9 17:17
*/
public final class GlobalConstants {
private GlobalConstants() {
}
/**
* 使 1 0 0
*
*/
public static final Long NOT_DELETED_FLAG = 0L;
/**
*
*/
public static final String ENV_PROD = "prod";
/**
* ID
*/
public static final Integer TREE_ROOT_ID = 0;
/**
* ID
*/
public static final Long TREE_ROOT_ID_LONG = Long.valueOf(TREE_ROOT_ID);
}

@ -0,0 +1,28 @@
package com.hccake.ballcat.common.core.constant;
/**
* @author Hccake
* @version 1.0
* @date 2019/10/31 11:55
*/
public final class HeaderConstants {
private HeaderConstants() {
}
/**
*
*/
public static final String REQ_TIME = "reqTime";
/**
* sign
*/
public static final String SIGN = "sign";
/**
* SECRET_ID
*/
public static final String SECRET_ID = "secretId";
}

@ -0,0 +1,100 @@
package com.hccake.ballcat.common.core.constant;
import com.hccake.ballcat.common.core.https.CompatibleSSLFactory;
import com.hccake.ballcat.common.core.https.SSLSocketFactoryInitException;
import lombok.experimental.UtilityClass;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* @author lingting
*/
@UtilityClass
@SuppressWarnings("java:S4830")
public class HttpsConstants {
public static final String SSL = "SSL";
public static final String SSL_V2 = "SSLv2";
public static final String SSL_V3 = "SSLv3";
public static final String TLS = "TLS";
public static final String TLS_V1 = "TLSv1";
public static final String TLS_V11 = "TLSv1.1";
public static final String TLS_V12 = "TLSv1.2";
public static final String DALVIK = "dalvik";
public static final String VM_NAME = "java.vm.name";
/**
* AndroidSSL访
*/
private static final String[] ANDROID_PROTOCOLS = { SSL_V3, TLS_V1, TLS_V11, TLS_V12 };
/**
*
*/
@SuppressWarnings("java:S5527")
public static final HostnameVerifier HOSTNAME_VERIFIER = (s, sslSession) -> true;
public static final KeyManager[] KEY_MANAGERS = null;
public static final X509TrustManager TRUST_MANAGER;
public static final TrustManager[] TRUST_MANAGERS;
static {
TRUST_MANAGER = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
//
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
//
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
TRUST_MANAGERS = new TrustManager[] { TRUST_MANAGER };
}
/**
* SSLSocketFactory
*/
public static final SSLSocketFactory SSF;
static {
try {
if (DALVIK.equalsIgnoreCase(System.getProperty(VM_NAME))) {
// 兼容android低版本SSL连接
SSF = new CompatibleSSLFactory(ANDROID_PROTOCOLS);
}
else {
SSF = new CompatibleSSLFactory();
}
}
catch (KeyManagementException | NoSuchAlgorithmException e) {
throw new SSLSocketFactoryInitException("ssf 创建失败!", e);
}
}
}

@ -0,0 +1,19 @@
package com.hccake.ballcat.common.core.constant;
/**
* MDC
*
* @author hccake
* @since 1.3.1
*/
public final class MDCConstants {
private MDCConstants() {
}
/**
* ID
*/
public static final String TRACE_ID_KEY = "traceId";
}

@ -0,0 +1,35 @@
package com.hccake.ballcat.common.core.constant.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Boolean
*
* @author Hccake 2020/4/6 21:52
*/
@AllArgsConstructor
public enum BooleanEnum {
/**
*
*/
TRUE(true, 1),
/**
*
*/
FALSE(false, 0);
private final Boolean booleanValue;
private final Integer intValue;
public Boolean booleanValue() {
return booleanValue;
}
public Integer intValue() {
return intValue;
}
}

@ -0,0 +1,25 @@
package com.hccake.ballcat.common.core.constant.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*
*
* @author Hccake
*/
@Getter
@AllArgsConstructor
public enum ImportModeEnum {
/**
*
*/
SKIP_EXISTING,
/**
*
*/
OVERWRITE_EXISTING;
}

@ -0,0 +1,57 @@
package com.hccake.ballcat.common.core.exception;
import cn.hutool.core.text.CharSequenceUtil;
import com.hccake.ballcat.common.model.result.ResultCode;
import lombok.Getter;
/**
*
*
* @author Hccake
*/
@Getter
public class BusinessException extends RuntimeException {
private final String message;
private final int code;
public BusinessException(ResultCode resultCode) {
super(resultCode.getMessage());
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
/**
* format
*/
public BusinessException(ResultCode resultCode, Object... args) {
this(resultCode.getCode(), CharSequenceUtil.format(resultCode.getMessage(), args));
}
public BusinessException(ResultCode resultCode, Throwable e) {
super(resultCode.getMessage(), e);
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
/**
* format
*/
public BusinessException(ResultCode resultCode, Throwable e, Object... args) {
this(resultCode.getCode(), CharSequenceUtil.format(resultCode.getMessage(), args), e);
}
public BusinessException(int code, String message) {
super(message);
this.message = message;
this.code = code;
}
public BusinessException(int code, String message, Throwable e) {
super(message, e);
this.message = message;
this.code = code;
}
}

@ -0,0 +1,30 @@
package com.hccake.ballcat.common.core.exception;
import com.hccake.ballcat.common.model.result.SystemResultCode;
/**
* sql
*
* @author Hccake
* @version 1.0
* @date 2019/10/19 16:52
*/
public class SqlCheckedException extends BusinessException {
public SqlCheckedException(SystemResultCode systemResultMsg) {
super(systemResultMsg);
}
public SqlCheckedException(SystemResultCode systemResultMsg, Throwable e) {
super(systemResultMsg, e);
}
public SqlCheckedException(int code, String msg) {
super(code, msg);
}
public SqlCheckedException(int code, String msg, Throwable e) {
super(code, msg, e);
}
}

@ -0,0 +1,18 @@
package com.hccake.ballcat.common.core.exception.handler;
/**
*
*
* @author Hccake
* @version 1.0
* @date 2019/10/18 17:05
*/
public interface GlobalExceptionHandler {
/**
* ES
* @param throwable
*/
void handle(Throwable throwable);
}

@ -0,0 +1,95 @@
package com.hccake.ballcat.common.core.https;
import com.hccake.ballcat.common.core.constant.HttpsConstants;
import com.hccake.ballcat.common.util.ArrayUtils;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
* android 使
*
* @author lingting
*/
public class CompatibleSSLFactory extends SSLSocketFactory {
private final String[] protocols;
private final SSLSocketFactory factory;
public CompatibleSSLFactory(String... protocols) throws NoSuchAlgorithmException, KeyManagementException {
this(HttpsConstants.TLS, HttpsConstants.KEY_MANAGERS, HttpsConstants.TRUST_MANAGERS, new SecureRandom(),
protocols);
}
public CompatibleSSLFactory(String protocol, KeyManager[] keyManagers, TrustManager[] trustManagers,
SecureRandom secureRandom, String... protocols) throws NoSuchAlgorithmException, KeyManagementException {
this.protocols = protocols;
final SSLContext context = SSLContext.getInstance(protocol);
context.init(keyManagers, trustManagers, secureRandom);
this.factory = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return factory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return factory.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
return enabledProtocols(factory.createSocket());
}
@Override
public Socket createSocket(Socket socket, InputStream inputStream, boolean b) throws IOException {
return enabledProtocols(factory.createSocket(socket, inputStream, b));
}
@Override
public Socket createSocket(Socket socket, String s, int i, boolean b) throws IOException {
return enabledProtocols(factory.createSocket(socket, s, i, b));
}
@Override
public Socket createSocket(String s, int i) throws IOException {
return enabledProtocols(factory.createSocket(s, i));
}
@Override
public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) throws IOException {
return enabledProtocols(factory.createSocket(s, i, inetAddress, i1));
}
@Override
public Socket createSocket(InetAddress inetAddress, int i) throws IOException {
return enabledProtocols(factory.createSocket(inetAddress, i));
}
@Override
public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) throws IOException {
return enabledProtocols(factory.createSocket(inetAddress, i, inetAddress1, i1));
}
private Socket enabledProtocols(Socket socket) {
if (!ArrayUtils.isEmpty(protocols) && socket instanceof SSLSocket) {
((SSLSocket) socket).setEnabledProtocols(protocols);
}
return socket;
}
}

@ -0,0 +1,12 @@
package com.hccake.ballcat.common.core.https;
/**
* @author lingting 2023/2/1 14:29
*/
public class SSLSocketFactoryInitException extends RuntimeException {
public SSLSocketFactoryInitException(String message, Throwable cause) {
super(message, cause);
}
}

@ -0,0 +1,45 @@
package com.hccake.ballcat.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;
/**
* java8
*
* @see com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
* @author Hccake
*/
public class CustomJavaTimeModule extends SimpleModule {
public CustomJavaTimeModule() {
super(PackageVersion.VERSION);
this.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE));
this.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ISO_LOCAL_TIME));
this.addSerializer(Instant.class, InstantSerializer.INSTANCE);
this.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE));
this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ISO_LOCAL_TIME));
this.addDeserializer(Instant.class, InstantDeserializer.INSTANT);
}
}

@ -0,0 +1,32 @@
package com.hccake.ballcat.common.core.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.io.Serializable;
/**
* Array null []
*
* @author Hccake
* @version 1.0
* @date 2019/10/17 23:17
*/
public class NullArrayJsonSerializer extends JsonSerializer<Object> implements Serializable {
private static final long serialVersionUID = 1L;
@Override
public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
if (value == null) {
jsonGenerator.writeStartArray();
jsonGenerator.writeEndArray();
}
else {
jsonGenerator.writeObject(value);
}
}
}

@ -0,0 +1,32 @@
package com.hccake.ballcat.common.core.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.io.Serializable;
/**
* Map Map null {}
*
* @author Hccake
* @version 1.0
* @date 2019/10/17 23:17
*/
public class NullMapJsonSerializer extends JsonSerializer<Object> implements Serializable {
private static final long serialVersionUID = 1L;
@Override
public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
if (value == null) {
jsonGenerator.writeStartObject();
jsonGenerator.writeEndObject();
}
else {
jsonGenerator.writeObject(value);
}
}
}

@ -0,0 +1,120 @@
package com.hccake.ballcat.common.core.jackson;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
import com.fasterxml.jackson.databind.ser.SerializerFactory;
import java.util.Collection;
import java.util.Map;
/**
* <p>
* Null
* </p>
*
* <ul>
* <li>String null ''
* <li>null []
* <li>Map null {}
* <ul/>
*
* @author hccake
*/
public class NullSerializerProvider extends DefaultSerializerProvider {
private static final long serialVersionUID = 1L;
public NullSerializerProvider() {
super();
}
public NullSerializerProvider(NullSerializerProvider src) {
super(src);
}
protected NullSerializerProvider(SerializerProvider src, SerializationConfig config, SerializerFactory f) {
super(src, config, f);
}
/**
* null array listset '[]'
*/
private final JsonSerializer<Object> nullArrayJsonSerializer = new NullArrayJsonSerializer();
/**
* null Map '{}'
*/
private final JsonSerializer<Object> nullMapJsonSerializer = new NullMapJsonSerializer();
/**
* null ''
*/
private final JsonSerializer<Object> nullStringJsonSerializer = new NullStringJsonSerializer();
@Override
public DefaultSerializerProvider copy() {
if (getClass() != NullSerializerProvider.class) {
return super.copy();
}
return new NullSerializerProvider(this);
}
@Override
public NullSerializerProvider createInstance(SerializationConfig config, SerializerFactory jsf) {
return new NullSerializerProvider(this, config, jsf);
}
@Override
public JsonSerializer<Object> findNullValueSerializer(BeanProperty property) throws JsonMappingException {
JavaType propertyType = property.getType();
if (isStringType(propertyType)) {
return this.nullStringJsonSerializer;
}
else if (isArrayOrCollectionTrype(propertyType)) {
return this.nullArrayJsonSerializer;
}
else if (isMapType(propertyType)) {
return this.nullMapJsonSerializer;
}
else {
return super.findNullValueSerializer(property);
}
}
/**
* String
* @param type JavaType
* @return boolean
*/
private boolean isStringType(JavaType type) {
Class<?> clazz = type.getRawClass();
return String.class.isAssignableFrom(clazz);
}
/**
* Map
* @param type JavaType
* @return boolean
*/
private boolean isMapType(JavaType type) {
Class<?> clazz = type.getRawClass();
return Map.class.isAssignableFrom(clazz);
}
/**
*
* @param type JavaType
* @return boolean
*/
private boolean isArrayOrCollectionTrype(JavaType type) {
Class<?> clazz = type.getRawClass();
return clazz.isArray() || Collection.class.isAssignableFrom(clazz);
}
}

@ -0,0 +1,26 @@
package com.hccake.ballcat.common.core.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.io.Serializable;
/**
* jackson NULL ""
*
* @author Hccake
* @version 1.0
* @date 2019/10/17 22:19
*/
public class NullStringJsonSerializer extends JsonSerializer<Object> implements Serializable {
private static final long serialVersionUID = 1L;
@Override
public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
jsonGenerator.writeString("");
}
}

@ -0,0 +1,91 @@
package com.hccake.ballcat.common.core.lock;
import lombok.Getter;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author lingting 2023-04-22 10:55
*/
@Getter
public class JavaReentrantLock {
/**
*
*/
protected final ReentrantLock lock = new ReentrantLock();
/**
* 线
*/
protected final Condition defaultCondition = lock.newCondition();
public Condition newCondition() {
return getLock().newCondition();
}
public void lock() {
getLock().lock();
}
public void lockInterruptibly() throws InterruptedException {
getLock().lockInterruptibly();
}
public void runLock(Runnable runnable) throws InterruptedException {
ReentrantLock reentrantLock = getLock();
reentrantLock.lock();
try {
runnable.run();
}
finally {
reentrantLock.unlock();
}
}
public void runLockInterruptibly(Runnable runnable) throws InterruptedException {
ReentrantLock reentrantLock = getLock();
reentrantLock.lockInterruptibly();
try {
runnable.run();
}
finally {
reentrantLock.unlock();
}
}
public void unlock() {
getLock().unlock();
}
public void signal() {
getDefaultCondition().signal();
}
public void signalAll() {
getDefaultCondition().signalAll();
}
public void await() throws InterruptedException {
getDefaultCondition().await();
}
/**
* @return
*/
public boolean await(long time, TimeUnit timeUnit) throws InterruptedException {
return getDefaultCondition().await(time, timeUnit);
}
/**
* @author lingting 2023-04-22 11:35
*/
public interface Runnable {
void run() throws InterruptedException;
}
}

@ -0,0 +1,282 @@
package com.hccake.ballcat.common.core.markdown;
import cn.hutool.core.convert.Convert;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.hccake.ballcat.common.util.json.JacksonJsonToolAdapter;
import lombok.SneakyThrows;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
* markdown
*
* @author lingting 2020/6/10 22:43
*/
public class MarkdownBuilder {
public static final String TITLE_PREFIX = "#";
public static final String QUOTE_PREFIX = "> ";
public static final String CODE_PREFIX = "``` ";
public static final String CODE_SUFFIX = "```";
public static final String BOLD_PREFIX = "**";
public static final String ITALIC_PREFIX = "*";
public static final String UNORDERED_LIST_PREFIX = "- ";
public static final String ORDER_LIST_PREFIX = ". ";
/**
*
*/
private final List<String> content = new ArrayList<>();
/**
*
*/
private StringBuilder lineTextBuilder;
public MarkdownBuilder() {
this.lineTextBuilder = new StringBuilder();
}
/**
*
* @param content
*/
public MarkdownBuilder append(Object content) {
lineTextBuilder.append(toString(content));
return this;
}
/**
*
* @param content
*/
public MarkdownBuilder orderList(Object content) {
// 获取最后一个字符串
String tmp = "";
if (!this.content.isEmpty()) {
tmp = this.content.get(this.content.size() - 1);
}
// 索引
int index = 1;
// 校验 是否 为有序列表行的正则
String isOrderListPattern = "^\\d\\. .*";
if (Pattern.matches(isOrderListPattern, tmp)) {
// 如果是数字开头
index = Convert.toInt(tmp.substring(0, tmp.indexOf(ORDER_LIST_PREFIX) - 1));
}
return orderList(index, content);
}
/**
*
* @param index
* @param content
*/
public MarkdownBuilder orderList(int index, Object content) {
lineBreak();
lineTextBuilder.append(index).append(ORDER_LIST_PREFIX).append(toString(content));
return this;
}
/**
* - item1 - item2
*/
public MarkdownBuilder unorderedList(Object content) {
// 换行
lineBreak();
lineTextBuilder.append(UNORDERED_LIST_PREFIX).append(toString(content));
return this;
}
/**
*
* @param url
*/
public MarkdownBuilder pic(String url) {
return pic("", url);
}
/**
*
* @param title
* @param url
*/
public MarkdownBuilder pic(Object title, String url) {
lineTextBuilder.append("![").append(title).append("](").append(url).append(")");
return this;
}
/**
*
* @param title
* @param url http
*/
public MarkdownBuilder link(Object title, String url) {
lineTextBuilder.append("[").append(title).append("](").append(url).append(")");
return this;
}
/**
*
*/
public MarkdownBuilder italic(Object content) {
lineTextBuilder.append(ITALIC_PREFIX).append(toString(content)).append(ITALIC_PREFIX);
return this;
}
/**
*
*/
public MarkdownBuilder bold(Object content) {
lineTextBuilder.append(BOLD_PREFIX).append(toString(content)).append(BOLD_PREFIX);
return this;
}
/**
* >
* @param content
*/
public MarkdownBuilder quote(Object... content) {
lineBreak();
lineTextBuilder.append(QUOTE_PREFIX);
for (Object o : content) {
lineTextBuilder.append(toString(o));
}
return this;
}
/**
* , ,
*/
public MarkdownBuilder quoteBreak(Object... content) {
// 当前行引用内容
quote(content);
// 空引用行
return quote();
}
/**
*
*/
public MarkdownBuilder code(String type, Object... code) {
lineBreak();
lineTextBuilder.append(CODE_PREFIX).append(type);
lineBreak();
for (Object o : code) {
lineTextBuilder.append(toString(o));
}
lineBreak();
lineTextBuilder.append(CODE_SUFFIX);
return lineBreak();
}
/**
*
*/
public MarkdownBuilder json(Object obj) {
String json;
if (obj instanceof String) {
json = (String) obj;
}
else {
json = multiJson(obj);
}
return code("json", json);
}
@SneakyThrows
private String multiJson(Object obj) {
ObjectMapper mapper = JacksonJsonToolAdapter.getMapper().copy().enable(SerializationFeature.INDENT_OUTPUT);
return mapper.writeValueAsString(obj);
}
/**
*
*/
public MarkdownBuilder forceLineBreak() {
content.add(lineTextBuilder.toString());
lineTextBuilder = new StringBuilder();
return this;
}
/**
* 0
*/
public MarkdownBuilder lineBreak() {
if (lineTextBuilder.length() != 0) {
return forceLineBreak();
}
return this;
}
/**
* i
*
* @author lingting 2020-06-10 22:55:39
*/
private MarkdownBuilder title(int i, Object content) {
// 如果当前操作行已有字符,需要换行
lineBreak();
for (int j = 0; j < i; j++) {
lineTextBuilder.append(TITLE_PREFIX);
}
this.content.add(lineTextBuilder.append(" ").append(toString(content)).toString());
lineTextBuilder = new StringBuilder();
return this;
}
public MarkdownBuilder title1(Object text) {
return title(1, text);
}
public MarkdownBuilder title2(Object text) {
return title(2, text);
}
public MarkdownBuilder title3(Object text) {
return title(3, text);
}
public MarkdownBuilder title4(Object text) {
return title(4, text);
}
public MarkdownBuilder title5(Object text) {
return title(5, text);
}
String toString(Object o) {
if (o == null) {
return "";
}
return o.toString();
}
@Override
public String toString() {
return build();
}
/**
* Markdown
*/
public String build() {
lineBreak();
StringBuilder res = new StringBuilder();
content.forEach(line -> res.append(line).append(" \n"));
return res.toString();
}
}

@ -0,0 +1,28 @@
package com.hccake.ballcat.common.core.request.wrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.Map;
/**
* parameterMap
*
* @author Hccake
* @version 1.0
* @date 2019/10/17 21:57
*/
public class ModifyParamMapRequestWrapper extends HttpServletRequestWrapper {
private final Map<String, String[]> parameterMap;
public ModifyParamMapRequestWrapper(HttpServletRequest request, Map<String, String[]> parameterMap) {
super(request);
this.parameterMap = parameterMap;
}
@Override
public Map<String, String[]> getParameterMap() {
return this.parameterMap;
}
}

@ -0,0 +1,89 @@
package com.hccake.ballcat.common.core.request.wrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StreamUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
/**
* Request body
*
* @author Hccake
* @version 1.0
* @date 2019/10/16 10:14
*/
@Slf4j
public class RepeatBodyRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
private final Map<String, String[]> parameterMap;
public RepeatBodyRequestWrapper(HttpServletRequest request) {
super(request);
this.parameterMap = super.getParameterMap();
this.body = getByteBody(request);
}
@Override
public BufferedReader getReader() {
return ObjectUtils.isEmpty(body) ? null : new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
// doNoting
}
@Override
public int read() {
return byteArrayInputStream.read();
}
};
}
private static byte[] getByteBody(HttpServletRequest request) {
byte[] body = new byte[0];
try {
body = StreamUtils.copyToByteArray(request.getInputStream());
}
catch (IOException e) {
log.error("解析流中数据异常", e);
}
return body;
}
/**
* getParameterMap() undertow body
* @see io.undertow.servlet.spec.HttpServletRequestImpl#readStarted
* @return Map<String, String[]> parameterMap
*/
@Override
public Map<String, String[]> getParameterMap() {
return parameterMap;
}
}

@ -0,0 +1,64 @@
package com.hccake.ballcat.common.core.spring;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
/**
* @author lingting 2022/10/22 15:11
*/
public interface BallcatBeanPostProcessor extends BeanPostProcessor, Ordered {
/**
* bean
* @param bean bean
* @param beanName bean
* @param isBefore true, false
* @return boolean true bean
*/
boolean isProcess(Object bean, String beanName, boolean isBefore);
/**
*
* @param bean bean
* @param beanName bean
* @return java.lang.Object
*/
default Object postProcessBefore(Object bean, String beanName) {
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
/**
*
* @param bean bean
* @param beanName bean
* @return java.lang.Object
*/
default Object postProcessAfter(Object bean, String beanName) {
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
@Override
default int getOrder() {
// 默认优先级
return Ordered.HIGHEST_PRECEDENCE;
}
@Override
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (isProcess(bean, beanName, true)) {
return postProcessBefore(bean, beanName);
}
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (isProcess(bean, beanName, false)) {
return postProcessAfter(bean, beanName);
}
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}

@ -0,0 +1,24 @@
package com.hccake.ballcat.common.core.spring.compose;
import com.hccake.ballcat.common.core.compose.ContextComponent;
import com.hccake.ballcat.common.core.spring.BallcatBeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* @author lingting 2022/10/22 15:10
*/
@Component
public class ContextComposeBeanPostProcessor implements BallcatBeanPostProcessor {
@Override
public boolean isProcess(Object bean, String beanName, boolean isBefore) {
return bean != null && ContextComponent.class.isAssignableFrom(bean.getClass());
}
@Override
public Object postProcessAfter(Object bean, String beanName) {
((ContextComponent) bean).onApplicationStart();
return bean;
}
}

@ -0,0 +1,38 @@
package com.hccake.ballcat.common.core.spring.compose;
import com.hccake.ballcat.common.core.compose.ContextComponent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author lingting 2022/10/22 17:45
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class SpringContextClosed implements ApplicationListener<ContextClosedEvent> {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
log.debug("spring context closed");
ApplicationContext applicationContext = event.getApplicationContext();
// 上下文容器停止
contextComponentStop(applicationContext);
}
private static void contextComponentStop(ApplicationContext applicationContext) {
Map<String, ContextComponent> contextComponentMap = applicationContext.getBeansOfType(ContextComponent.class);
for (Map.Entry<String, ContextComponent> entry : contextComponentMap.entrySet()) {
entry.getValue().onApplicationStop();
}
}
}

@ -0,0 +1,39 @@
package com.hccake.ballcat.common.core.thread;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* 线
*
* @author lingting
*/
@Slf4j
public abstract class AbstractBlockingQueueThread<T> extends AbstractQueueThread<T> {
private final BlockingQueue<T> queue = new LinkedBlockingQueue<>();
@Override
public void put(T t) {
if (t != null) {
try {
queue.put(t);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
catch (Exception e) {
log.error("{} put Object error, param: {}", this.getClass().toString(), t, e);
}
}
}
@Override
protected T poll(long time) throws InterruptedException {
return queue.poll(time, TimeUnit.MILLISECONDS);
}
}

@ -0,0 +1,102 @@
package com.hccake.ballcat.common.core.thread;
import com.hccake.ballcat.common.core.lock.JavaReentrantLock;
import lombok.extern.slf4j.Slf4j;
import java.util.Comparator;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* @author lingting 2023-04-22 10:39
*/
@Slf4j
@SuppressWarnings("java:S1066")
public abstract class AbstractDynamicTimer<T> extends AbstractThreadContextComponent {
private final JavaReentrantLock lock = new JavaReentrantLock();
protected final PriorityBlockingQueue<T> queue = new PriorityBlockingQueue<>(defaultCapacity(), comparator());
public abstract Comparator<T> comparator();
/**
*
*/
protected int defaultCapacity() {
return 11;
}
/**
*
* @param t
* @return , :
*/
protected abstract long sleepTime(T t);
public void put(T t) {
if (t == null) {
return;
}
try {
lock.runLockInterruptibly(() -> {
queue.add(t);
lock.signalAll();
});
}
catch (InterruptedException e) {
interrupt();
}
catch (Exception e) {
log.error("{} put error, param: {}", this.getClass().toString(), t, e);
}
}
@Override
public void run() {
init();
while (isRun()) {
try {
T t = pool();
lock.runLockInterruptibly(() -> {
if (t == null) {
lock.await(24, TimeUnit.HOURS);
return;
}
long sleepTime = sleepTime(t);
// 需要休眠
if (sleepTime > 0) {
// 如果是被唤醒
if (lock.await(sleepTime, TimeUnit.MILLISECONDS)) {
put(t);
return;
}
}
process(t);
});
}
catch (InterruptedException e) {
interrupt();
shutdown();
}
catch (Exception e) {
log.error("类: {}; 线程: {}; 运行异常! ", getSimpleName(), getId(), e);
}
}
}
protected T pool() {
return queue.poll();
}
protected abstract void process(T t);
protected void shutdown() {
log.warn("类: {}; 线程: {}; 被中断! 剩余数据: {}", getSimpleName(), getId(), queue.size());
}
}

@ -0,0 +1,212 @@
package com.hccake.ballcat.common.core.thread;
import com.hccake.ballcat.common.core.compose.ContextComponent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 线
*
* @author lingting 2021/3/2 15:07
*/
@Slf4j
public abstract class AbstractQueueThread<E> extends Thread implements ContextComponent {
/**
*
*/
private static final int DEFAULT_BATCH_SIZE = 500;
/**
* 30
*/
private static final long DEFAULT_BATCH_TIMEOUT_MS = 30 * 1000L;
/**
*
*/
private static final long POLL_TIMEOUT_MS = 5 * 1000L;
/**
*
* @return long
*/
public int getBatchSize() {
return DEFAULT_BATCH_SIZE;
}
/**
*
* @return
*/
public long getBatchTimeout() {
return DEFAULT_BATCH_TIMEOUT_MS;
}
/**
*
* @return
*/
public long getPollTimeout() {
return POLL_TIMEOUT_MS;
}
/**
*
* @param e
*/
public abstract void put(E e);
/**
*
*/
protected void init() {
}
/**
*
* @return boolean true
*/
public boolean isRun() {
// 未被中断表示可以继续运行
return !isInterrupted();
}
/**
*
*/
protected void preProcess() {
}
/**
*
* @param time ,
* @return E
* @throws InterruptedException 线
*/
protected abstract E poll(long time) throws InterruptedException;
/**
*
* @param list
* @param e
*/
protected void receiveProcess(List<E> list, E e) {
list.add(e);
}
/**
*
* @param list
* @throws Exception
*/
@SuppressWarnings("java:S112")
protected abstract void process(List<E> list) throws Exception;
@Override
@SuppressWarnings("java:S1181")
public void run() {
init();
List<E> list;
while (isRun()) {
list = new ArrayList<>(getBatchSize());
try {
preProcess();
fillList(list);
if (!isRun()) {
shutdown(list);
}
else {
process(list);
}
}
catch (InterruptedException e) {
shutdown(list);
Thread.currentThread().interrupt();
}
catch (Exception e) {
error(e, list);
}
// Throwable 异常直接结束. 这里捕获用来保留信息. 方便排查问题
catch (Throwable t) {
log.error("线程队列运行异常!", t);
throw t;
}
}
}
protected void fillList(List<E> list) {
long timestamp = 0;
int count = 0;
while (count < getBatchSize()) {
E e = poll();
if (e != null) {
// 第一次插入数据
if (count++ == 0) {
// 记录时间
timestamp = System.currentTimeMillis();
}
receiveProcess(list, e);
}
// 无法继续运行
final boolean isBreak = !isRun()
// 或者 已有数据且超过设定的等待时间
|| (!CollectionUtils.isEmpty(list) && System.currentTimeMillis() - timestamp >= getBatchTimeout());
if (isBreak) {
break;
}
}
}
public E poll() {
E e = null;
try {
e = poll(getPollTimeout());
}
catch (InterruptedException ex) {
log.error("{} 类的poll线程被中断!id: {}", getClass().getSimpleName(), getId());
interrupt();
}
return e;
}
/**
*
* @param e
* @param list
*/
protected abstract void error(Throwable e, List<E> list);
/**
* 线. .
* @param list
*/
protected void shutdown(List<E> list) {
log.warn("{} 线程: {} 被关闭. 数据:{}", this.getClass().getSimpleName(), getId(), list);
}
@Override
public void onApplicationStart() {
// 默认配置线程名. 用来方便查询
setName(this.getClass().getSimpleName());
if (!this.isAlive()) {
this.start();
}
}
@Override
public void onApplicationStop() {
log.warn("{} 线程: {}; 开始关闭!", getClass().getSimpleName(), getId());
// 通过中断线程唤醒当前线程. 让线程进入 shutdownHandler 方法处理数据
interrupt();
}
}

@ -0,0 +1,41 @@
package com.hccake.ballcat.common.core.thread;
import com.hccake.ballcat.common.core.compose.ContextComponent;
import lombok.extern.slf4j.Slf4j;
/**
* @author lingting 2023-04-22 10:40
*/
@Slf4j
public abstract class AbstractThreadContextComponent extends Thread implements ContextComponent {
protected void init() {
}
public boolean isRun() {
return !isInterrupted() && isAlive();
}
@Override
public void onApplicationStart() {
setName(getClass().getSimpleName());
if (!isAlive()) {
start();
}
}
@Override
public void onApplicationStop() {
log.warn("{} 线程: {}; 开始关闭!", getClass().getSimpleName(), getId());
interrupt();
}
public String getSimpleName() {
return getClass().getSimpleName();
}
@Override
public abstract void run();
}

@ -0,0 +1,85 @@
package com.hccake.ballcat.common.core.thread;
import com.hccake.ballcat.common.core.compose.ContextComponent;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
/**
* @author lingting 2022/6/27 20:26
*/
@Slf4j
public abstract class AbstractTimer extends Thread implements ContextComponent {
/**
* , :
*/
public long getTimeout() {
return TimeUnit.SECONDS.toMillis(30);
}
public boolean isRun() {
return !isInterrupted();
}
/**
*
*/
protected void init() {
}
/**
*
*/
protected abstract void process();
/**
* 线.
*/
protected void shutdown() {
}
protected void error(Exception e) {
log.error("{} 类 线程: {} 出现异常!", getClass().getSimpleName(), getId(), e);
}
@Override
public void run() {
init();
while (isRun()) {
try {
process();
// 已经停止运行, 结束
if (!isRun()) {
shutdown();
return;
}
Thread.sleep(getTimeout());
}
catch (InterruptedException e) {
interrupt();
shutdown();
}
catch (Exception e) {
error(e);
}
}
}
@Override
public void onApplicationStart() {
setName(getClass().getSimpleName());
if (!isAlive()) {
start();
}
}
@Override
public void onApplicationStop() {
log.warn("{} 线程: {}; 开始关闭!", getClass().getSimpleName(), getId());
interrupt();
}
}

@ -0,0 +1,406 @@
package com.hccake.ballcat.common.core.util;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.poi.excel.BigExcelWriter;
import cn.hutool.poi.excel.ExcelUtil;
import com.hccake.ballcat.common.core.exception.BusinessException;
import com.hccake.ballcat.common.model.result.BaseResultCode;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.*;
/**
* File hutool
*/
public class FileUtil extends cn.hutool.core.io.FileUtil {
private static final Logger log = LoggerFactory.getLogger(FileUtil.class);
/**
* <br>
* windows Linux , windows \\==\ <pre>
* java.io.tmpdir
* windows : C:\Users/xxx\AppData\Local\Temp\
* linux: /temp
* </pre>
*/
public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator;
/**
* GB
*/
private static final int GB = 1024 * 1024 * 1024;
/**
* MB
*/
private static final int MB = 1024 * 1024;
/**
* KB
*/
private static final int KB = 1024;
/**
*
*/
private static final String PRE_FILE = "out_task_";
/**
* (,...) [使]
*/
private static final String SEPARATOR = "_";
/**
* (,...) [使]
*/
private static final int RANDOM_STRING_LENGTH = 6;
/**
*
*/
private static final DecimalFormat DF = new DecimalFormat("0.00");
/**
* MultipartFileFile
*/
public static File toFile(MultipartFile multipartFile) {
// 获取文件名
String fileName = multipartFile.getOriginalFilename();
// 获取文件后缀
String prefix = "." + getExtensionName(fileName);
File file = null;
try {
// 用uuid作为文件名防止生成的临时文件重复
file = File.createTempFile(IdUtil.simpleUUID(), prefix);
// MultipartFile to File
multipartFile.transferTo(file);
}
catch (IOException e) {
log.error(e.getMessage(), e);
}
return file;
}
/**
* .
*/
public static String getExtensionName(String filename) {
if ((filename != null) && (filename.length() > 0)) {
int dot = filename.lastIndexOf('.');
if ((dot > -1) && (dot < (filename.length() - 1))) {
return filename.substring(dot + 1);
}
}
return filename;
}
/**
* Java
*/
public static String getFileNameNoEx(String filename) {
if ((filename != null) && (filename.length() > 0)) {
int dot = filename.lastIndexOf('.');
if ((dot > -1) && (dot < (filename.length()))) {
return filename.substring(0, dot);
}
}
return filename;
}
/**
*
*/
public static String getSize(long size) {
String resultSize;
if (size / GB >= 1) {
// 如果当前Byte的值大于等于1GB
resultSize = DF.format(size / (float) GB) + "GB ";
}
else if (size / MB >= 1) {
// 如果当前Byte的值大于等于1MB
resultSize = DF.format(size / (float) MB) + "MB ";
}
else if (size / KB >= 1) {
// 如果当前Byte的值大于等于1KB
resultSize = DF.format(size / (float) KB) + "KB ";
}
else {
resultSize = size + "B ";
}
return resultSize;
}
/**
* inputStream File
*/
public static File inputStreamToFile(InputStream ins, String name) throws Exception {
File file = new File(SYS_TEM_DIR + name);
if (file.exists()) {
return file;
}
OutputStream os = new FileOutputStream(file);
int bytesRead;
int len = 8192;
byte[] buffer = new byte[len];
while ((bytesRead = ins.read(buffer, 0, len)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
ins.close();
return file;
}
/**
*
*/
public static File upload(MultipartFile file, String filePath) {
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssS");
String name = getFileNameNoEx(file.getOriginalFilename());
String suffix = getExtensionName(file.getOriginalFilename());
String nowStr = "-" + format.format(date);
try {
String fileName = name + nowStr + "." + suffix;
String path = filePath + fileName;
// getCanonicalFile 可解析正确各种路径
File dest = new File(path).getCanonicalFile();
// 检测是否存在目录
if (!dest.getParentFile().exists()) {
if (!dest.getParentFile().mkdirs()) {
System.out.println("was not successful.");
}
}
// 文件写入
file.transferTo(dest);
return dest;
}
catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
/**
* excel
*/
public static void downloadExcel(List<Map<String, Object>> list, HttpServletResponse response) throws IOException {
String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx";
File file = new File(tempPath);
BigExcelWriter writer = ExcelUtil.getBigWriter(file);
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(list, true);
// response为HttpServletResponse对象
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
// test.xls是弹出下载对话框的文件名不能为中文中文请自行编码
response.setHeader("Content-Disposition", "attachment;filename=file.xlsx");
ServletOutputStream out = response.getOutputStream();
// 终止后删除临时文件
file.deleteOnExit();
writer.flush(out, true);
// 此处记得关闭输出Servlet流
IoUtil.close(out);
}
/**
* excel
* @param file
* @param response
* @throws IOException
*/
public static void downloadExcel(File file, HttpServletResponse response) throws IOException {
BigExcelWriter writer = ExcelUtil.getBigWriter(file);
// response为HttpServletResponse对象
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
// test.xls是弹出下载对话框的文件名不能为中文中文请自行编码
response.setHeader("Content-Disposition", "attachment;filename=file.xlsx");
ServletOutputStream out = response.getOutputStream();
// 终止后删除临时文件
file.deleteOnExit();
writer.flush(out, true);
// 此处记得关闭输出Servlet流
IoUtil.close(out);
}
public static String getFileType(String type) {
String documents = "txt doc pdf ppt pps xlsx xls docx";
String music = "mp3 wav wma mpa ram ra aac aif m4a";
String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg";
String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg";
if (image.contains(type)) {
return "图片";
}
else if (documents.contains(type)) {
return "文档";
}
else if (music.contains(type)) {
return "音乐";
}
else if (video.contains(type)) {
return "视频";
}
else {
return "其他";
}
}
public static void checkSize(long maxSize, long size) {
// 1M
int len = 1024 * 1024;
if (size > (maxSize * len)) {
throw new BusinessException(BaseResultCode.FILE_UPLOAD_ERROR, "文件超出规定大小");
}
}
/**
*
*/
public static boolean check(File file1, File file2) {
String img1Md5 = getMd5(file1);
String img2Md5 = getMd5(file2);
return img1Md5.equals(img2Md5);
}
/**
*
*/
public static boolean check(String file1Md5, String file2Md5) {
return file1Md5.equals(file2Md5);
}
private static byte[] getByte(File file) {
// 得到文件长度
byte[] b = new byte[(int) file.length()];
try {
InputStream in = new FileInputStream(file);
try {
System.out.println(in.read(b));
}
catch (IOException e) {
log.error(e.getMessage(), e);
}
}
catch (FileNotFoundException e) {
log.error(e.getMessage(), e);
return null;
}
return b;
}
private static String getMd5(byte[] bytes) {
// 16进制字符
char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
try {
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(bytes);
byte[] md = mdTemp.digest();
int j = md.length;
char[] str = new char[j * 2];
int k = 0;
// 移位 输出字符串
for (byte byte0 : md) {
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
}
catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
public static String getMd5(File file) {
return getMd5(getByte(file));
}
/**
*
* @return
*/
public static String buildOnlyFileNameRule() {
String fileName = PRE_FILE + RandomUtil.randomString(RANDOM_STRING_LENGTH) + SEPARATOR
+ Instant.now().getEpochSecond();
return fileName;
}
/**
* MultipartFilefile
* @param multipartFile
* @return
*/
public static File multiToFile(MultipartFile multipartFile) {
// 选择用缓冲区来实现这个转换即使用java 创建的临时文件 使用 MultipartFile.transferto()方法 。
File file = null;
try {
String originalFilename = multipartFile.getOriginalFilename();
String[] filename = originalFilename.split("\\.");
file = File.createTempFile(filename[0], StrPool.DOT.concat(filename[1]));
multipartFile.transferTo(file);
file.deleteOnExit();
}
catch (IOException e) {
log.error(e.getMessage());
}
return file;
}
/**
* <h1></h1>
* @param dirFilePath
* @return
*/
public static List<File> getAllFile(String dirFilePath) {
if (StringUtils.isBlank(dirFilePath))
return null;
return getAllFile(new File(dirFilePath));
}
/**
* <h1></h1>
* @param dirFile
* @return
*/
public static List<File> getAllFile(File dirFile) {
// 如果文件夹不存在或着不是文件夹,则返回 null
if (Objects.isNull(dirFile) || !dirFile.exists() || dirFile.isFile())
return null;
File[] childrenFiles = dirFile.listFiles();
if (Objects.isNull(childrenFiles) || childrenFiles.length == 0)
return null;
List<File> files = new ArrayList<>();
for (File childFile : childrenFiles) {
// 如果时文件,直接添加到结果集合
if (childFile.isFile()) {
files.add(childFile);
}
else {
// 如果是文件夹,则将其内部文件添加进结果集合
List<File> cFiles = getAllFile(childFile);
if (Objects.isNull(cFiles) || cFiles.isEmpty())
continue;
files.addAll(cFiles);
}
}
return files;
}
}

@ -0,0 +1,109 @@
package com.hccake.ballcat.common.core.util;
import org.apache.commons.lang3.StringUtils;
import java.util.regex.Pattern;
/**
* @author Enzo
* @date : 2022/4/18
*/
public class MobileUtil {
/**
*
* 133,149,153,173,177,180,181,189,191,199,1349,1410,1700,1701,1702,193
**/
private static final String CHINA_TELECOM_PATTERN = "(?:^(?:\\+86)?1(?:33|49|53|7[37]|8[019]|9[139])\\d{8}$)|(?:^(?:\\+86)?1349\\d{7}$)|(?:^(?:\\+86)?1410\\d{7}$)|(?:^(?:\\+86)?170[0-2]\\d{7}$)";
/**
*
* 130,131,132,145,146,155,156,166,171,175,176,185,186,1704,1707,1708,1709
**/
private static final String CHINA_UNICOM_PATTERN = "(?:^(?:\\+86)?1(?:3[0-2]|4[56]|5[56]|66|7[156]|8[56])\\d{8}$)|(?:^(?:\\+86)?170[47-9]\\d{7}$)";
/**
*
* 134,135,136,137,138,139,147,148,150,151,152,157,158,159,178,182,183,184,187,188,195,198,1440,1703,1705,1706
**/
// private static final String CHINA_MOBILE_PATTERN =
// "(?:^(?:\\+86)?1(?:3[4-9]|4[78]|5[0-27-9]|78|8[2-478]|98|95)\\d{8}$)|(?:^(?:\\+86)?1440\\d{7}$)|(?:^(?:\\+86)?170[356]\\d{7}$)";
private static final String CHINA_MOBILE_PATTERN = "(?:^(?:\\+86)?1(?:3[4-9]|4[78]|5[0-27-9]|6[5]|7[28]|8[2-478]|98|95)\\d{8}$)|(?:^(?:\\+86)?1440\\d{7}$)|(?:^(?:\\+86)?170[356]\\d{7}$)";
/**
*
* @param phone
* @return
*/
public static boolean checkPhone(String phone) {
if (StringUtils.isNotBlank(phone)) {
if (checkChinaMobile(phone) || checkChinaUnicom(phone) || checkChinaTelecom(phone)) {
return true;
}
}
return false;
}
public static void main(String[] args) {
// String s = "/home/eladmin/mail/MM_20221125_2.zip";
// String filePath = s.substring(s.lastIndexOf(StrPool.SLASH) +
// DefaultNumberConstants.ONE_NUMBER);
System.out.println(checkPhone("17269788988"));
}
/**
*
* @param phone
* @return
*/
public static boolean checkChinaMobile(String phone) {
if (StringUtils.isNotBlank(phone)) {
Pattern regexp = Pattern.compile(CHINA_MOBILE_PATTERN);
return regexp.matcher(phone).matches();
}
return false;
}
/**
*
* @param phone
* @return
*/
public static boolean checkChinaUnicom(String phone) {
if (StringUtils.isNotBlank(phone)) {
Pattern regexp = Pattern.compile(CHINA_UNICOM_PATTERN);
if (regexp.matcher(phone).matches()) {
return true;
}
}
return false;
}
/**
*
* @param phone
* @return
*/
public static boolean checkChinaTelecom(String phone) {
if (StringUtils.isNotBlank(phone)) {
Pattern regexp = Pattern.compile(CHINA_TELECOM_PATTERN);
if (regexp.matcher(phone).matches()) {
return true;
}
}
return false;
}
/**
*
* @param phone
* @return java.lang.String
*/
public static String hideMiddleMobile(String phone) {
if (StringUtils.isNotBlank(phone)) {
phone = phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}
return phone;
}
}

@ -0,0 +1,45 @@
package com.hccake.ballcat.common.core.util;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
/**
* WebUtils
*
* @author Hccake
*/
@Slf4j
@UtilityClass
public class WebUtils extends org.springframework.web.util.WebUtils {
/**
* ServletRequestAttributes
* @return {ServletRequestAttributes}
*/
public ServletRequestAttributes getServletRequestAttributes() {
return (ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes());
}
/**
* HttpServletRequest
* @return {HttpServletRequest}
*/
public HttpServletRequest getRequest() {
return getServletRequestAttributes().getRequest();
}
/**
* HttpServletResponse
* @return {HttpServletResponse}
*/
public HttpServletResponse getResponse() {
return getServletRequestAttributes().getResponse();
}
}

@ -0,0 +1,59 @@
package com.hccake.ballcat.common.core.validation;
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Locale;
/**
*
* <p>
* {@link ResourceBundleMessageInterpolator}
*
* @author hccake
*/
public class EmptyCurlyToDefaultMessageInterpolator extends ResourceBundleMessageInterpolator {
private static final String EMPTY_CURLY_BRACES = "{}";
public EmptyCurlyToDefaultMessageInterpolator() {
}
public EmptyCurlyToDefaultMessageInterpolator(ResourceBundleLocator userResourceBundleLocator) {
super(userResourceBundleLocator);
}
@Override
public String interpolate(String message, Context context, Locale locale) {
// 如果包含花括号占位符
if (message.contains(EMPTY_CURLY_BRACES)) {
// 获取注解类型
Class<? extends Annotation> annotationType = context.getConstraintDescriptor()
.getAnnotation()
.annotationType();
Method messageMethod;
try {
messageMethod = annotationType.getDeclaredMethod("message");
}
catch (NoSuchMethodException e) {
return super.interpolate(message, context, locale);
}
// 找到对应 message 的默认值,将 {} 替换为默认值
if (messageMethod.getDefaultValue() != null) {
Object defaultValue = messageMethod.getDefaultValue();
if (defaultValue instanceof String) {
String defaultMessage = (String) defaultValue;
message = message.replace(EMPTY_CURLY_BRACES, defaultMessage);
}
}
}
return super.interpolate(message, context, locale);
}
}

@ -0,0 +1,48 @@
package com.hccake.ballcat.common.core.validation.constraints;
import com.hccake.ballcat.common.core.validation.validator.EnumValueValidatorOfClass;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @author housl
* @version 1.0
*/
@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(OneOfClasses.List.class)
@Documented
@Constraint(validatedBy = { EnumValueValidatorOfClass.class })
public @interface OneOfClasses {
String message() default "value must match one of the values in the list: {value}";
Class<?>[] value();
/**
* null,
*/
boolean allowNull() default false;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@interface List {
OneOfClasses[] value();
}
}

@ -0,0 +1,48 @@
package com.hccake.ballcat.common.core.validation.constraints;
import com.hccake.ballcat.common.core.validation.validator.EnumValueValidatorOfInt;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @author housl
* @version 1.0
*/
@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(OneOfInts.List.class)
@Documented
@Constraint(validatedBy = { EnumValueValidatorOfInt.class })
public @interface OneOfInts {
String message() default "value must match one of the values in the list: {value}";
int[] value();
/**
* null,
*/
boolean allowNull() default false;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@interface List {
OneOfInts[] value();
}
}

@ -0,0 +1,48 @@
package com.hccake.ballcat.common.core.validation.constraints;
import com.hccake.ballcat.common.core.validation.validator.EnumValueValidatorOfString;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @author housl
* @version 1.0
*/
@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(OneOfStrings.List.class)
@Documented
@Constraint(validatedBy = { EnumValueValidatorOfString.class })
public @interface OneOfStrings {
String message() default "value must match one of the values in the list: {value}";
String[] value();
/**
* null,
*/
boolean allowNull() default false;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@interface List {
OneOfStrings[] value();
}
}

@ -0,0 +1,50 @@
package com.hccake.ballcat.common.core.validation.constraints;
import com.hccake.ballcat.common.core.validation.validator.ValueOfEnumValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @author housl
* @version 1.0
*/
@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(ValueOfEnum.List.class)
@Documented
@Constraint(validatedBy = { ValueOfEnumValidator.class })
public @interface ValueOfEnum {
String message() default "value is not in enum {enumClass}";
Class<?>[] enumClass();
String method() default "valueOf";
/**
* null,
*/
boolean allowNull() default false;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@interface List {
ValueOfEnum[] value();
}
}

@ -0,0 +1,10 @@
package com.hccake.ballcat.common.core.validation.group;
/**
* Validation Group
*
* @author hccake
*/
public interface CreateGroup {
}

@ -0,0 +1,10 @@
package com.hccake.ballcat.common.core.validation.group;
/**
* Validation Group
*
* @author hccake
*/
public interface UpdateGroup {
}

@ -0,0 +1,37 @@
package com.hccake.ballcat.common.core.validation.validator;
import com.hccake.ballcat.common.core.validation.constraints.OneOfClasses;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* @author hccake
* @version 1.0 Validator
*/
public class EnumValueValidatorOfClass implements ConstraintValidator<OneOfClasses, Class<?>> {
private Class<?>[] classList;
private boolean allowNull;
@Override
public void initialize(OneOfClasses constraintAnnotation) {
classList = constraintAnnotation.value();
allowNull = constraintAnnotation.allowNull();
}
@Override
public boolean isValid(Class value, ConstraintValidatorContext context) {
if (value == null) {
return allowNull;
}
for (Class<?> clazz : classList) {
if (clazz.isAssignableFrom(value)) {
return true;
}
}
return false;
}
}

@ -0,0 +1,37 @@
package com.hccake.ballcat.common.core.validation.validator;
import com.hccake.ballcat.common.core.validation.constraints.OneOfInts;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* @author housl
* @version 1.0 Validator
*/
public class EnumValueValidatorOfInt implements ConstraintValidator<OneOfInts, Integer> {
private int[] ints;
private boolean allowNull;
@Override
public void initialize(OneOfInts constraintAnnotation) {
ints = constraintAnnotation.value();
allowNull = constraintAnnotation.allowNull();
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
if (value == null) {
return allowNull;
}
for (int anInt : ints) {
if (anInt == value) {
return true;
}
}
return false;
}
}

@ -0,0 +1,37 @@
package com.hccake.ballcat.common.core.validation.validator;
import com.hccake.ballcat.common.core.validation.constraints.OneOfStrings;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* @author housl
* @version 1.0 Validator
*/
public class EnumValueValidatorOfString implements ConstraintValidator<OneOfStrings, String> {
private String[] stringList;
private boolean allowNull;
@Override
public void initialize(OneOfStrings constraintAnnotation) {
stringList = constraintAnnotation.value();
allowNull = constraintAnnotation.allowNull();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return allowNull;
}
for (String strValue : stringList) {
if (strValue.equals(value)) {
return true;
}
}
return false;
}
}

@ -0,0 +1,58 @@
package com.hccake.ballcat.common.core.validation.validator;
import com.hccake.ballcat.common.core.validation.constraints.ValueOfEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import javax.validation.ConstraintDefinitionException;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* @author housl
* @version 1.0 Validator
*/
@Slf4j
public class ValueOfEnumValidator implements ConstraintValidator<ValueOfEnum, Object> {
private Class<?>[] targetEnum;
private String checkMethod;
private boolean allowNull;
@Override
public void initialize(ValueOfEnum constraintAnnotation) {
targetEnum = constraintAnnotation.enumClass();
checkMethod = constraintAnnotation.method();
allowNull = constraintAnnotation.allowNull();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null) {
return allowNull;
}
for (Class<?> eClass : targetEnum) {
// 包装类和原始类型的处理
Class<?> clazz = ClassUtils.isPrimitiveWrapper(value.getClass())
? ClassUtils.wrapperToPrimitive(value.getClass()) : value.getClass();
try {
Object enumInstance = MethodUtils.invokeStaticMethod(eClass, checkMethod, new Object[] { value },
new Class[] { clazz });
return enumInstance != null;
}
catch (NoSuchMethodException ex) {
throw new ConstraintDefinitionException(ex);
}
catch (Exception ex) {
log.warn("check enum error: ", ex);
return false;
}
}
return false;
}
}

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ad-distribute-common</artifactId>
<groupId>com.baiye</groupId>
<version>1.1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-desensitize</artifactId>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,100 @@
package com.hccake.ballcat.common.desensitize;
import cn.hutool.core.lang.Assert;
import com.hccake.ballcat.common.desensitize.enums.RegexDesensitizationTypeEnum;
import com.hccake.ballcat.common.desensitize.enums.SlideDesensitizationTypeEnum;
import com.hccake.ballcat.common.desensitize.functions.DesensitizeFunction;
import com.hccake.ballcat.common.desensitize.handler.RegexDesensitizationHandler;
import com.hccake.ballcat.common.desensitize.handler.SimpleDesensitizationHandler;
import com.hccake.ballcat.common.desensitize.handler.SlideDesensitizationHandler;
import com.hccake.ballcat.common.desensitize.json.annotation.JsonRegexDesensitize;
import com.hccake.ballcat.common.desensitize.json.annotation.JsonSimpleDesensitize;
import com.hccake.ballcat.common.desensitize.json.annotation.JsonSlideDesensitize;
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* ->
*
* @author Yakir
*/
public final class AnnotationHandlerHolder {
private static final AnnotationHandlerHolder INSTANCE = new AnnotationHandlerHolder();
/**
*
*/
private final Map<Class<? extends Annotation>, DesensitizeFunction> annotationHandlers;
private AnnotationHandlerHolder() {
annotationHandlers = new ConcurrentHashMap<>();
annotationHandlers.put(JsonSimpleDesensitize.class, (annotation, value) -> {
// Simple 类型处理
JsonSimpleDesensitize an = (JsonSimpleDesensitize) annotation;
Class<? extends SimpleDesensitizationHandler> handlerClass = an.handler();
SimpleDesensitizationHandler desensitizationHandler = DesensitizationHandlerHolder
.getSimpleHandler(handlerClass);
Assert.notNull(desensitizationHandler, "SimpleDesensitizationHandler can not be Null");
return desensitizationHandler.handle(value);
});
annotationHandlers.put(JsonRegexDesensitize.class, (annotation, value) -> {
// 正则类型脱敏处理
JsonRegexDesensitize an = (JsonRegexDesensitize) annotation;
RegexDesensitizationTypeEnum type = an.type();
RegexDesensitizationHandler regexDesensitizationHandler = DesensitizationHandlerHolder
.getRegexDesensitizationHandler();
return RegexDesensitizationTypeEnum.CUSTOM.equals(type)
? regexDesensitizationHandler.handle(value, an.regex(), an.replacement())
: regexDesensitizationHandler.handle(value, type);
});
annotationHandlers.put(JsonSlideDesensitize.class, (annotation, value) -> {
// 滑动类型脱敏处理
JsonSlideDesensitize an = (JsonSlideDesensitize) annotation;
SlideDesensitizationTypeEnum type = an.type();
SlideDesensitizationHandler slideDesensitizationHandler = DesensitizationHandlerHolder
.getSlideDesensitizationHandler();
return SlideDesensitizationTypeEnum.CUSTOM.equals(type) ? slideDesensitizationHandler.handle(value,
an.leftPlainTextLen(), an.rightPlainTextLen(), an.maskString())
: slideDesensitizationHandler.handle(value, type);
});
}
/**
*
* @param annotationType {@code annotationType}
* @return {@link DesensitizeFunction}|null
*/
public static DesensitizeFunction getHandleFunction(Class<? extends Annotation> annotationType) {
return INSTANCE.annotationHandlers.get(annotationType);
}
/**
*
* @param annotationType {@code annotationType}
* @param desensitizeFunction {@link DesensitizeFunction}
* @return key | null
*/
public static DesensitizeFunction addHandleFunction(Class<? extends Annotation> annotationType,
DesensitizeFunction desensitizeFunction) {
Assert.notNull(annotationType, "annotation cannot be null");
Assert.notNull(desensitizeFunction, "desensitization function cannot be null");
// 加入注解处理映射
return INSTANCE.annotationHandlers.put(annotationType, desensitizeFunction);
}
/**
*
* @return {@code }
*/
public static Set<Class<? extends Annotation>> getAnnotationClasses() {
return INSTANCE.annotationHandlers.keySet();
}
}

@ -0,0 +1,87 @@
package com.hccake.ballcat.common.desensitize;
import com.hccake.ballcat.common.desensitize.handler.DesensitizationHandler;
import com.hccake.ballcat.common.desensitize.handler.RegexDesensitizationHandler;
import com.hccake.ballcat.common.desensitize.handler.SimpleDesensitizationHandler;
import com.hccake.ballcat.common.desensitize.handler.SlideDesensitizationHandler;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
/**
*
* <p>
* - Regex Slide <br/>
* - Simple 使SPI便
* </p>
*
* @author Hccake 2021/1/22
* @version 1.0
*/
public final class DesensitizationHandlerHolder {
private static final DesensitizationHandlerHolder INSTANCE = new DesensitizationHandlerHolder();
private final Map<Class<? extends DesensitizationHandler>, DesensitizationHandler> desensitizationHandlerMap;
private DesensitizationHandlerHolder() {
desensitizationHandlerMap = new ConcurrentHashMap<>(16);
// 滑动脱敏处理器
desensitizationHandlerMap.put(SlideDesensitizationHandler.class, new SlideDesensitizationHandler());
// 正则脱敏处理器
desensitizationHandlerMap.put(RegexDesensitizationHandler.class, new RegexDesensitizationHandler());
// SPI 加载所有的 Simple脱敏类型处理
ServiceLoader<SimpleDesensitizationHandler> loadedDrivers = ServiceLoader
.load(SimpleDesensitizationHandler.class);
for (SimpleDesensitizationHandler desensitizationHandler : loadedDrivers) {
desensitizationHandlerMap.put(desensitizationHandler.getClass(), desensitizationHandler);
}
}
/**
* DesensitizationHandler
* @return
*/
public static DesensitizationHandler getHandler(Class<? extends DesensitizationHandler> handlerClass) {
return INSTANCE.desensitizationHandlerMap.get(handlerClass);
}
/**
* RegexDesensitizationHandler
* @return
*/
public static RegexDesensitizationHandler getRegexDesensitizationHandler() {
return (RegexDesensitizationHandler) INSTANCE.desensitizationHandlerMap.get(RegexDesensitizationHandler.class);
}
/**
* SlideDesensitizationHandler
* @return
*/
public static SlideDesensitizationHandler getSlideDesensitizationHandler() {
return (SlideDesensitizationHandler) INSTANCE.desensitizationHandlerMap.get(SlideDesensitizationHandler.class);
}
/**
* SimpleDesensitizationHandler
* @param handlerClass SimpleDesensitizationHandler
* @return
*/
public static SimpleDesensitizationHandler getSimpleHandler(
Class<? extends SimpleDesensitizationHandler> handlerClass) {
return (SimpleDesensitizationHandler) INSTANCE.desensitizationHandlerMap.get(handlerClass);
}
/**
* Handler
* @param handlerClass DesensitizationHandler
* @param handler
* @return handler
*/
public static DesensitizationHandler addHandler(Class<? extends DesensitizationHandler> handlerClass,
DesensitizationHandler handler) {
return INSTANCE.desensitizationHandlerMap.put(handlerClass, handler);
}
}

@ -0,0 +1,39 @@
package com.hccake.ballcat.common.desensitize.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* @author Hccake 2021/1/23
* @version 1.0
*/
@Getter
@RequiredArgsConstructor
public enum RegexDesensitizationTypeEnum {
/**
*
*/
CUSTOM("^[\\s\\S]*$", "******"),
/**
* '@'4* eg. 12@qq.com -> 1****@qq.com
*/
EMAIL("(^.)[^@]*(@.*$)", "$1****$2"),
/**
* 32 4 *
*/
ENCRYPTED_PASSWORD("(.{3}).*(.{2}$)", "$1****$2");
/**
*
*/
private final String regex;
/**
*
*/
private final String replacement;
}

@ -0,0 +1,54 @@
package com.hccake.ballcat.common.desensitize.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* @author Hccake 2021/1/23
* @version 1.0
*/
@Getter
@RequiredArgsConstructor
public enum SlideDesensitizationTypeEnum {
/**
*
*/
CUSTOM(0, 0, "*"),
/**
* *
*/
ALL_ASTERISK(0, 0, "*"),
/**
* , 64 eg. 330150******1234
*/
BANK_CARD_NO(6, 4, "*"),
/**
* 64 eg. 655356*******1234
*/
ID_CARD_NO(6, 4, "*"),
/**
* 使
*/
PHONE_NUMBER(3, 2, "*");
/**
*
*/
private final int leftPlainTextLen;
/**
*
*/
private final int rightPlainTextLen;
/**
*
*/
private final String maskString;
}

@ -0,0 +1,21 @@
package com.hccake.ballcat.common.desensitize.functions;
import java.lang.annotation.Annotation;
/**
*
*
* @author Yakir
*/
@FunctionalInterface
public interface DesensitizeFunction {
/**
*
* @param annotation
* @param value
* @return
*/
String desensitize(Annotation annotation, String value);
}

@ -0,0 +1,11 @@
package com.hccake.ballcat.common.desensitize.handler;
/**
*
*
* @author Hccake 2021/1/22
* @version 1.0
*/
public interface DesensitizationHandler {
}

@ -0,0 +1,34 @@
package com.hccake.ballcat.common.desensitize.handler;
import com.hccake.ballcat.common.desensitize.enums.RegexDesensitizationTypeEnum;
/**
* 使
*
* @author Hccake 2021/1/23
* @version 1.0
*/
public class RegexDesensitizationHandler implements DesensitizationHandler {
/**
*
* @param origin
* @param regex
* @param replacement
* @return
*/
public String handle(String origin, String regex, String replacement) {
return origin.replaceAll(regex, replacement);
}
/**
*
* @param origin
* @param typeEnum
* @return
*/
public String handle(String origin, RegexDesensitizationTypeEnum typeEnum) {
return origin.replaceAll(typeEnum.getRegex(), typeEnum.getReplacement());
}
}

@ -0,0 +1,18 @@
package com.hccake.ballcat.common.desensitize.handler;
/**
*
*
* @author Hccake 2021/1/23
* @version 1.0
*/
public interface SimpleDesensitizationHandler extends DesensitizationHandler {
/**
*
* @param origin
* @return
*/
String handle(String origin);
}

@ -0,0 +1,21 @@
package com.hccake.ballcat.common.desensitize.handler;
/**
* 6*6* eg. ******
*
* @author Hccake 2021/1/22
* @version 1.0
*/
public class SixAsteriskDesensitizationHandler implements SimpleDesensitizationHandler {
/**
*
* @param origin
* @return
*/
@Override
public String handle(String origin) {
return "******";
}
}

@ -0,0 +1,62 @@
package com.hccake.ballcat.common.desensitize.handler;
import com.hccake.ballcat.common.desensitize.enums.SlideDesensitizationTypeEnum;
/**
*
*
* @author Hccake 2021/1/23
* @version 1.0
*/
public class SlideDesensitizationHandler implements DesensitizationHandler {
/**
*
* @param origin
* @param leftPlainTextLen
* @param rightPlainTextLen
* @return
*/
public String handle(String origin, int leftPlainTextLen, int rightPlainTextLen) {
return this.handle(origin, leftPlainTextLen, rightPlainTextLen, "*");
}
/**
*
* @param origin
* @param leftPlainTextLen
* @param rightPlainTextLen
* @param maskString
* @return
*/
public String handle(String origin, int leftPlainTextLen, int rightPlainTextLen, String maskString) {
if (origin == null) {
return null;
}
StringBuilder sb = new StringBuilder();
char[] chars = origin.toCharArray();
int length = chars.length;
for (int i = 0; i < length; i++) {
// 明文位内则明文显示
if (i < leftPlainTextLen || i > (length - rightPlainTextLen - 1)) {
sb.append(chars[i]);
}
else {
sb.append(maskString);
}
}
return sb.toString();
}
/**
*
* @param value
* @param type
* @return
*/
public String handle(String value, SlideDesensitizationTypeEnum type) {
return this.handle(value, type.getLeftPlainTextLen(), type.getRightPlainTextLen(), type.getMaskString());
}
}

@ -0,0 +1,17 @@
package com.hccake.ballcat.common.desensitize.json;
/**
*
*
* @author Yakir
*/
public interface DesensitizeStrategy {
/**
*
* @param fieldName {@code }
* @return @{code true |false }
*/
boolean ignoreField(String fieldName);
}

@ -0,0 +1,17 @@
package com.hccake.ballcat.common.desensitize.json;
import com.fasterxml.jackson.databind.module.SimpleModule;
/**
* Json
*
* @author hccake
*/
public class JsonDesensitizeModule extends SimpleModule {
public JsonDesensitizeModule(JsonDesensitizeSerializerModifier jsonDesensitizeSerializerModifier) {
super();
this.setSerializerModifier(jsonDesensitizeSerializerModifier);
}
}

@ -0,0 +1,57 @@
package com.hccake.ballcat.common.desensitize.json;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.hccake.ballcat.common.desensitize.AnnotationHandlerHolder;
import com.hccake.ballcat.common.desensitize.functions.DesensitizeFunction;
import java.io.IOException;
import java.lang.annotation.Annotation;
/**
* Jackson
*
* @author Hccake 2021/1/22
* @version 1.0
*/
public class JsonDesensitizeSerializer extends JsonSerializer<Object> {
/**
* json
*/
private final Annotation jsonDesensitizeAnnotation;
/**
*
*/
private final DesensitizeStrategy desensitizeStrategy;
public JsonDesensitizeSerializer(Annotation jsonDesensitizeAnnotation, DesensitizeStrategy desensitizeStrategy) {
this.jsonDesensitizeAnnotation = jsonDesensitizeAnnotation;
this.desensitizeStrategy = desensitizeStrategy;
}
@Override
public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializers)
throws IOException {
if (value instanceof String) {
String str = (String) value;
String fieldName = jsonGenerator.getOutputContext().getCurrentName();
// 未开启脱敏
if (desensitizeStrategy != null && desensitizeStrategy.ignoreField(fieldName)) {
jsonGenerator.writeString(str);
return;
}
DesensitizeFunction handleFunction = AnnotationHandlerHolder
.getHandleFunction(jsonDesensitizeAnnotation.annotationType());
if (handleFunction == null) {
jsonGenerator.writeString(str);
return;
}
jsonGenerator.writeString(handleFunction.desensitize(jsonDesensitizeAnnotation, str));
}
}
}

@ -0,0 +1,57 @@
package com.hccake.ballcat.common.desensitize.json;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.hccake.ballcat.common.desensitize.AnnotationHandlerHolder;
import java.lang.annotation.Annotation;
import java.util.List;
/**
* json serial modifier
*
* @author Yakir
*/
public class JsonDesensitizeSerializerModifier extends BeanSerializerModifier {
private DesensitizeStrategy desensitizeStrategy;
public JsonDesensitizeSerializerModifier() {
}
public JsonDesensitizeSerializerModifier(DesensitizeStrategy desensitizeStrategy) {
this.desensitizeStrategy = desensitizeStrategy;
}
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
for (BeanPropertyWriter beanProperty : beanProperties) {
Annotation annotation = getDesensitizeAnnotation(beanProperty);
if (annotation != null && beanProperty.getType().isTypeOrSubTypeOf(String.class)) {
beanProperty.assignSerializer(new JsonDesensitizeSerializer(annotation, desensitizeStrategy));
}
}
return beanProperties;
}
/**
*
* @param beanProperty BeanPropertyWriter
* @return
*/
private Annotation getDesensitizeAnnotation(BeanPropertyWriter beanProperty) {
for (Class<? extends Annotation> annotationClass : AnnotationHandlerHolder.getAnnotationClasses()) {
Annotation annotation = beanProperty.getAnnotation(annotationClass);
if (annotation != null) {
return annotation;
}
}
return null;
}
}

@ -0,0 +1,37 @@
package com.hccake.ballcat.common.desensitize.json.annotation;
import com.hccake.ballcat.common.desensitize.enums.RegexDesensitizationTypeEnum;
import com.hccake.ballcat.common.desensitize.handler.RegexDesensitizationHandler;
import java.lang.annotation.*;
/**
* Jackson Filed , 使
*
* @see RegexDesensitizationHandler
* @author Hccake 2021/1/22
* @version 1.0
*/
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JsonRegexDesensitize {
/**
* CUSTOM
* @see RegexDesensitizationTypeEnum#CUSTOM
* @return type
*/
RegexDesensitizationTypeEnum type();
/**
* type CUSTOM
*/
String regex() default "^[\\s\\S]*$";
/**
* type CUSTOM
*/
String replacement() default "******";
}

@ -0,0 +1,25 @@
package com.hccake.ballcat.common.desensitize.json.annotation;
import com.hccake.ballcat.common.desensitize.handler.SimpleDesensitizationHandler;
import java.lang.annotation.*;
/**
* Jackson Filed 使
*
* @see SimpleDesensitizationHandler
* @author Hccake 2021/1/22
* @version 1.0
*/
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JsonSimpleDesensitize {
/**
*
* @return type
*/
Class<? extends SimpleDesensitizationHandler> handler();
}

@ -0,0 +1,42 @@
package com.hccake.ballcat.common.desensitize.json.annotation;
import com.hccake.ballcat.common.desensitize.enums.SlideDesensitizationTypeEnum;
import com.hccake.ballcat.common.desensitize.handler.SlideDesensitizationHandler;
import java.lang.annotation.*;
/**
* Jackson Filed , 使
*
* @see SlideDesensitizationHandler
* @author Hccake 2021/1/22
* @version 1.0
*/
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JsonSlideDesensitize {
/**
* CUSTOM
* @see SlideDesensitizationTypeEnum#CUSTOM
* @return type
*/
SlideDesensitizationTypeEnum type();
/**
* type CUSTOM
*/
int leftPlainTextLen() default 0;
/**
* type CUSTOM
*/
int rightPlainTextLen() default 0;
/**
* type CUSTOM
*/
String maskString() default "*";
}

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ad-distribute-common</artifactId>
<groupId>com.baiye</groupId>
<version>1.1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-i18n</artifactId>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<!-- slf4j日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

@ -0,0 +1,32 @@
package com.hccake.ballcat.common.i18n;
import lombok.RequiredArgsConstructor;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.lang.Nullable;
import java.text.MessageFormat;
import java.util.Locale;
/**
* MessageSource redis message
*
* @author hccake
*/
@RequiredArgsConstructor
public class DynamicMessageSource extends AbstractMessageSource {
public static final String DYNAMIC_MESSAGE_SOURCE_BEAN_NAME = "dynamicMessageSource";
private final I18nMessageProvider i18nMessageProvider;
@Override
@Nullable
protected MessageFormat resolveCode(String code, Locale locale) {
I18nMessage i18nMessage = i18nMessageProvider.getI18nMessage(code, locale);
if (i18nMessage != null) {
return createMessageFormat(i18nMessage.getMessage(), locale);
}
return null;
}
}

@ -0,0 +1,16 @@
package com.hccake.ballcat.common.i18n;
import java.lang.annotation.*;
/**
* , {@link I18nField} 使
*
* @see I18nResponseAdvice
* @author hccake
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface I18nClass {
}

@ -0,0 +1,41 @@
package com.hccake.ballcat.common.i18n;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
/**
* String {@link I18nClass}
*
* @author hccake
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface I18nField {
/**
* <p>
* This is an alias for {@link #code}
* </p>
* @return String
*/
@AliasFor("code")
String value() default "";
/**
* 使SpEL code 1, 使 code 2, ,
* code ,使 code () : String & Number(String) :
* "title" 3,code prefix() : "'prefix'+ "title"
* @return String
*/
@AliasFor("value")
String code() default "";
/**
* SpEL
* @return boolean SpEL
*/
String condition() default "";
}

@ -0,0 +1,15 @@
package com.hccake.ballcat.common.i18n;
import java.lang.annotation.*;
/**
*
*
* @author hccake
*/
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface I18nIgnore {
}

@ -0,0 +1,38 @@
package com.hccake.ballcat.common.i18n;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
/**
* message bundle
*
* @author hccake
*/
@Data
@Schema(title = "国际化信息")
public class I18nMessage {
/**
*
*/
@NotEmpty(message = "{i18nMessage.code}{}")
@Schema(title = "国际化标识")
private String code;
/**
*
*/
@NotEmpty(message = "{i18nMessage.message}{}")
@Schema(title = "文本值,可以使用 { } 加角标,作为占位符")
private String message;
/**
*
*/
@NotEmpty(message = "{i18nMessage.languageTag}{}")
@Schema(title = "语言标签")
private String languageTag;
}

@ -0,0 +1,23 @@
package com.hccake.ballcat.common.i18n;
import org.springframework.context.ApplicationEvent;
import java.util.List;
/**
* I18nMessage Listener I18nMessage
*
* @author hccake
*/
public class I18nMessageCreateEvent extends ApplicationEvent {
public I18nMessageCreateEvent(List<I18nMessage> i18nMessages) {
super(i18nMessages);
}
@SuppressWarnings("unchecked")
public List<I18nMessage> getI18nMessages() {
return (List<I18nMessage>) super.getSource();
}
}

@ -0,0 +1,20 @@
package com.hccake.ballcat.common.i18n;
import java.util.Locale;
/**
* 使
*
* @author hccake
*/
public interface I18nMessageProvider {
/**
* I18nMessage
* @param code
* @param locale
* @return
*/
I18nMessage getI18nMessage(String code, Locale locale);
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save