From 70b25718260b43f397291adca26ca8b45ceb0ab4 Mon Sep 17 00:00:00 2001 From: houzhongjian <houzhongyi@126.com> Date: 星期四, 10 四月 2025 09:49:52 +0800 Subject: [PATCH] 1、移除wx、aliyun等第三方工具类 2、移除sms消息功能 3、移除iailab-framework框架依赖,将其单独提取出来 4、网关增加ansteel和shangangrizhao --- iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/auth/AdminAuthService.java | 23 - iailab-module-system/iailab-module-system-biz/src/main/resources/application-dev.yaml | 11 iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/convert/auth/AuthConvert.java | 6 iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/auth/AdminAuthServiceImpl.java | 58 --- iailab-plat-sdk/src/main/java/com/iailab/sdk/auth/client/IailabClient.java | 2 pom.xml | 655 ++++++++++++++++----------------- /dev/null | 173 --------- iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/convert/user/UserConvert.java | 4 iailab-cloud/iailab-gateway/src/main/resources/application.yaml | 7 iailab-module-system/iailab-module-system-biz/pom.xml | 94 +++- iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/auth/AuthController.java | 49 -- iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/rpc/config/RpcConfiguration.java | 3 iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/user/UserProfileController.java | 8 iailab-module-system/iailab-module-system-biz/src/main/resources/application.yaml | 11 14 files changed, 415 insertions(+), 689 deletions(-) diff --git a/iailab-cloud/iailab-gateway/src/main/resources/application.yaml b/iailab-cloud/iailab-gateway/src/main/resources/application.yaml index 4ecec4f..02f0004 100644 --- a/iailab-cloud/iailab-gateway/src/main/resources/application.yaml +++ b/iailab-cloud/iailab-gateway/src/main/resources/application.yaml @@ -150,6 +150,13 @@ - Path=/admin-api/xmcsms/** filters: - RewritePath=/admin-api/xmcsms/v3/api-docs, /v3/api-docs + ## shangangrizhao 服务 + - id: shangangrizhao-admin-api # 路由的编号 + uri: grayLb://shangangrizhao-server + predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 + - Path=/admin-api/shangangrizhao/** + filters: + - RewritePath=/admin-api/shangangrizhao/v3/api-docs, /v3/api-docs x-forwarded: prefix-enabled: true # 避免 Swagger 重复带上额外的 /admin-api/system 前缀 diff --git a/iailab-framework/iailab-common-biz-data-permission/pom.xml b/iailab-framework/iailab-common-biz-data-permission/pom.xml deleted file mode 100644 index 2fd16fe..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/pom.xml +++ /dev/null @@ -1,63 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <artifactId>iailab-framework</artifactId> - <groupId>com.iailab</groupId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-biz-data-permission</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description>数据权限</description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common</artifactId> - </dependency> - - <!-- Web 相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-security</artifactId> - <optional>true</optional> <!-- 可选,如果使用 DeptDataPermissionRule 必须提供 --> - </dependency> - - <!-- DB 相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-mybatis</artifactId> - </dependency> - <dependency> - <groupId>org.postgresql</groupId> - <artifactId>postgresql</artifactId> - </dependency> - - <!-- RPC 远程调用相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-rpc</artifactId> - <optional>true</optional> - </dependency> - - <!-- 业务组件 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-module-system-api</artifactId> <!-- 需要使用它,进行数据权限的获取 --> - <version>${revision}</version> - </dependency> - - <!-- Test 测试相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-test</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/config/IailabDataPermissionAutoConfiguration.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/config/IailabDataPermissionAutoConfiguration.java deleted file mode 100644 index 7428e3c..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/config/IailabDataPermissionAutoConfiguration.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.iailab.framework.datapermission.config; - -import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor; -import com.iailab.framework.datapermission.core.aop.DataPermissionAnnotationAdvisor; -import com.iailab.framework.datapermission.core.db.DataPermissionRuleHandler; -import com.iailab.framework.datapermission.core.rule.DataPermissionRule; -import com.iailab.framework.datapermission.core.rule.DataPermissionRuleFactory; -import com.iailab.framework.datapermission.core.rule.DataPermissionRuleFactoryImpl; -import com.iailab.framework.mybatis.core.util.MyBatisUtils; -import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Bean; - -import java.util.List; - -/** - * 数据权限的自动配置类 - * - * @author iailab - */ -@AutoConfiguration -public class IailabDataPermissionAutoConfiguration { - - @Bean - public DataPermissionRuleFactory dataPermissionRuleFactory(List<DataPermissionRule> rules) { - return new DataPermissionRuleFactoryImpl(rules); - } - - @Bean - public DataPermissionRuleHandler dataPermissionRuleHandler(MybatisPlusInterceptor interceptor, - DataPermissionRuleFactory ruleFactory) { - // 创建 DataPermissionInterceptor 拦截器 - DataPermissionRuleHandler handler = new DataPermissionRuleHandler(ruleFactory); - DataPermissionInterceptor inner = new DataPermissionInterceptor(handler); - // 添加到 interceptor 中 - // 需要加在首个,主要是为了在分页插件前面。这个是 MyBatis Plus 的规定 - MyBatisUtils.addInterceptor(interceptor, inner, 0); - return handler; - } - - - @Bean - public DataPermissionAnnotationAdvisor dataPermissionAnnotationAdvisor() { - return new DataPermissionAnnotationAdvisor(); - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/config/IailabDataPermissionRpcAutoConfiguration.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/config/IailabDataPermissionRpcAutoConfiguration.java deleted file mode 100644 index e4ba244..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/config/IailabDataPermissionRpcAutoConfiguration.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.iailab.framework.datapermission.config; - -import com.iailab.framework.datapermission.core.rpc.DataPermissionRequestInterceptor; -import com.iailab.framework.datapermission.core.rpc.DataPermissionRpcWebFilter; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; - -import static com.iailab.framework.common.enums.WebFilterOrderEnum.TENANT_CONTEXT_FILTER; - - -/** - * 数据权限针对 RPC 的自动配置类 - * - * @author iailab - */ -@AutoConfiguration -@ConditionalOnClass(name = "feign.RequestInterceptor") -public class IailabDataPermissionRpcAutoConfiguration { - - @Bean - public DataPermissionRequestInterceptor dataPermissionRequestInterceptor() { - return new DataPermissionRequestInterceptor(); - } - - @Bean - public FilterRegistrationBean<DataPermissionRpcWebFilter> dataPermissionRpcFilter() { - FilterRegistrationBean<DataPermissionRpcWebFilter> registrationBean = new FilterRegistrationBean<>(); - registrationBean.setFilter(new DataPermissionRpcWebFilter()); - registrationBean.setOrder(TENANT_CONTEXT_FILTER - 1); // 顺序没有绝对的要求,在租户 Filter 前面稳妥点 - return registrationBean; - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/config/IailabDeptDataPermissionAutoConfiguration.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/config/IailabDeptDataPermissionAutoConfiguration.java deleted file mode 100644 index a591f11..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/config/IailabDeptDataPermissionAutoConfiguration.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.iailab.framework.datapermission.config; - -import cn.hutool.extra.spring.SpringUtil; -import com.iailab.framework.datapermission.core.rule.dept.DeptDataPermissionRule; -import com.iailab.framework.datapermission.core.rule.dept.DeptDataPermissionRuleCustomizer; -import com.iailab.framework.security.core.LoginUser; -import com.iailab.module.system.api.permission.PermissionApi; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.context.annotation.Bean; - -import java.util.List; - -/** - * 基于部门的数据权限 AutoConfiguration - * - * @author iailab - */ -@AutoConfiguration -@ConditionalOnClass(LoginUser.class) -@ConditionalOnBean(value = DeptDataPermissionRuleCustomizer.class) -public class IailabDeptDataPermissionAutoConfiguration { - - @Bean - public DeptDataPermissionRule deptDataPermissionRule(PermissionApi permissionApi, - List<DeptDataPermissionRuleCustomizer> customizers) { - // Cloud 专属逻辑:优先使用本地的 PermissionApi 实现类,而不是 Feign 调用 - // 原因:在创建租户时,租户还没创建好,导致 Feign 调用获取数据权限时,报“租户不存在”的错误 - try { - PermissionApi permissionApiImpl = SpringUtil.getBean("permissionApiImpl", PermissionApi.class); - if (permissionApiImpl != null) { - permissionApi = permissionApiImpl; - } - } catch (Exception ignored) {} - - // 创建 DeptDataPermissionRule 对象 - DeptDataPermissionRule rule = new DeptDataPermissionRule(permissionApi); - // 补全表配置 - customizers.forEach(customizer -> customizer.customize(rule)); - return rule; - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/annotation/DataPermission.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/annotation/DataPermission.java deleted file mode 100644 index 65f8cd5..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/annotation/DataPermission.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.iailab.framework.datapermission.core.annotation; - -import com.iailab.framework.datapermission.core.rule.DataPermissionRule; - -import java.lang.annotation.*; - -/** - * 数据权限注解 - * 可声明在类或者方法上,标识使用的数据权限规则 - * - * @author iailab - */ -@Target({ElementType.TYPE, ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface DataPermission { - - /** - * 当前类或方法是否开启数据权限 - * 即使不添加 @DataPermission 注解,默认是开启状态 - * 可通过设置 enable 为 false 禁用 - */ - boolean enable() default true; - - /** - * 生效的数据权限规则数组,优先级高于 {@link #excludeRules()} - */ - Class<? extends DataPermissionRule>[] includeRules() default {}; - - /** - * 排除的数据权限规则数组,优先级最低 - */ - Class<? extends DataPermissionRule>[] excludeRules() default {}; - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/aop/DataPermissionAnnotationAdvisor.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/aop/DataPermissionAnnotationAdvisor.java deleted file mode 100644 index 80308c4..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/aop/DataPermissionAnnotationAdvisor.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.iailab.framework.datapermission.core.aop; - -import com.iailab.framework.datapermission.core.annotation.DataPermission; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import org.aopalliance.aop.Advice; -import org.springframework.aop.Pointcut; -import org.springframework.aop.support.AbstractPointcutAdvisor; -import org.springframework.aop.support.ComposablePointcut; -import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; - -/** - * {@link com.iailab.framework.datapermission.core.annotation.DataPermission} 注解的 Advisor 实现类 - * - * @author iailab - */ -@Getter -@EqualsAndHashCode(callSuper = true) -public class DataPermissionAnnotationAdvisor extends AbstractPointcutAdvisor { - - private final Advice advice; - - private final Pointcut pointcut; - - public DataPermissionAnnotationAdvisor() { - this.advice = new DataPermissionAnnotationInterceptor(); - this.pointcut = this.buildPointcut(); - } - - protected Pointcut buildPointcut() { - Pointcut classPointcut = new AnnotationMatchingPointcut(DataPermission.class, true); - Pointcut methodPointcut = new AnnotationMatchingPointcut(null, DataPermission.class, true); - return new ComposablePointcut(classPointcut).union(methodPointcut); - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/aop/DataPermissionAnnotationInterceptor.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/aop/DataPermissionAnnotationInterceptor.java deleted file mode 100644 index 2e9726a..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/aop/DataPermissionAnnotationInterceptor.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.iailab.framework.datapermission.core.aop; - -import com.iailab.framework.datapermission.core.annotation.DataPermission; -import lombok.Getter; -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.core.MethodClassKey; -import org.springframework.core.annotation.AnnotationUtils; - -import java.lang.reflect.Method; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * {@link DataPermission} 注解的拦截器 - * 1. 在执行方法前,将 @DataPermission 注解入栈 - * 2. 在执行方法后,将 @DataPermission 注解出栈 - * - * @author iailab - */ -@DataPermission // 该注解,用于 {@link DATA_PERMISSION_NULL} 的空对象 -public class DataPermissionAnnotationInterceptor implements MethodInterceptor { - - /** - * DataPermission 空对象,用于方法无 {@link DataPermission} 注解时,使用 DATA_PERMISSION_NULL 进行占位 - */ - static final DataPermission DATA_PERMISSION_NULL = DataPermissionAnnotationInterceptor.class.getAnnotation(DataPermission.class); - - @Getter - private final Map<MethodClassKey, DataPermission> dataPermissionCache = new ConcurrentHashMap<>(); - - @Override - public Object invoke(MethodInvocation methodInvocation) throws Throwable { - // 入栈 - DataPermission dataPermission = this.findAnnotation(methodInvocation); - if (dataPermission != null) { - DataPermissionContextHolder.add(dataPermission); - } - try { - // 执行逻辑 - return methodInvocation.proceed(); - } finally { - // 出栈 - if (dataPermission != null) { - DataPermissionContextHolder.remove(); - } - } - } - - private DataPermission findAnnotation(MethodInvocation methodInvocation) { - // 1. 从缓存中获取 - Method method = methodInvocation.getMethod(); - Object targetObject = methodInvocation.getThis(); - Class<?> clazz = targetObject != null ? targetObject.getClass() : method.getDeclaringClass(); - MethodClassKey methodClassKey = new MethodClassKey(method, clazz); - DataPermission dataPermission = dataPermissionCache.get(methodClassKey); - if (dataPermission != null) { - return dataPermission != DATA_PERMISSION_NULL ? dataPermission : null; - } - - // 2.1 从方法中获取 - dataPermission = AnnotationUtils.findAnnotation(method, DataPermission.class); - // 2.2 从类上获取 - if (dataPermission == null) { - dataPermission = AnnotationUtils.findAnnotation(clazz, DataPermission.class); - } - // 2.3 添加到缓存中 - dataPermissionCache.put(methodClassKey, dataPermission != null ? dataPermission : DATA_PERMISSION_NULL); - return dataPermission; - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/aop/DataPermissionContextHolder.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/aop/DataPermissionContextHolder.java deleted file mode 100644 index c7cdbb3..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/aop/DataPermissionContextHolder.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.iailab.framework.datapermission.core.aop; - -import com.iailab.framework.datapermission.core.annotation.DataPermission; -import com.alibaba.ttl.TransmittableThreadLocal; - -import java.util.LinkedList; -import java.util.List; - -/** - * {@link DataPermission} 注解的 Context 上下文 - * - * @author iailab - */ -public class DataPermissionContextHolder { - - /** - * 使用 List 的原因,可能存在方法的嵌套调用 - */ - private static final ThreadLocal<LinkedList<DataPermission>> DATA_PERMISSIONS = - TransmittableThreadLocal.withInitial(LinkedList::new); - - /** - * 获得当前的 DataPermission 注解 - * - * @return DataPermission 注解 - */ - public static DataPermission get() { - return DATA_PERMISSIONS.get().peekLast(); - } - - /** - * 入栈 DataPermission 注解 - * - * @param dataPermission DataPermission 注解 - */ - public static void add(DataPermission dataPermission) { - DATA_PERMISSIONS.get().addLast(dataPermission); - } - - /** - * 出栈 DataPermission 注解 - * - * @return DataPermission 注解 - */ - public static DataPermission remove() { - DataPermission dataPermission = DATA_PERMISSIONS.get().removeLast(); - // 无元素时,清空 ThreadLocal - if (DATA_PERMISSIONS.get().isEmpty()) { - DATA_PERMISSIONS.remove(); - } - return dataPermission; - } - - /** - * 获得所有 DataPermission - * - * @return DataPermission 队列 - */ - public static List<DataPermission> getAll() { - return DATA_PERMISSIONS.get(); - } - - /** - * 清空上下文 - * - * 目前仅仅用于单测 - */ - public static void clear() { - DATA_PERMISSIONS.remove(); - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/db/DataPermissionRuleHandler.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/db/DataPermissionRuleHandler.java deleted file mode 100644 index b341ad9..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/db/DataPermissionRuleHandler.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.iailab.framework.datapermission.core.db; - -import cn.hutool.core.collection.CollUtil; -import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler; -import com.iailab.framework.datapermission.core.rule.DataPermissionRule; -import com.iailab.framework.datapermission.core.rule.DataPermissionRuleFactory; -import com.iailab.framework.mybatis.core.util.MyBatisUtils; -import lombok.RequiredArgsConstructor; -import net.sf.jsqlparser.expression.Expression; -import net.sf.jsqlparser.expression.operators.conditional.AndExpression; -import net.sf.jsqlparser.schema.Table; - -import java.util.List; - -/** - * 基于 {@link DataPermissionRule} 的数据权限处理器 - * - * 它的底层,是基于 MyBatis Plus 的 <a href="https://baomidou.com/plugins/data-permission/">数据权限插件</a> - * 核心原理:它会在 SQL 执行前拦截 SQL 语句,并根据用户权限动态添加权限相关的 SQL 片段。这样,只有用户有权限访问的数据才会被查询出来 - * - * @author iailab - */ -@RequiredArgsConstructor -public class DataPermissionRuleHandler implements MultiDataPermissionHandler { - - private final DataPermissionRuleFactory ruleFactory; - - @Override - public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) { - // 获得 Mapper 对应的数据权限的规则 - List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(mappedStatementId); - if (CollUtil.isEmpty(rules)) { - return null; - } - - // 生成条件 - Expression allExpression = null; - for (DataPermissionRule rule : rules) { - // 判断表名是否匹配 - String tableName = MyBatisUtils.getTableName(table); - if (!rule.getTableNames().contains(tableName)) { - continue; - } - - // 单条规则的条件 - Expression oneExpress = rule.getExpression(tableName, table.getAlias()); - if (oneExpress == null) { - continue; - } - // 拼接到 allExpression 中 - allExpression = allExpression == null ? oneExpress - : new AndExpression(allExpression, oneExpress); - } - return allExpression; - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rpc/DataPermissionRequestInterceptor.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rpc/DataPermissionRequestInterceptor.java deleted file mode 100644 index 6969a45..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rpc/DataPermissionRequestInterceptor.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.iailab.framework.datapermission.core.rpc; - -import com.iailab.framework.datapermission.core.annotation.DataPermission; -import com.iailab.framework.datapermission.core.aop.DataPermissionContextHolder; -import feign.RequestInterceptor; -import feign.RequestTemplate; - -/** - * DataPermission 的 RequestInterceptor 实现类:Feign 请求时,将 {@link DataPermission} 设置到 header 中,继续透传给被调用的服务 - * - * 注意:由于 {@link DataPermission} 不支持序列化和反序列化,所以暂时只能传递它的 enable 属性 - * - * @author iailab - */ -public class DataPermissionRequestInterceptor implements RequestInterceptor { - - public static final String ENABLE_HEADER_NAME = "data-permission-enable"; - - @Override - public void apply(RequestTemplate requestTemplate) { - DataPermission dataPermission = DataPermissionContextHolder.get(); - if (dataPermission != null && Boolean.FALSE.equals(dataPermission.enable())) { - requestTemplate.header(ENABLE_HEADER_NAME, "false"); - } - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rpc/DataPermissionRpcWebFilter.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rpc/DataPermissionRpcWebFilter.java deleted file mode 100644 index 8a4bb79..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rpc/DataPermissionRpcWebFilter.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.iailab.framework.datapermission.core.rpc; - -import com.iailab.framework.datapermission.core.util.DataPermissionUtils; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Objects; - -/** - * 针对 {@link DataPermissionRequestInterceptor} 的 RPC 调用,设置 {@link com.iailab.framework.datapermission.core.aop.DataPermissionContextHolder} 的上下文 - * - * @author iailab - */ -public class DataPermissionRpcWebFilter extends OncePerRequestFilter { - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) - throws ServletException, IOException { - String enable = request.getHeader(DataPermissionRequestInterceptor.ENABLE_HEADER_NAME); - if (Objects.equals(enable, Boolean.FALSE.toString())) { - DataPermissionUtils.executeIgnore(() -> { - try { - chain.doFilter(request, response); - } catch (IOException | ServletException e) { - throw new RuntimeException(e); - } - }); - } else { - chain.doFilter(request, response); - } - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/DataPermissionRule.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/DataPermissionRule.java deleted file mode 100644 index 9b45315..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/DataPermissionRule.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.iailab.framework.datapermission.core.rule; - -import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; -import net.sf.jsqlparser.expression.Alias; -import net.sf.jsqlparser.expression.Expression; - -import java.util.Set; - -/** - * 数据权限规则接口 - * 通过实现接口,自定义数据规则。例如说, - * - * @author iailab - */ -public interface DataPermissionRule { - - /** - * 返回需要生效的表名数组 - * 为什么需要该方法?Data Permission 数组基于 SQL 重写,通过 Where 返回只有权限的数据 - * - * 如果需要基于实体名获得表名,可调用 {@link TableInfoHelper#getTableInfo(Class)} 获得 - * - * @return 表名数组 - */ - Set<String> getTableNames(); - - /** - * 根据表名和别名,生成对应的 WHERE / OR 过滤条件 - * - * @param tableName 表名 - * @param tableAlias 别名,可能为空 - * @return 过滤条件 Expression 表达式 - */ - Expression getExpression(String tableName, Alias tableAlias); - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/DataPermissionRuleFactory.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/DataPermissionRuleFactory.java deleted file mode 100644 index 48ebf78..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/DataPermissionRuleFactory.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.framework.datapermission.core.rule; - -import java.util.List; - -/** - * {@link DataPermissionRule} 工厂接口 - * 作为 {@link DataPermissionRule} 的容器,提供管理能力 - * - * @author iailab - */ -public interface DataPermissionRuleFactory { - - /** - * 获得所有数据权限规则数组 - * - * @return 数据权限规则数组 - */ - List<DataPermissionRule> getDataPermissionRules(); - - /** - * 获得指定 Mapper 的数据权限规则数组 - * - * @param mappedStatementId 指定 Mapper 的编号 - * @return 数据权限规则数组 - */ - List<DataPermissionRule> getDataPermissionRule(String mappedStatementId); - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/DataPermissionRuleFactoryImpl.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/DataPermissionRuleFactoryImpl.java deleted file mode 100644 index 7ebe9db..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/DataPermissionRuleFactoryImpl.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.iailab.framework.datapermission.core.rule; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ArrayUtil; -import com.iailab.framework.datapermission.core.annotation.DataPermission; -import com.iailab.framework.datapermission.core.aop.DataPermissionContextHolder; -import lombok.RequiredArgsConstructor; - -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -/** - * 默认的 DataPermissionRuleFactoryImpl 实现类 - * 支持通过 {@link DataPermissionContextHolder} 过滤数据权限 - * - * @author iailab - */ -@RequiredArgsConstructor -public class DataPermissionRuleFactoryImpl implements DataPermissionRuleFactory { - - /** - * 数据权限规则数组 - */ - private final List<DataPermissionRule> rules; - - @Override - public List<DataPermissionRule> getDataPermissionRules() { - return rules; - } - - @Override // mappedStatementId 参数,暂时没有用。以后,可以基于 mappedStatementId + DataPermission 进行缓存 - public List<DataPermissionRule> getDataPermissionRule(String mappedStatementId) { - // 1. 无数据权限 - if (CollUtil.isEmpty(rules)) { - return Collections.emptyList(); - } - // 2. 未配置,则默认开启 - DataPermission dataPermission = DataPermissionContextHolder.get(); - if (dataPermission == null) { - return rules; - } - // 3. 已配置,但禁用 - if (!dataPermission.enable()) { - return Collections.emptyList(); - } - - // 4. 已配置,只选择部分规则 - if (ArrayUtil.isNotEmpty(dataPermission.includeRules())) { - return rules.stream().filter(rule -> ArrayUtil.contains(dataPermission.includeRules(), rule.getClass())) - .collect(Collectors.toList()); // 一般规则不会太多,所以不采用 HashSet 查询 - } - // 5. 已配置,只排除部分规则 - if (ArrayUtil.isNotEmpty(dataPermission.excludeRules())) { - return rules.stream().filter(rule -> !ArrayUtil.contains(dataPermission.excludeRules(), rule.getClass())) - .collect(Collectors.toList()); // 一般规则不会太多,所以不采用 HashSet 查询 - } - // 6. 已配置,全部规则 - return rules; - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java deleted file mode 100644 index 409d01d..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java +++ /dev/null @@ -1,207 +0,0 @@ -package com.iailab.framework.datapermission.core.rule.dept; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.enums.UserTypeEnum; -import com.iailab.framework.common.util.collection.CollectionUtils; -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.framework.datapermission.core.rule.DataPermissionRule; -import com.iailab.framework.mybatis.core.dataobject.BaseDO; -import com.iailab.framework.mybatis.core.util.MyBatisUtils; -import com.iailab.framework.security.core.LoginUser; -import com.iailab.framework.security.core.util.SecurityFrameworkUtils; -import com.iailab.module.system.api.permission.PermissionApi; -import com.iailab.module.system.api.permission.dto.DeptDataPermissionRespDTO; -import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import net.sf.jsqlparser.expression.*; -import net.sf.jsqlparser.expression.operators.conditional.OrExpression; -import net.sf.jsqlparser.expression.operators.relational.EqualsTo; -import net.sf.jsqlparser.expression.operators.relational.ExpressionList; -import net.sf.jsqlparser.expression.operators.relational.InExpression; -import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * 基于部门的 {@link DataPermissionRule} 数据权限规则实现 - * - * 注意,使用 DeptDataPermissionRule 时,需要保证表中有 dept_id 部门编号的字段,可自定义。 - * - * 实际业务场景下,会存在一个经典的问题?当用户修改部门时,冗余的 dept_id 是否需要修改? - * 1. 一般情况下,dept_id 不进行修改,则会导致用户看不到之前的数据。【iailab-server 采用该方案】 - * 2. 部分情况下,希望该用户还是能看到之前的数据,则有两种方式解决:【需要你改造该 DeptDataPermissionRule 的实现代码】 - * 1)编写洗数据的脚本,将 dept_id 修改成新部门的编号;【建议】 - * 最终过滤条件是 WHERE dept_id = ? - * 2)洗数据的话,可能涉及的数据量较大,也可以采用 user_id 进行过滤的方式,此时需要获取到 dept_id 对应的所有 user_id 用户编号; - * 最终过滤条件是 WHERE user_id IN (?, ?, ? ...) - * 3)想要保证原 dept_id 和 user_id 都可以看的到,此时使用 dept_id 和 user_id 一起过滤; - * 最终过滤条件是 WHERE dept_id = ? OR user_id IN (?, ?, ? ...) - * - * @author iailab - */ -@AllArgsConstructor -@Slf4j -public class DeptDataPermissionRule implements DataPermissionRule { - - /** - * LoginUser 的 Context 缓存 Key - */ - protected static final String CONTEXT_KEY = DeptDataPermissionRule.class.getSimpleName(); - - private static final String DEPT_COLUMN_NAME = "dept_id"; - private static final String USER_COLUMN_NAME = "user_id"; - - static final Expression EXPRESSION_NULL = new NullValue(); - - private final PermissionApi permissionApi; - - /** - * 基于部门的表字段配置 - * 一般情况下,每个表的部门编号字段是 dept_id,通过该配置自定义。 - * - * key:表名 - * value:字段名 - */ - private final Map<String, String> deptColumns = new HashMap<>(); - /** - * 基于用户的表字段配置 - * 一般情况下,每个表的部门编号字段是 dept_id,通过该配置自定义。 - * - * key:表名 - * value:字段名 - */ - private final Map<String, String> userColumns = new HashMap<>(); - /** - * 所有表名,是 {@link #deptColumns} 和 {@link #userColumns} 的合集 - */ - private final Set<String> TABLE_NAMES = new HashSet<>(); - - @Override - public Set<String> getTableNames() { - return TABLE_NAMES; - } - - @Override - public Expression getExpression(String tableName, Alias tableAlias) { - // 只有有登陆用户的情况下,才进行数据权限的处理 - LoginUser loginUser = SecurityFrameworkUtils.getLoginUser(); - if (loginUser == null) { - return null; - } - // 只有管理员类型的用户,才进行数据权限的处理 - if (ObjectUtil.notEqual(loginUser.getUserType(), UserTypeEnum.ADMIN.getValue())) { - return null; - } - - // 获得数据权限 - DeptDataPermissionRespDTO deptDataPermission = loginUser.getContext(CONTEXT_KEY, DeptDataPermissionRespDTO.class); - // 从上下文中拿不到,则调用逻辑进行获取 - if (deptDataPermission == null) { - deptDataPermission = permissionApi.getDeptDataPermission(loginUser.getId()).getCheckedData(); - if (deptDataPermission == null) { - log.error("[getExpression][LoginUser({}) 获取数据权限为 null]", JsonUtils.toJsonString(loginUser)); - throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 未返回数据权限", - loginUser.getId(), tableName, tableAlias.getName())); - } - // 添加到上下文中,避免重复计算 - loginUser.setContext(CONTEXT_KEY, deptDataPermission); - } - - // 情况一,如果是 ALL 可查看全部,则无需拼接条件 - if (deptDataPermission.getAll()) { - return null; - } - - // 情况二,即不能查看部门,又不能查看自己,则说明 100% 无权限 - if (CollUtil.isEmpty(deptDataPermission.getDeptIds()) - && Boolean.FALSE.equals(deptDataPermission.getSelf())) { - return new EqualsTo(null, null); // WHERE null = null,可以保证返回的数据为空 - } - - // 情况三,拼接 Dept 和 User 的条件,最后组合 - Expression deptExpression = buildDeptExpression(tableName,tableAlias, deptDataPermission.getDeptIds()); - Expression userExpression = buildUserExpression(tableName, tableAlias, deptDataPermission.getSelf(), loginUser.getId()); - if (deptExpression == null && userExpression == null) { - // TODO iailab:获得不到条件的时候,暂时不抛出异常,而是不返回数据 - log.warn("[getExpression][LoginUser({}) Table({}/{}) DeptDataPermission({}) 构建的条件为空]", - JsonUtils.toJsonString(loginUser), tableName, tableAlias, JsonUtils.toJsonString(deptDataPermission)); -// throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 构建的条件为空", -// loginUser.getId(), tableName, tableAlias.getName())); - return EXPRESSION_NULL; - } - if (deptExpression == null) { - return userExpression; - } - if (userExpression == null) { - return deptExpression; - } - // 目前,如果有指定部门 + 可查看自己,采用 OR 条件。即,WHERE (dept_id IN ? OR user_id = ?) - return new ParenthesedExpressionList(new OrExpression(deptExpression, userExpression)); - } - - private Expression buildDeptExpression(String tableName, Alias tableAlias, Set<Long> deptIds) { - // 如果不存在配置,则无需作为条件 - String columnName = deptColumns.get(tableName); - if (StrUtil.isEmpty(columnName)) { - return null; - } - // 如果为空,则无条件 - if (CollUtil.isEmpty(deptIds)) { - return null; - } - // 拼接条件 - return new InExpression(MyBatisUtils.buildColumn(tableName, tableAlias, columnName), - // Parenthesis 的目的,是提供 (1,2,3) 的 () 左右括号 - new ParenthesedExpressionList(new ExpressionList<LongValue>(CollectionUtils.convertList(deptIds, LongValue::new)))); - } - - private Expression buildUserExpression(String tableName, Alias tableAlias, Boolean self, Long userId) { - // 如果不查看自己,则无需作为条件 - if (Boolean.FALSE.equals(self)) { - return null; - } - String columnName = userColumns.get(tableName); - if (StrUtil.isEmpty(columnName)) { - return null; - } - // 拼接条件 - return new EqualsTo(MyBatisUtils.buildColumn(tableName, tableAlias, columnName), new LongValue(userId)); - } - - // ==================== 添加配置 ==================== - - public void addDeptColumn(Class<? extends BaseDO> entityClass) { - addDeptColumn(entityClass, DEPT_COLUMN_NAME); - } - - public void addDeptColumn(Class<? extends BaseDO> entityClass, String columnName) { - String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName(); - addDeptColumn(tableName, columnName); - } - - public void addDeptColumn(String tableName, String columnName) { - deptColumns.put(tableName, columnName); - TABLE_NAMES.add(tableName); - } - - public void addUserColumn(Class<? extends BaseDO> entityClass) { - addUserColumn(entityClass, USER_COLUMN_NAME); - } - - public void addUserColumn(Class<? extends BaseDO> entityClass, String columnName) { - String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName(); - addUserColumn(tableName, columnName); - } - - public void addUserColumn(String tableName, String columnName) { - userColumns.put(tableName, columnName); - TABLE_NAMES.add(tableName); - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/dept/DeptDataPermissionRuleCustomizer.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/dept/DeptDataPermissionRuleCustomizer.java deleted file mode 100644 index b8a1947..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/dept/DeptDataPermissionRuleCustomizer.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.iailab.framework.datapermission.core.rule.dept; - -/** - * {@link DeptDataPermissionRule} 的自定义配置接口 - * - * @author iailab - */ -@FunctionalInterface -public interface DeptDataPermissionRuleCustomizer { - - /** - * 自定义该权限规则 - * 1. 调用 {@link DeptDataPermissionRule#addDeptColumn(Class, String)} 方法,配置基于 dept_id 的过滤规则 - * 2. 调用 {@link DeptDataPermissionRule#addUserColumn(Class, String)} 方法,配置基于 user_id 的过滤规则 - * - * @param rule 权限规则 - */ - void customize(DeptDataPermissionRule rule); - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/dept/package-info.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/dept/package-info.java deleted file mode 100644 index cb10588..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rule/dept/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 基于部门的数据权限规则 - * - * @author iailab - */ -package com.iailab.framework.datapermission.core.rule.dept; diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/util/DataPermissionUtils.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/util/DataPermissionUtils.java deleted file mode 100644 index a4a5ffc..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/util/DataPermissionUtils.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.iailab.framework.datapermission.core.util; - -import com.iailab.framework.datapermission.core.annotation.DataPermission; -import com.iailab.framework.datapermission.core.aop.DataPermissionContextHolder; -import lombok.SneakyThrows; - -import java.util.concurrent.Callable; - -/** - * 数据权限 Util - * - * @author iailab - */ -public class DataPermissionUtils { - - private static DataPermission DATA_PERMISSION_DISABLE; - - @DataPermission(enable = false) - @SneakyThrows - private static DataPermission getDisableDataPermissionDisable() { - if (DATA_PERMISSION_DISABLE == null) { - DATA_PERMISSION_DISABLE = DataPermissionUtils.class - .getDeclaredMethod("getDisableDataPermissionDisable") - .getAnnotation(DataPermission.class); - } - return DATA_PERMISSION_DISABLE; - } - - /** - * 忽略数据权限,执行对应的逻辑 - * - * @param runnable 逻辑 - */ - public static void executeIgnore(Runnable runnable) { - DataPermission dataPermission = getDisableDataPermissionDisable(); - DataPermissionContextHolder.add(dataPermission); - try { - // 执行 runnable - runnable.run(); - } finally { - DataPermissionContextHolder.remove(); - } - } - - /** - * 忽略数据权限,执行对应的逻辑 - * - * @param callable 逻辑 - * @return 执行结果 - */ - @SneakyThrows - public static <T> T executeIgnore(Callable<T> callable) { - DataPermission dataPermission = getDisableDataPermissionDisable(); - DataPermissionContextHolder.add(dataPermission); - try { - // 执行 callable - return callable.call(); - } finally { - DataPermissionContextHolder.remove(); - } - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/package-info.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/package-info.java deleted file mode 100644 index afd94ed..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 基于 JSqlParser 解析 SQL,增加数据权限的 WHERE 条件 - */ -package com.iailab.framework.datapermission; diff --git a/iailab-framework/iailab-common-biz-data-permission/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/iailab-framework/iailab-common-biz-data-permission/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 5499998..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,3 +0,0 @@ -com.iailab.framework.datapermission.config.IailabDataPermissionAutoConfiguration -com.iailab.framework.datapermission.config.IailabDeptDataPermissionAutoConfiguration -com.iailab.framework.datapermission.config.IailabDataPermissionRpcAutoConfiguration diff --git a/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/aop/DataPermissionAnnotationInterceptorTest.java b/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/aop/DataPermissionAnnotationInterceptorTest.java deleted file mode 100644 index aa28b86..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/aop/DataPermissionAnnotationInterceptorTest.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.iailab.framework.datapermission.core.aop; - -import cn.hutool.core.collection.CollUtil; -import com.iailab.framework.datapermission.core.annotation.DataPermission; -import com.iailab.framework.test.core.ut.BaseMockitoUnitTest; -import org.aopalliance.intercept.MethodInvocation; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import java.lang.reflect.Method; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; - -/** - * {@link DataPermissionAnnotationInterceptor} 的单元测试 - * - * @author iailab - */ -public class DataPermissionAnnotationInterceptorTest extends BaseMockitoUnitTest { - - @InjectMocks - private DataPermissionAnnotationInterceptor interceptor; - - @Mock - private MethodInvocation methodInvocation; - - @BeforeEach - public void setUp() { - interceptor.getDataPermissionCache().clear(); - } - - @Test // 无 @DataPermission 注解 - public void testInvoke_none() throws Throwable { - // 参数 - mockMethodInvocation(TestNone.class); - - // 调用 - Object result = interceptor.invoke(methodInvocation); - // 断言 - assertEquals("none", result); - assertEquals(1, interceptor.getDataPermissionCache().size()); - assertTrue(CollUtil.getFirst(interceptor.getDataPermissionCache().values()).enable()); - } - - @Test // 在 Method 上有 @DataPermission 注解 - public void testInvoke_method() throws Throwable { - // 参数 - mockMethodInvocation(TestMethod.class); - - // 调用 - Object result = interceptor.invoke(methodInvocation); - // 断言 - assertEquals("method", result); - assertEquals(1, interceptor.getDataPermissionCache().size()); - assertFalse(CollUtil.getFirst(interceptor.getDataPermissionCache().values()).enable()); - } - - @Test // 在 Class 上有 @DataPermission 注解 - public void testInvoke_class() throws Throwable { - // 参数 - mockMethodInvocation(TestClass.class); - - // 调用 - Object result = interceptor.invoke(methodInvocation); - // 断言 - assertEquals("class", result); - assertEquals(1, interceptor.getDataPermissionCache().size()); - assertFalse(CollUtil.getFirst(interceptor.getDataPermissionCache().values()).enable()); - } - - private void mockMethodInvocation(Class<?> clazz) throws Throwable { - Object targetObject = clazz.newInstance(); - Method method = targetObject.getClass().getMethod("echo"); - when(methodInvocation.getThis()).thenReturn(targetObject); - when(methodInvocation.getMethod()).thenReturn(method); - when(methodInvocation.proceed()).then(invocationOnMock -> method.invoke(targetObject)); - } - - static class TestMethod { - - @DataPermission(enable = false) - public String echo() { - return "method"; - } - - } - - @DataPermission(enable = false) - static class TestClass { - - public String echo() { - return "class"; - } - - } - - static class TestNone { - - public String echo() { - return "none"; - } - - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/aop/DataPermissionContextHolderTest.java b/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/aop/DataPermissionContextHolderTest.java deleted file mode 100644 index d3c1308..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/aop/DataPermissionContextHolderTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.iailab.framework.datapermission.core.aop; - -import com.iailab.framework.datapermission.core.annotation.DataPermission; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.mockito.Mockito.mock; - -/** - * {@link DataPermissionContextHolder} 的单元测试 - * - * @author iailab - */ -class DataPermissionContextHolderTest { - - @BeforeEach - public void setUp() { - DataPermissionContextHolder.clear(); - } - - @Test - public void testGet() { - // mock 方法 - DataPermission dataPermission01 = mock(DataPermission.class); - DataPermissionContextHolder.add(dataPermission01); - DataPermission dataPermission02 = mock(DataPermission.class); - DataPermissionContextHolder.add(dataPermission02); - - // 调用 - DataPermission result = DataPermissionContextHolder.get(); - // 断言 - assertSame(result, dataPermission02); - } - - @Test - public void testPush() { - // 调用 - DataPermission dataPermission01 = mock(DataPermission.class); - DataPermissionContextHolder.add(dataPermission01); - DataPermission dataPermission02 = mock(DataPermission.class); - DataPermissionContextHolder.add(dataPermission02); - // 断言 - DataPermission first = DataPermissionContextHolder.getAll().get(0); - DataPermission second = DataPermissionContextHolder.getAll().get(1); - assertSame(dataPermission01, first); - assertSame(dataPermission02, second); - } - - @Test - public void testRemove() { - // mock 方法 - DataPermission dataPermission01 = mock(DataPermission.class); - DataPermissionContextHolder.add(dataPermission01); - DataPermission dataPermission02 = mock(DataPermission.class); - DataPermissionContextHolder.add(dataPermission02); - - // 调用 - DataPermission result = DataPermissionContextHolder.remove(); - // 断言 - assertSame(result, dataPermission02); - assertEquals(1, DataPermissionContextHolder.getAll().size()); - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/db/DataPermissionRuleHandlerTest.java b/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/db/DataPermissionRuleHandlerTest.java deleted file mode 100644 index 555ae24..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/db/DataPermissionRuleHandlerTest.java +++ /dev/null @@ -1,540 +0,0 @@ -package com.iailab.framework.datapermission.core.db; - -import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor; -import com.iailab.framework.datapermission.core.rule.DataPermissionRule; -import com.iailab.framework.datapermission.core.rule.DataPermissionRuleFactory; -import com.iailab.framework.mybatis.core.util.MyBatisUtils; -import com.iailab.framework.test.core.ut.BaseMockitoUnitTest; -import net.sf.jsqlparser.expression.Alias; -import net.sf.jsqlparser.expression.Expression; -import net.sf.jsqlparser.expression.LongValue; -import net.sf.jsqlparser.expression.Parenthesis; -import net.sf.jsqlparser.expression.operators.relational.EqualsTo; -import net.sf.jsqlparser.expression.operators.relational.ExpressionList; -import net.sf.jsqlparser.expression.operators.relational.InExpression; -import net.sf.jsqlparser.schema.Column; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import java.util.Arrays; -import java.util.Set; - -import static com.iailab.framework.common.util.collection.SetUtils.asSet; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - -/** - * {@link DataPermissionRuleHandler} 的单元测试 - * 主要复用了 MyBatis Plus 的 TenantLineInnerInterceptorTest 的单元测试 - * 不过它的单元测试不是很规范,考虑到是复用的,所以暂时不进行修改~ - * - * @author iailab - */ -public class DataPermissionRuleHandlerTest extends BaseMockitoUnitTest { - - @InjectMocks - private DataPermissionRuleHandler handler; - - @Mock - private DataPermissionRuleFactory ruleFactory; - - private DataPermissionInterceptor interceptor; - - @BeforeEach - public void setUp() { - interceptor = new DataPermissionInterceptor(handler); - - // 租户的数据权限规则 - DataPermissionRule tenantRule = new DataPermissionRule() { - - private static final String COLUMN = "tenant_id"; - - @Override - public Set<String> getTableNames() { - return asSet("entity", "entity1", "entity2", "entity3", "t1", "t2", "sys_dict_item", // 支持 MyBatis Plus 的单元测试 - "t_user", "t_role"); // 满足自己的单元测试 - } - - @Override - public Expression getExpression(String tableName, Alias tableAlias) { - Column column = MyBatisUtils.buildColumn(tableName, tableAlias, COLUMN); - LongValue value = new LongValue(1L); - return new EqualsTo(column, value); - } - - }; - // 部门的数据权限规则 - DataPermissionRule deptRule = new DataPermissionRule() { - - private static final String COLUMN = "dept_id"; - - @Override - public Set<String> getTableNames() { - return asSet("t_user"); // 满足自己的单元测试 - } - - @Override - public Expression getExpression(String tableName, Alias tableAlias) { - Column column = MyBatisUtils.buildColumn(tableName, tableAlias, COLUMN); - ExpressionList<LongValue> values = new ExpressionList<>(new LongValue(10L), - new LongValue(20L)); - return new InExpression(column, new Parenthesis((values))); - } - - }; - // 设置到上下文 - when(ruleFactory.getDataPermissionRule(any())).thenReturn(Arrays.asList(tenantRule, deptRule)); - } - - @Test - void delete() { - assertSql("delete from entity where id = ?", - "DELETE FROM entity WHERE id = ? AND entity.tenant_id = 1"); - } - - @Test - void update() { - assertSql("update entity set name = ? where id = ?", - "UPDATE entity SET name = ? WHERE id = ? AND entity.tenant_id = 1"); - } - - @Test - void selectSingle() { - // 单表 - assertSql("select * from entity where id = ?", - "SELECT * FROM entity WHERE id = ? AND entity.tenant_id = 1"); - - assertSql("select * from entity where id = ? or name = ?", - "SELECT * FROM entity WHERE (id = ? OR name = ?) AND entity.tenant_id = 1"); - - assertSql("SELECT * FROM entity WHERE (id = ? OR name = ?)", - "SELECT * FROM entity WHERE (id = ? OR name = ?) AND entity.tenant_id = 1"); - - /* not */ - assertSql("SELECT * FROM entity WHERE not (id = ? OR name = ?)", - "SELECT * FROM entity WHERE NOT (id = ? OR name = ?) AND entity.tenant_id = 1"); - } - - @Test - void selectSubSelectIn() { - /* in */ - assertSql("SELECT * FROM entity e WHERE e.id IN (select e1.id from entity1 e1 where e1.id = ?)", - "SELECT * FROM entity e WHERE e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1"); - // 在最前 - assertSql("SELECT * FROM entity e WHERE e.id IN " + - "(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?", - "SELECT * FROM entity e WHERE e.id IN " + - "(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ? AND e.tenant_id = 1"); - // 在最后 - assertSql("SELECT * FROM entity e WHERE e.id = ? and e.id IN " + - "(select e1.id from entity1 e1 where e1.id = ?)", - "SELECT * FROM entity e WHERE e.id = ? AND e.id IN " + - "(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1"); - // 在中间 - assertSql("SELECT * FROM entity e WHERE e.id = ? and e.id IN " + - "(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?", - "SELECT * FROM entity e WHERE e.id = ? AND e.id IN " + - "(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ? AND e.tenant_id = 1"); - } - - @Test - void selectSubSelectEq() { - /* = */ - assertSql("SELECT * FROM entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?)", - "SELECT * FROM entity e WHERE e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1"); - } - - @Test - void selectSubSelectInnerNotEq() { - /* inner not = */ - assertSql("SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?))", - "SELECT * FROM entity e WHERE NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1)) AND e.tenant_id = 1"); - - assertSql("SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?) and e.id = ?)", - "SELECT * FROM entity e WHERE NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ?) AND e.tenant_id = 1"); - } - - @Test - void selectSubSelectExists() { - /* EXISTS */ - assertSql("SELECT * FROM entity e WHERE EXISTS (select e1.id from entity1 e1 where e1.id = ?)", - "SELECT * FROM entity e WHERE EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1"); - - - /* NOT EXISTS */ - assertSql("SELECT * FROM entity e WHERE NOT EXISTS (select e1.id from entity1 e1 where e1.id = ?)", - "SELECT * FROM entity e WHERE NOT EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1"); - } - - @Test - void selectSubSelect() { - /* >= */ - assertSql("SELECT * FROM entity e WHERE e.id >= (select e1.id from entity1 e1 where e1.id = ?)", - "SELECT * FROM entity e WHERE e.id >= (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1"); - - - /* <= */ - assertSql("SELECT * FROM entity e WHERE e.id <= (select e1.id from entity1 e1 where e1.id = ?)", - "SELECT * FROM entity e WHERE e.id <= (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1"); - - - /* <> */ - assertSql("SELECT * FROM entity e WHERE e.id <> (select e1.id from entity1 e1 where e1.id = ?)", - "SELECT * FROM entity e WHERE e.id <> (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1"); - } - - @Test - void selectFromSelect() { - assertSql("SELECT * FROM (select e.id from entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?))", - "SELECT * FROM (SELECT e.id FROM entity e WHERE e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1)"); - } - - @Test - void selectBodySubSelect() { - assertSql("select t1.col1,(select t2.col2 from t2 t2 where t1.col1=t2.col1) from t1 t1", - "SELECT t1.col1, (SELECT t2.col2 FROM t2 t2 WHERE t1.col1 = t2.col1 AND t2.tenant_id = 1) FROM t1 t1 WHERE t1.tenant_id = 1"); - } - - @Test - void selectLeftJoin() { - // left join - assertSql("SELECT * FROM entity e " + - "left join entity1 e1 on e1.id = e.id " + - "WHERE e.id = ? OR e.name = ?", - "SELECT * FROM entity e " + - "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1"); - - assertSql("SELECT * FROM entity e " + - "left join entity1 e1 on e1.id = e.id " + - "WHERE (e.id = ? OR e.name = ?)", - "SELECT * FROM entity e " + - "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1"); - - assertSql("SELECT * FROM entity e " + - "left join entity1 e1 on e1.id = e.id " + - "left join entity2 e2 on e1.id = e2.id", - "SELECT * FROM entity e " + - "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 " + - "WHERE e.tenant_id = 1"); - } - - @Test - void selectRightJoin() { - // right join - assertSql("SELECT * FROM entity e " + - "right join entity1 e1 on e1.id = e.id", - "SELECT * FROM entity e " + - "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " + - "WHERE e1.tenant_id = 1"); - - assertSql("SELECT * FROM with_as_1 e " + - "right join entity1 e1 on e1.id = e.id", - "SELECT * FROM with_as_1 e " + - "RIGHT JOIN entity1 e1 ON e1.id = e.id " + - "WHERE e1.tenant_id = 1"); - - assertSql("SELECT * FROM entity e " + - "right join entity1 e1 on e1.id = e.id " + - "WHERE e.id = ? OR e.name = ?", - "SELECT * FROM entity e " + - "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " + - "WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1"); - - assertSql("SELECT * FROM entity e " + - "right join entity1 e1 on e1.id = e.id " + - "right join entity2 e2 on e1.id = e2.id ", - "SELECT * FROM entity e " + - "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " + - "RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1 " + - "WHERE e2.tenant_id = 1"); - } - - @Test - void selectMixJoin() { - assertSql("SELECT * FROM entity e " + - "right join entity1 e1 on e1.id = e.id " + - "left join entity2 e2 on e1.id = e2.id", - "SELECT * FROM entity e " + - "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " + - "LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 " + - "WHERE e1.tenant_id = 1"); - - assertSql("SELECT * FROM entity e " + - "left join entity1 e1 on e1.id = e.id " + - "right join entity2 e2 on e1.id = e2.id", - "SELECT * FROM entity e " + - "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e.tenant_id = 1 " + - "WHERE e2.tenant_id = 1"); - - assertSql("SELECT * FROM entity e " + - "left join entity1 e1 on e1.id = e.id " + - "inner join entity2 e2 on e1.id = e2.id", - "SELECT * FROM entity e " + - "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "INNER JOIN entity2 e2 ON e1.id = e2.id AND e.tenant_id = 1 AND e2.tenant_id = 1"); - } - - - @Test - void selectJoinSubSelect() { - assertSql("select * from (select * from entity) e1 " + - "left join entity2 e2 on e1.id = e2.id", - "SELECT * FROM (SELECT * FROM entity WHERE entity.tenant_id = 1) e1 " + - "LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1"); - - assertSql("select * from entity1 e1 " + - "left join (select * from entity2) e2 " + - "on e1.id = e2.id", - "SELECT * FROM entity1 e1 " + - "LEFT JOIN (SELECT * FROM entity2 WHERE entity2.tenant_id = 1) e2 " + - "ON e1.id = e2.id " + - "WHERE e1.tenant_id = 1"); - } - - @Test - void selectSubJoin() { - - assertSql("select * FROM " + - "(entity1 e1 right JOIN entity2 e2 ON e1.id = e2.id)", - "SELECT * FROM " + - "(entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) " + - "WHERE e2.tenant_id = 1"); - - assertSql("select * FROM " + - "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id)", - "SELECT * FROM " + - "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " + - "WHERE e1.tenant_id = 1"); - - - assertSql("select * FROM " + - "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id) " + - "right join entity3 e3 on e1.id = e3.id", - "SELECT * FROM " + - "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " + - "RIGHT JOIN entity3 e3 ON e1.id = e3.id AND e1.tenant_id = 1 " + - "WHERE e3.tenant_id = 1"); - - - assertSql("select * FROM entity e " + - "LEFT JOIN (entity1 e1 right join entity2 e2 ON e1.id = e2.id) " + - "on e.id = e2.id", - "SELECT * FROM entity e " + - "LEFT JOIN (entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) " + - "ON e.id = e2.id AND e2.tenant_id = 1 " + - "WHERE e.tenant_id = 1"); - - assertSql("select * FROM entity e " + - "LEFT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) " + - "on e.id = e2.id", - "SELECT * FROM entity e " + - "LEFT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " + - "ON e.id = e2.id AND e1.tenant_id = 1 " + - "WHERE e.tenant_id = 1"); - - assertSql("select * FROM entity e " + - "RIGHT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) " + - "on e.id = e2.id", - "SELECT * FROM entity e " + - "RIGHT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " + - "ON e.id = e2.id AND e.tenant_id = 1 " + - "WHERE e1.tenant_id = 1"); - } - - - @Test - void selectLeftJoinMultipleTrailingOn() { - // 多个 on 尾缀的 - assertSql("SELECT * FROM entity e " + - "LEFT JOIN entity1 e1 " + - "LEFT JOIN entity2 e2 ON e2.id = e1.id " + - "ON e1.id = e.id " + - "WHERE (e.id = ? OR e.NAME = ?)", - "SELECT * FROM entity e " + - "LEFT JOIN entity1 e1 " + - "LEFT JOIN entity2 e2 ON e2.id = e1.id AND e2.tenant_id = 1 " + - "ON e1.id = e.id AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.NAME = ?) AND e.tenant_id = 1"); - - assertSql("SELECT * FROM entity e " + - "LEFT JOIN entity1 e1 " + - "LEFT JOIN with_as_A e2 ON e2.id = e1.id " + - "ON e1.id = e.id " + - "WHERE (e.id = ? OR e.NAME = ?)", - "SELECT * FROM entity e " + - "LEFT JOIN entity1 e1 " + - "LEFT JOIN with_as_A e2 ON e2.id = e1.id " + - "ON e1.id = e.id AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.NAME = ?) AND e.tenant_id = 1"); - } - - @Test - void selectInnerJoin() { - // inner join - assertSql("SELECT * FROM entity e " + - "inner join entity1 e1 on e1.id = e.id " + - "WHERE e.id = ? OR e.name = ?", - "SELECT * FROM entity e " + - "INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 " + - "WHERE e.id = ? OR e.name = ?"); - - assertSql("SELECT * FROM entity e " + - "inner join entity1 e1 on e1.id = e.id " + - "WHERE (e.id = ? OR e.name = ?)", - "SELECT * FROM entity e " + - "INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.name = ?)"); - - // 隐式内连接 - assertSql("SELECT * FROM entity,entity1 " + - "WHERE entity.id = entity1.id", - "SELECT * FROM entity, entity1 " + - "WHERE entity.id = entity1.id AND entity.tenant_id = 1 AND entity1.tenant_id = 1"); - - // 隐式内连接 - assertSql("SELECT * FROM entity a, with_as_entity1 b " + - "WHERE a.id = b.id", - "SELECT * FROM entity a, with_as_entity1 b " + - "WHERE a.id = b.id AND a.tenant_id = 1"); - - assertSql("SELECT * FROM with_as_entity a, with_as_entity1 b " + - "WHERE a.id = b.id", - "SELECT * FROM with_as_entity a, with_as_entity1 b " + - "WHERE a.id = b.id"); - - // SubJoin with 隐式内连接 - assertSql("SELECT * FROM (entity,entity1) " + - "WHERE entity.id = entity1.id", - "SELECT * FROM (entity, entity1) " + - "WHERE entity.id = entity1.id " + - "AND entity.tenant_id = 1 AND entity1.tenant_id = 1"); - - assertSql("SELECT * FROM ((entity,entity1),entity2) " + - "WHERE entity.id = entity1.id and entity.id = entity2.id", - "SELECT * FROM ((entity, entity1), entity2) " + - "WHERE entity.id = entity1.id AND entity.id = entity2.id " + - "AND entity.tenant_id = 1 AND entity1.tenant_id = 1 AND entity2.tenant_id = 1"); - - assertSql("SELECT * FROM (entity,(entity1,entity2)) " + - "WHERE entity.id = entity1.id and entity.id = entity2.id", - "SELECT * FROM (entity, (entity1, entity2)) " + - "WHERE entity.id = entity1.id AND entity.id = entity2.id " + - "AND entity.tenant_id = 1 AND entity1.tenant_id = 1 AND entity2.tenant_id = 1"); - - // 沙雕的括号写法 - assertSql("SELECT * FROM (((entity,entity1))) " + - "WHERE entity.id = entity1.id", - "SELECT * FROM (((entity, entity1))) " + - "WHERE entity.id = entity1.id " + - "AND entity.tenant_id = 1 AND entity1.tenant_id = 1"); - - } - - - @Test - void selectWithAs() { - assertSql("with with_as_A as (select * from entity) select * from with_as_A", - "WITH with_as_A AS (SELECT * FROM entity WHERE entity.tenant_id = 1) SELECT * FROM with_as_A"); - } - - - @Test - void selectIgnoreTable() { - assertSql(" SELECT dict.dict_code, item.item_text AS \"text\", item.item_value AS \"value\" FROM sys_dict_item item INNER JOIN sys_dict dict ON dict.id = item.dict_id WHERE dict.dict_code IN (1, 2, 3) AND item.item_value IN (1, 2, 3)", - "SELECT dict.dict_code, item.item_text AS \"text\", item.item_value AS \"value\" FROM sys_dict_item item INNER JOIN sys_dict dict ON dict.id = item.dict_id AND item.tenant_id = 1 WHERE dict.dict_code IN (1, 2, 3) AND item.item_value IN (1, 2, 3)"); - } - - private void assertSql(String sql, String targetSql) { - assertEquals(targetSql, interceptor.parserSingle(sql, null)); - } - - // ========== 额外的测试 ========== - - @Test - public void testSelectSingle() { - // 单表 - assertSql("select * from t_user where id = ?", - "SELECT * FROM t_user WHERE id = ? AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)"); - - assertSql("select * from t_user where id = ? or name = ?", - "SELECT * FROM t_user WHERE (id = ? OR name = ?) AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)"); - - assertSql("SELECT * FROM t_user WHERE (id = ? OR name = ?)", - "SELECT * FROM t_user WHERE (id = ? OR name = ?) AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)"); - - /* not */ - assertSql("SELECT * FROM t_user WHERE not (id = ? OR name = ?)", - "SELECT * FROM t_user WHERE NOT (id = ? OR name = ?) AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)"); - } - - @Test - public void testSelectLeftJoin() { - // left join - assertSql("SELECT * FROM t_user e " + - "left join t_role e1 on e1.id = e.id " + - "WHERE e.id = ? OR e.name = ?", - "SELECT * FROM t_user e " + - "LEFT JOIN t_role e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1 AND e.dept_id IN (10, 20)"); - - // 条件 e.id = ? OR e.name = ? 带括号 - assertSql("SELECT * FROM t_user e " + - "left join t_role e1 on e1.id = e.id " + - "WHERE (e.id = ? OR e.name = ?)", - "SELECT * FROM t_user e " + - "LEFT JOIN t_role e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1 AND e.dept_id IN (10, 20)"); - } - - @Test - public void testSelectRightJoin() { - // right join - assertSql("SELECT * FROM t_user e " + - "right join t_role e1 on e1.id = e.id " + - "WHERE e.id = ? OR e.name = ?", - "SELECT * FROM t_user e " + - "RIGHT JOIN t_role e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) " + - "WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1"); - - // 条件 e.id = ? OR e.name = ? 带括号 - assertSql("SELECT * FROM t_user e " + - "right join t_role e1 on e1.id = e.id " + - "WHERE (e.id = ? OR e.name = ?)", - "SELECT * FROM t_user e " + - "RIGHT JOIN t_role e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) " + - "WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1"); - } - - @Test - public void testSelectInnerJoin() { - // inner join - assertSql("SELECT * FROM t_user e " + - "inner join entity1 e1 on e1.id = e.id " + - "WHERE e.id = ? OR e.name = ?", - "SELECT * FROM t_user e " + - "INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) AND e1.tenant_id = 1 " + - "WHERE e.id = ? OR e.name = ?"); - - // 条件 e.id = ? OR e.name = ? 带括号 - assertSql("SELECT * FROM t_user e " + - "inner join entity1 e1 on e1.id = e.id " + - "WHERE (e.id = ? OR e.name = ?)", - "SELECT * FROM t_user e " + - "INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.name = ?)"); - - // 没有 On 的 inner join - assertSql("SELECT * FROM entity,entity1 " + - "WHERE entity.id = entity1.id", - "SELECT * FROM entity, entity1 " + - "WHERE entity.id = entity1.id AND entity.tenant_id = 1 AND entity1.tenant_id = 1"); - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/rule/DataPermissionRuleFactoryImplTest.java b/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/rule/DataPermissionRuleFactoryImplTest.java deleted file mode 100644 index ec3a3be..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/rule/DataPermissionRuleFactoryImplTest.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.iailab.framework.datapermission.core.rule; - -import com.iailab.framework.datapermission.core.annotation.DataPermission; -import com.iailab.framework.datapermission.core.aop.DataPermissionContextHolder; -import com.iailab.framework.test.core.ut.BaseMockitoUnitTest; -import net.sf.jsqlparser.expression.Alias; -import net.sf.jsqlparser.expression.Expression; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Spy; -import org.springframework.core.annotation.AnnotationUtils; - -import java.util.Arrays; -import java.util.List; -import java.util.Set; - -import static com.iailab.framework.test.core.util.RandomUtils.randomString; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link DataPermissionRuleFactoryImpl} 单元测试 - * - * @author iailab - */ -class DataPermissionRuleFactoryImplTest extends BaseMockitoUnitTest { - - @InjectMocks - private DataPermissionRuleFactoryImpl dataPermissionRuleFactory; - - @Spy - private List<DataPermissionRule> rules = Arrays.asList(new DataPermissionRule01(), - new DataPermissionRule02()); - - @BeforeEach - public void setUp() { - DataPermissionContextHolder.clear(); - } - - @Test - public void testGetDataPermissionRule_02() { - // 准备参数 - String mappedStatementId = randomString(); - - // 调用 - List<DataPermissionRule> result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId); - // 断言 - assertSame(rules, result); - } - - @Test - public void testGetDataPermissionRule_03() { - // 准备参数 - String mappedStatementId = randomString(); - // mock 方法 - DataPermissionContextHolder.add(AnnotationUtils.findAnnotation(TestClass03.class, DataPermission.class)); - - // 调用 - List<DataPermissionRule> result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId); - // 断言 - assertTrue(result.isEmpty()); - } - - @Test - public void testGetDataPermissionRule_04() { - // 准备参数 - String mappedStatementId = randomString(); - // mock 方法 - DataPermissionContextHolder.add(AnnotationUtils.findAnnotation(TestClass04.class, DataPermission.class)); - - // 调用 - List<DataPermissionRule> result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId); - // 断言 - assertEquals(1, result.size()); - assertEquals(DataPermissionRule01.class, result.get(0).getClass()); - } - - @Test - public void testGetDataPermissionRule_05() { - // 准备参数 - String mappedStatementId = randomString(); - // mock 方法 - DataPermissionContextHolder.add(AnnotationUtils.findAnnotation(TestClass05.class, DataPermission.class)); - - // 调用 - List<DataPermissionRule> result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId); - // 断言 - assertEquals(1, result.size()); - assertEquals(DataPermissionRule02.class, result.get(0).getClass()); - } - - @Test - public void testGetDataPermissionRule_06() { - // 准备参数 - String mappedStatementId = randomString(); - // mock 方法 - DataPermissionContextHolder.add(AnnotationUtils.findAnnotation(TestClass06.class, DataPermission.class)); - - // 调用 - List<DataPermissionRule> result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId); - // 断言 - assertSame(rules, result); - } - - @DataPermission(enable = false) - static class TestClass03 {} - - @DataPermission(includeRules = DataPermissionRule01.class) - static class TestClass04 {} - - @DataPermission(excludeRules = DataPermissionRule01.class) - static class TestClass05 {} - - @DataPermission - static class TestClass06 {} - - static class DataPermissionRule01 implements DataPermissionRule { - - @Override - public Set<String> getTableNames() { - return null; - } - - @Override - public Expression getExpression(String tableName, Alias tableAlias) { - return null; - } - - } - - static class DataPermissionRule02 implements DataPermissionRule { - - @Override - public Set<String> getTableNames() { - return null; - } - - @Override - public Expression getExpression(String tableName, Alias tableAlias) { - return null; - } - - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/rule/dept/DeptDataPermissionRuleTest.java b/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/rule/dept/DeptDataPermissionRuleTest.java deleted file mode 100644 index 86b6587..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/rule/dept/DeptDataPermissionRuleTest.java +++ /dev/null @@ -1,239 +0,0 @@ -package com.iailab.framework.datapermission.core.rule.dept; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ReflectUtil; -import com.iailab.framework.common.enums.UserTypeEnum; -import com.iailab.framework.common.util.collection.SetUtils; -import com.iailab.framework.security.core.LoginUser; -import com.iailab.framework.security.core.util.SecurityFrameworkUtils; -import com.iailab.framework.test.core.ut.BaseMockitoUnitTest; -import com.iailab.module.system.api.permission.PermissionApi; -import com.iailab.module.system.api.permission.dto.DeptDataPermissionRespDTO; -import net.sf.jsqlparser.expression.Alias; -import net.sf.jsqlparser.expression.Expression; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockedStatic; - -import java.util.Map; - -import static com.iailab.framework.common.pojo.CommonResult.success; -import static com.iailab.framework.datapermission.core.rule.dept.DeptDataPermissionRule.EXPRESSION_NULL; -import static com.iailab.framework.test.core.util.RandomUtils.randomPojo; -import static com.iailab.framework.test.core.util.RandomUtils.randomString; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; - -/** - * {@link DeptDataPermissionRule} 的单元测试 - * - * @author iailab - */ -class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { - - @InjectMocks - private DeptDataPermissionRule rule; - - @Mock - private PermissionApi permissionApi; - - @BeforeEach - @SuppressWarnings("unchecked") - public void setUp() { - // 清空 rule - rule.getTableNames().clear(); - ((Map<String, String>) ReflectUtil.getFieldValue(rule, "deptColumns")).clear(); - ((Map<String, String>) ReflectUtil.getFieldValue(rule, "deptColumns")).clear(); - } - - @Test // 无 LoginUser - public void testGetExpression_noLoginUser() { - // 准备参数 - String tableName = randomString(); - Alias tableAlias = new Alias(randomString()); - // mock 方法 - - // 调用 - Expression expression = rule.getExpression(tableName, tableAlias); - // 断言 - assertNull(expression); - } - - @Test // 无数据权限时 - public void testGetExpression_noDeptDataPermission() { - try (MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock - = mockStatic(SecurityFrameworkUtils.class)) { - // 准备参数 - String tableName = "t_user"; - Alias tableAlias = new Alias("u"); - // mock 方法 - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) - .setUserType(UserTypeEnum.ADMIN.getValue())); - securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); - // mock 方法(permissionApi 返回 null) - when(permissionApi.getDeptDataPermission(eq(loginUser.getId()))).thenReturn(success(null)); - - // 调用 - NullPointerException exception = assertThrows(NullPointerException.class, - () -> rule.getExpression(tableName, tableAlias)); - // 断言 - assertEquals("LoginUser(1) Table(t_user/u) 未返回数据权限", exception.getMessage()); - } - } - - @Test // 全部数据权限 - public void testGetExpression_allDeptDataPermission() { - try (MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock - = mockStatic(SecurityFrameworkUtils.class)) { - // 准备参数 - String tableName = "t_user"; - Alias tableAlias = new Alias("u"); - // mock 方法(LoginUser) - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) - .setUserType(UserTypeEnum.ADMIN.getValue())); - securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); - // mock 方法(DeptDataPermissionRespDTO) - DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO().setAll(true); - when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission)); - - // 调用 - Expression expression = rule.getExpression(tableName, tableAlias); - // 断言 - assertNull(expression); - assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); - } - } - - @Test // 即不能查看部门,又不能查看自己,则说明 100% 无权限 - public void testGetExpression_noDept_noSelf() { - try (MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock - = mockStatic(SecurityFrameworkUtils.class)) { - // 准备参数 - String tableName = "t_user"; - Alias tableAlias = new Alias("u"); - // mock 方法(LoginUser) - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) - .setUserType(UserTypeEnum.ADMIN.getValue())); - securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); - // mock 方法(DeptDataPermissionRespDTO) - DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO(); - when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission)); - - // 调用 - Expression expression = rule.getExpression(tableName, tableAlias); - // 断言 - assertEquals("null = null", expression.toString()); - assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); - } - } - - @Test // 拼接 Dept 和 User 的条件(字段都不符合) - public void testGetExpression_noDeptColumn_noSelfColumn() { - try (MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock - = mockStatic(SecurityFrameworkUtils.class)) { - // 准备参数 - String tableName = "t_user"; - Alias tableAlias = new Alias("u"); - // mock 方法(LoginUser) - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) - .setUserType(UserTypeEnum.ADMIN.getValue())); - securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); - // mock 方法(DeptDataPermissionRespDTO) - DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() - .setDeptIds(SetUtils.asSet(10L, 20L)).setSelf(true); - when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission)); - - // 调用 - Expression expression = rule.getExpression(tableName, tableAlias); - // 断言 - assertSame(EXPRESSION_NULL, expression); - assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); - } - } - - @Test // 拼接 Dept 和 User 的条件(self 符合) - public void testGetExpression_noDeptColumn_yesSelfColumn() { - try (MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock - = mockStatic(SecurityFrameworkUtils.class)) { - // 准备参数 - String tableName = "t_user"; - Alias tableAlias = new Alias("u"); - // mock 方法(LoginUser) - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) - .setUserType(UserTypeEnum.ADMIN.getValue())); - securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); - // mock 方法(DeptDataPermissionRespDTO) - DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() - .setSelf(true); - when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission)); - // 添加 user 字段配置 - rule.addUserColumn("t_user", "id"); - - // 调用 - Expression expression = rule.getExpression(tableName, tableAlias); - // 断言 - assertEquals("u.id = 1", expression.toString()); - assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); - } - } - - @Test // 拼接 Dept 和 User 的条件(dept 符合) - public void testGetExpression_yesDeptColumn_noSelfColumn() { - try (MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock - = mockStatic(SecurityFrameworkUtils.class)) { - // 准备参数 - String tableName = "t_user"; - Alias tableAlias = new Alias("u"); - // mock 方法(LoginUser) - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) - .setUserType(UserTypeEnum.ADMIN.getValue())); - securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); - // mock 方法(DeptDataPermissionRespDTO) - DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() - .setDeptIds(CollUtil.newLinkedHashSet(10L, 20L)); - when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission)); - // 添加 dept 字段配置 - rule.addDeptColumn("t_user", "dept_id"); - - // 调用 - Expression expression = rule.getExpression(tableName, tableAlias); - // 断言 - assertEquals("u.dept_id IN (10, 20)", expression.toString()); - assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); - } - } - - @Test // 拼接 Dept 和 User 的条件(dept + self 符合) - public void testGetExpression_yesDeptColumn_yesSelfColumn() { - try (MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock - = mockStatic(SecurityFrameworkUtils.class)) { - // 准备参数 - String tableName = "t_user"; - Alias tableAlias = new Alias("u"); - // mock 方法(LoginUser) - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) - .setUserType(UserTypeEnum.ADMIN.getValue())); - securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); - // mock 方法(DeptDataPermissionRespDTO) - DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() - .setDeptIds(CollUtil.newLinkedHashSet(10L, 20L)).setSelf(true); - when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission)); - // 添加 user 字段配置 - rule.addUserColumn("t_user", "id"); - // 添加 dept 字段配置 - rule.addDeptColumn("t_user", "dept_id"); - - // 调用 - Expression expression = rule.getExpression(tableName, tableAlias); - // 断言 - assertEquals("(u.dept_id IN (10, 20) OR u.id = 1)", expression.toString()); - assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); - } - } - -} diff --git a/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/util/DataPermissionUtilsTest.java b/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/util/DataPermissionUtilsTest.java deleted file mode 100644 index a020683..0000000 --- a/iailab-framework/iailab-common-biz-data-permission/src/test/java/com/iailab/framework/datapermission/core/util/DataPermissionUtilsTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.iailab.framework.datapermission.core.util; - -import com.iailab.framework.datapermission.core.aop.DataPermissionContextHolder; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -public class DataPermissionUtilsTest { - - @Test - public void testExecuteIgnore() { - DataPermissionUtils.executeIgnore(() -> assertFalse(DataPermissionContextHolder.get().enable())); - } - -} diff --git a/iailab-framework/iailab-common-biz-ip/pom.xml b/iailab-framework/iailab-common-biz-ip/pom.xml deleted file mode 100644 index 9ca6b36..0000000 --- a/iailab-framework/iailab-common-biz-ip/pom.xml +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>com.iailab</groupId> - <artifactId>iailab-framework</artifactId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-biz-ip</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description>IP 拓展,支持如下功能: - 1. IP 功能:查询 IP 对应的城市信息 - 基于 https://gitee.com/lionsoul/ip2region 实现 - 2. 城市功能:查询城市编码对应的城市信息 - 基于 https://github.com/modood/Administrative-divisions-of-China 实现 - </description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common</artifactId> - </dependency> - - <!-- IP地址检索 --> - <dependency> - <groupId>org.lionsoul</groupId> - <artifactId>ip2region</artifactId> - </dependency> - - <dependency> - <groupId>org.projectlombok</groupId> - <artifactId>lombok</artifactId> - </dependency> - - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>slf4j-api</artifactId> - <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 --> - </dependency> - - <!-- Test 测试相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-test</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/Area.java b/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/Area.java deleted file mode 100644 index fba926e..0000000 --- a/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/Area.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.iailab.framework.ip.core; - -import com.fasterxml.jackson.annotation.JsonManagedReference; -import com.iailab.framework.ip.core.enums.AreaTypeEnum; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.ToString; - -import java.util.List; - -/** - * 区域节点,包括国家、省份、城市、地区等信息 - * - * 数据可见 resources/area.csv 文件 - * - * @author iailab - */ -@Data -@AllArgsConstructor -@NoArgsConstructor -@ToString(exclude = {"parent"}) -public class Area { - - /** - * 编号 - 全球,即根目录 - */ - public static final Integer ID_GLOBAL = 0; - /** - * 编号 - 中国 - */ - public static final Integer ID_CHINA = 1; - - /** - * 编号 - */ - private Integer id; - /** - * 名字 - */ - private String name; - /** - * 类型 - * - * 枚举 {@link AreaTypeEnum} - */ - private Integer type; - - /** - * 父节点 - */ - @JsonManagedReference - private Area parent; - /** - * 子节点 - */ - @JsonManagedReference - private List<Area> children; - -} diff --git a/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/enums/AreaTypeEnum.java b/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/enums/AreaTypeEnum.java deleted file mode 100644 index ceed9b9..0000000 --- a/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/enums/AreaTypeEnum.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.iailab.framework.ip.core.enums; - -import com.iailab.framework.common.core.IntArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * 区域类型枚举 - * - * @author iailab - */ -@AllArgsConstructor -@Getter -public enum AreaTypeEnum implements IntArrayValuable { - - COUNTRY(1, "国家"), - PROVINCE(2, "省份"), - CITY(3, "城市"), - DISTRICT(4, "地区"), // 县、镇、区等 - ; - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AreaTypeEnum::getType).toArray(); - - /** - * 类型 - */ - private final Integer type; - /** - * 名字 - */ - private final String name; - - @Override - public int[] array() { - return ARRAYS; - } -} diff --git a/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/utils/AreaUtils.java b/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/utils/AreaUtils.java deleted file mode 100644 index 93298d6..0000000 --- a/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/utils/AreaUtils.java +++ /dev/null @@ -1,214 +0,0 @@ -package com.iailab.framework.ip.core.utils; - -import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.text.csv.CsvRow; -import cn.hutool.core.text.csv.CsvUtil; -import com.iailab.framework.common.util.object.ObjectUtils; -import com.iailab.framework.ip.core.Area; -import com.iailab.framework.ip.core.enums.AreaTypeEnum; -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; - -import static com.iailab.framework.common.util.collection.CollectionUtils.convertList; -import static com.iailab.framework.common.util.collection.CollectionUtils.findFirst; - -/** - * 区域工具类 - * - * @author iailab - */ -@Slf4j -public class AreaUtils { - - /** - * 初始化 SEARCHER - */ - @SuppressWarnings("InstantiationOfUtilityClass") - private final static AreaUtils INSTANCE = new AreaUtils(); - - /** - * Area 内存缓存,提升访问速度 - */ - private static Map<Integer, Area> areas; - - private AreaUtils() { - long now = System.currentTimeMillis(); - areas = new HashMap<>(); - areas.put(Area.ID_GLOBAL, new Area(Area.ID_GLOBAL, "全球", 0, - null, new ArrayList<>())); - // 从 csv 中加载数据 - List<CsvRow> rows = CsvUtil.getReader().read(ResourceUtil.getUtf8Reader("area.csv")).getRows(); - rows.remove(0); // 删除 header - for (CsvRow row : rows) { - // 创建 Area 对象 - Area area = new Area(Integer.valueOf(row.get(0)), row.get(1), Integer.valueOf(row.get(2)), - null, new ArrayList<>()); - // 添加到 areas 中 - areas.put(area.getId(), area); - } - - // 构建父子关系:因为 Area 中没有 parentId 字段,所以需要重复读取 - for (CsvRow row : rows) { - Area area = areas.get(Integer.valueOf(row.get(0))); // 自己 - Area parent = areas.get(Integer.valueOf(row.get(3))); // 父 - Assert.isTrue(area != parent, "{}:父子节点相同", area.getName()); - area.setParent(parent); - parent.getChildren().add(area); - } - log.info("启动加载 AreaUtils 成功,耗时 ({}) 毫秒", System.currentTimeMillis() - now); - } - - /** - * 获得指定编号对应的区域 - * - * @param id 区域编号 - * @return 区域 - */ - public static Area getArea(Integer id) { - return areas.get(id); - } - - /** - * 获得指定区域对应的编号 - * - * @param pathStr 区域路径,例如说:河南省/石家庄市/新华区 - * @return 区域 - */ - public static Area parseArea(String pathStr) { - String[] paths = pathStr.split("/"); - Area area = null; - for (String path : paths) { - if (area == null) { - area = findFirst(areas.values(), item -> item.getName().equals(path)); - } else { - area = findFirst(area.getChildren(), item -> item.getName().equals(path)); - } - } - return area; - } - - /** - * 获取所有节点的全路径名称如:河南省/石家庄市/新华区 - * - * @param areas 地区树 - * @return 所有节点的全路径名称 - */ - public static List<String> getAreaNodePathList(List<Area> areas) { - List<String> paths = new ArrayList<>(); - areas.forEach(area -> getAreaNodePathList(area, "", paths)); - return paths; - } - - /** - * 构建一棵树的所有节点的全路径名称,并将其存储为 "祖先/父级/子级" 的形式 - * - * @param node 父节点 - * @param path 全路径名称 - * @param paths 全路径名称列表,省份/城市/地区 - */ - private static void getAreaNodePathList(Area node, String path, List<String> paths) { - if (node == null) { - return; - } - // 构建当前节点的路径 - String currentPath = path.isEmpty() ? node.getName() : path + "/" + node.getName(); - paths.add(currentPath); - // 递归遍历子节点 - for (Area child : node.getChildren()) { - getAreaNodePathList(child, currentPath, paths); - } - } - - /** - * 格式化区域 - * - * @param id 区域编号 - * @return 格式化后的区域 - */ - public static String format(Integer id) { - return format(id, " "); - } - - /** - * 格式化区域 - * - * 例如说: - * 1. id = “静安区”时:上海 上海市 静安区 - * 2. id = “上海市”时:上海 上海市 - * 3. id = “上海”时:上海 - * 4. id = “美国”时:美国 - * 当区域在中国时,默认不显示中国 - * - * @param id 区域编号 - * @param separator 分隔符 - * @return 格式化后的区域 - */ - public static String format(Integer id, String separator) { - // 获得区域 - Area area = areas.get(id); - if (area == null) { - return null; - } - - // 格式化 - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < AreaTypeEnum.values().length; i++) { // 避免死循环 - sb.insert(0, area.getName()); - // “递归”父节点 - area = area.getParent(); - if (area == null - || ObjectUtils.equalsAny(area.getId(), Area.ID_GLOBAL, Area.ID_CHINA)) { // 跳过父节点为中国的情况 - break; - } - sb.insert(0, separator); - } - return sb.toString(); - } - - /** - * 获取指定类型的区域列表 - * - * @param type 区域类型 - * @param func 转换函数 - * @param <T> 结果类型 - * @return 区域列表 - */ - public static <T> List<T> getByType(AreaTypeEnum type, Function<Area, T> func) { - return convertList(areas.values(), func, area -> type.getType().equals(area.getType())); - } - - /** - * 根据区域编号、上级区域类型,获取上级区域编号 - * - * @param id 区域编号 - * @param type 区域类型 - * @return 上级区域编号 - */ - public static Integer getParentIdByType(Integer id, @NonNull AreaTypeEnum type) { - for (int i = 0; i < Byte.MAX_VALUE; i++) { - Area area = AreaUtils.getArea(id); - if (area == null) { - return null; - } - // 情况一:匹配到,返回它 - if (type.getType().equals(area.getType())) { - return area.getId(); - } - // 情况二:找到根节点,返回空 - if (area.getParent() == null || area.getParent().getId() == null) { - return null; - } - // 其它:继续向上查找 - id = area.getParent().getId(); - } - return null; - } - -} diff --git a/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/utils/IPUtils.java b/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/utils/IPUtils.java deleted file mode 100644 index a14d515..0000000 --- a/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/utils/IPUtils.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.iailab.framework.ip.core.utils; - -import cn.hutool.core.io.resource.ResourceUtil; -import com.iailab.framework.ip.core.Area; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.lionsoul.ip2region.xdb.Searcher; - -import java.io.IOException; - -/** - * IP 工具类 - * - * IP 数据源来自 ip2region.xdb 精简版,基于 <a href="https://gitee.com/zhijiantianya/ip2region"/> 项目 - * - * @author wanglhup - */ -@Slf4j -public class IPUtils { - - /** - * 初始化 SEARCHER - */ - @SuppressWarnings("InstantiationOfUtilityClass") - private final static IPUtils INSTANCE = new IPUtils(); - - /** - * IP 查询器,启动加载到内存中 - */ - private static Searcher SEARCHER; - - /** - * 私有化构造 - */ - private IPUtils() { - try { - long now = System.currentTimeMillis(); - byte[] bytes = ResourceUtil.readBytes("ip2region.xdb"); - SEARCHER = Searcher.newWithBuffer(bytes); - log.info("启动加载 IPUtils 成功,耗时 ({}) 毫秒", System.currentTimeMillis() - now); - } catch (IOException e) { - log.error("启动加载 IPUtils 失败", e); - } - } - - /** - * 查询 IP 对应的地区编号 - * - * @param ip IP 地址,格式为 127.0.0.1 - * @return 地区id - */ - @SneakyThrows - public static Integer getAreaId(String ip) { - return Integer.parseInt(SEARCHER.search(ip.trim())); - } - - /** - * 查询 IP 对应的地区编号 - * - * @param ip IP 地址的时间戳,格式参考{@link Searcher#checkIP(String)} 的返回 - * @return 地区编号 - */ - @SneakyThrows - public static Integer getAreaId(long ip) { - return Integer.parseInt(SEARCHER.search(ip)); - } - - /** - * 查询 IP 对应的地区 - * - * @param ip IP 地址,格式为 127.0.0.1 - * @return 地区 - */ - public static Area getArea(String ip) { - return AreaUtils.getArea(getAreaId(ip)); - } - - /** - * 查询 IP 对应的地区 - * - * @param ip IP 地址的时间戳,格式参考{@link Searcher#checkIP(String)} 的返回 - * @return 地区 - */ - public static Area getArea(long ip) { - return AreaUtils.getArea(getAreaId(ip)); - } -} diff --git a/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/package-info.java b/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/package-info.java deleted file mode 100644 index 666af73..0000000 --- a/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/package-info.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * IP 拓展,支持如下功能: - * - * 1. IP 功能:查询 IP 对应的城市信息 - * 基于 https://gitee.com/lionsoul/ip2region 实现 - * 2. 城市功能:查询城市编码对应的城市信息 - * 基于 https://github.com/modood/Administrative-divisions-of-China 实现 - * - * @author iailab - */ -package com.iailab.framework.ip; diff --git a/iailab-framework/iailab-common-biz-ip/src/main/resources/area.csv b/iailab-framework/iailab-common-biz-ip/src/main/resources/area.csv deleted file mode 100644 index 06954ba..0000000 --- a/iailab-framework/iailab-common-biz-ip/src/main/resources/area.csv +++ /dev/null @@ -1,3662 +0,0 @@ -id,name,type,parentId -1,中国,1,0 -2,蒙古,1,0 -3,朝鲜,1,0 -4,韩国,1,0 -5,日本,1,0 -6,菲律宾,1,0 -7,越南,1,0 -8,老挝,1,0 -9,柬埔寨,1,0 -10,缅甸,1,0 -11,泰国,1,0 -12,马来西亚,1,0 -13,文莱,1,0 -14,新加坡,1,0 -15,印度尼西亚,1,0 -16,东帝汶,1,0 -17,尼泊尔,1,0 -18,不丹,1,0 -19,孟加拉国,1,0 -20,印度,1,0 -21,巴基斯坦,1,0 -22,斯里兰卡,1,0 -23,马尔代夫,1,0 -24,哈萨克斯坦,1,0 -25,吉尔吉斯斯坦,1,0 -26,塔吉克斯坦,1,0 -27,乌兹别克斯坦,1,0 -28,土库曼斯坦,1,0 -29,阿富汗,1,0 -30,伊拉克,1,0 -31,伊朗,1,0 -32,叙利亚,1,0 -33,约旦,1,0 -34,黎巴嫩,1,0 -35,以色列,1,0 -36,巴勒斯坦,1,0 -37,沙特阿拉伯,1,0 -38,巴林,1,0 -39,卡塔尔,1,0 -40,科威特,1,0 -41,阿拉伯联合酋长国,1,0 -42,阿曼,1,0 -43,也门,1,0 -44,格鲁吉亚,1,0 -45,亚美尼亚,1,0 -46,阿塞拜疆,1,0 -47,土耳其,1,0 -48,塞浦路斯,1,0 -49,芬兰,1,0 -50,瑞典,1,0 -51,挪威,1,0 -52,冰岛,1,0 -53,丹麦,1,0 -54,爱沙尼亚,1,0 -55,拉脱维亚,1,0 -56,立陶宛,1,0 -57,白俄罗斯,1,0 -58,俄罗斯,1,0 -59,乌克兰,1,0 -60,摩尔多瓦,1,0 -61,波兰,1,0 -62,捷克,1,0 -63,斯洛伐克,1,0 -64,匈牙利,1,0 -65,德国,1,0 -66,奥地利,1,0 -67,瑞士,1,0 -68,列支敦士登,1,0 -69,英国,1,0 -70,爱尔兰,1,0 -71,荷兰,1,0 -72,比利时,1,0 -73,卢森堡,1,0 -74,法国,1,0 -75,摩纳哥,1,0 -76,罗马尼亚,1,0 -77,保加利亚,1,0 -78,塞尔维亚,1,0 -79,马其顿,1,0 -80,阿尔巴尼亚,1,0 -81,希腊,1,0 -82,斯洛文尼亚,1,0 -83,克罗地亚,1,0 -84,波斯尼亚和墨塞哥维那,1,0 -85,意大利,1,0 -86,梵蒂冈,1,0 -87,圣马力诺,1,0 -88,马耳他,1,0 -89,西班牙,1,0 -90,葡萄牙,1,0 -91,安道尔共和国,1,0 -92,埃及,1,0 -93,利比亚,1,0 -94,苏丹,1,0 -95,突尼斯,1,0 -96,阿尔及利亚,1,0 -97,摩洛哥,1,0 -98,亚速尔群岛,1,0 -99,马德拉群岛,1,0 -100,埃塞俄比亚,1,0 -101,厄立特里亚,1,0 -102,索马里,1,0 -103,吉布提,1,0 -104,肯尼亚,1,0 -105,坦桑尼亚,1,0 -106,乌干达,1,0 -107,卢旺达,1,0 -108,布隆迪,1,0 -109,塞舌尔,1,0 -110,圣多美及普林西比,1,0 -111,塞内加尔,1,0 -112,冈比亚,1,0 -113,马里,1,0 -114,布基纳法索,1,0 -115,几内亚,1,0 -116,几内亚比绍,1,0 -117,佛得角,1,0 -118,塞拉利昂,1,0 -119,利比里亚,1,0 -120,科特迪瓦,1,0 -121,加纳,1,0 -122,多哥,1,0 -123,贝宁,1,0 -124,尼日尔,1,0 -125,加那利群岛,1,0 -126,赞比亚,1,0 -127,安哥拉,1,0 -128,津巴布韦,1,0 -129,马拉维,1,0 -130,莫桑比克,1,0 -131,博茨瓦纳,1,0 -132,纳米比亚,1,0 -133,南非,1,0 -134,斯威士兰,1,0 -135,莱索托,1,0 -136,马达加斯加,1,0 -137,科摩罗,1,0 -138,毛里求斯,1,0 -139,留尼旺,1,0 -140,圣赫勒拿,1,0 -141,澳大利亚,1,0 -142,新西兰,1,0 -143,巴布亚新几内亚,1,0 -144,所罗门群岛,1,0 -145,瓦努阿图共和国,1,0 -146,密克罗尼西亚,1,0 -147,马绍尔群岛,1,0 -148,帕劳,1,0 -149,瑙鲁,1,0 -150,基里巴斯,1,0 -151,图瓦卢,1,0 -152,萨摩亚,1,0 -153,斐济,1,0 -154,汤加,1,0 -155,库克群岛,1,0 -156,关岛,1,0 -157,新喀里多尼亚,1,0 -158,法属波利尼西亚,1,0 -159,皮特凯恩岛,1,0 -160,瓦利斯与富图纳,1,0 -161,纽埃,1,0 -162,托克劳,1,0 -163,美属萨摩亚,1,0 -164,北马里亚纳,1,0 -165,加拿大,1,0 -166,美国,1,0 -167,墨西哥,1,0 -168,格陵兰,1,0 -169,危地马拉,1,0 -170,伯利兹,1,0 -171,萨尔瓦多,1,0 -172,洪都拉斯,1,0 -173,尼加拉瓜,1,0 -174,哥斯达黎加,1,0 -175,巴拿马,1,0 -176,巴哈马,1,0 -177,古巴,1,0 -178,牙买加,1,0 -179,海地,1,0 -180,多米尼加共和国,1,0 -181,安提瓜和巴布达,1,0 -182,圣基茨和尼维斯,1,0 -183,多米尼克,1,0 -184,圣卢西亚,1,0 -185,圣文森特和格林纳丁斯,1,0 -186,格林纳达,1,0 -187,巴巴多斯,1,0 -188,特立尼达和多巴哥,1,0 -189,波多黎各,1,0 -190,英属维尔京群岛,1,0 -191,美属维尔京群岛,1,0 -192,安圭拉,1,0 -193,蒙特塞拉特岛,1,0 -194,瓜德罗普,1,0 -195,马提尼克,1,0 -196,荷属安的列斯,1,0 -197,阿鲁巴,1,0 -198,特克斯和凯科斯群岛,1,0 -199,开曼群岛,1,0 -200,百慕大,1,0 -201,哥伦比亚,1,0 -202,委内瑞拉,1,0 -203,圭亚那,1,0 -204,法属圭亚那,1,0 -205,苏里南,1,0 -206,厄瓜多尔,1,0 -207,秘鲁,1,0 -208,玻利维亚,1,0 -209,巴西,1,0 -210,智利,1,0 -211,阿根廷,1,0 -212,乌拉圭,1,0 -213,巴拉圭,1,0 -214,波黑,1,0 -215,直布罗陀,1,0 -216,新喀里多尼亚群岛,1,0 -217,瓦利斯和富图纳群岛,1,0 -218,泽西岛,1,0 -219,黑山,1,0 -220,英属马恩岛,1,0 -221,尼日利亚,1,0 -222,喀麦隆,1,0 -223,加蓬,1,0 -224,乍得,1,0 -225,刚果共和国,1,0 -226,中非共和国,1,0 -227,南苏丹,1,0 -228,赤道几内亚,1,0 -229,毛里塔尼亚,1,0 -230,刚果民主共和国,1,0 -231,留尼汪岛,1,0 -232,格陵兰岛,1,0 -233,法罗群岛,1,0 -234,根西岛,1,0 -235,百慕大群岛,1,0 -236,圣皮埃尔和密克隆群岛,1,0 -237,法属圣马丁,1,0 -238,奥兰群岛,1,0 -239,北马里亚纳群岛,1,0 -240,库拉索,1,0 -241,博内尔岛,1,0 -242,圣马丁岛,1,0 -243,圣巴泰勒米岛,1,0 -244,福克兰群岛,1,0 -245,圣多美和普林西比,1,0 -246,英属印度洋领地,1,0 -247,东萨摩亚,1,0 -248,诺福克岛,1,0 -110000,北京,2,1 -120000,天津,2,1 -130000,河北省,2,1 -140000,山西省,2,1 -150000,内蒙古自治区,2,1 -210000,辽宁省,2,1 -220000,吉林省,2,1 -230000,黑龙江省,2,1 -310000,上海,2,1 -320000,江苏省,2,1 -330000,浙江省,2,1 -340000,安徽省,2,1 -350000,福建省,2,1 -360000,江西省,2,1 -370000,山东省,2,1 -410000,河南省,2,1 -420000,湖北省,2,1 -430000,湖南省,2,1 -440000,广东省,2,1 -450000,广西壮族自治区,2,1 -460000,海南省,2,1 -500000,重庆,2,1 -510000,四川省,2,1 -520000,贵州省,2,1 -530000,云南省,2,1 -540000,西藏自治区,2,1 -610000,陕西省,2,1 -620000,甘肃省,2,1 -630000,青海省,2,1 -640000,宁夏回族自治区,2,1 -650000,新疆维吾尔自治区,2,1 -110100,北京市,3,110000 -120100,天津市,3,120000 -130100,石家庄市,3,130000 -130200,唐山市,3,130000 -130300,秦皇岛市,3,130000 -130400,邯郸市,3,130000 -130500,邢台市,3,130000 -130600,保定市,3,130000 -130700,张家口市,3,130000 -130800,承德市,3,130000 -130900,沧州市,3,130000 -131000,廊坊市,3,130000 -131100,衡水市,3,130000 -140100,太原市,3,140000 -140200,大同市,3,140000 -140300,阳泉市,3,140000 -140400,长治市,3,140000 -140500,晋城市,3,140000 -140600,朔州市,3,140000 -140700,晋中市,3,140000 -140800,运城市,3,140000 -140900,忻州市,3,140000 -141000,临汾市,3,140000 -141100,吕梁市,3,140000 -150100,呼和浩特市,3,150000 -150200,包头市,3,150000 -150300,乌海市,3,150000 -150400,赤峰市,3,150000 -150500,通辽市,3,150000 -150600,鄂尔多斯市,3,150000 -150700,呼伦贝尔市,3,150000 -150800,巴彦淖尔市,3,150000 -150900,乌兰察布市,3,150000 -152200,兴安盟,3,150000 -152500,锡林郭勒盟,3,150000 -152900,阿拉善盟,3,150000 -210100,沈阳市,3,210000 -210200,大连市,3,210000 -210300,鞍山市,3,210000 -210400,抚顺市,3,210000 -210500,本溪市,3,210000 -210600,丹东市,3,210000 -210700,锦州市,3,210000 -210800,营口市,3,210000 -210900,阜新市,3,210000 -211000,辽阳市,3,210000 -211100,盘锦市,3,210000 -211200,铁岭市,3,210000 -211300,朝阳市,3,210000 -211400,葫芦岛市,3,210000 -220100,长春市,3,220000 -220200,吉林市,3,220000 -220300,四平市,3,220000 -220400,辽源市,3,220000 -220500,通化市,3,220000 -220600,白山市,3,220000 -220700,松原市,3,220000 -220800,白城市,3,220000 -222400,延边朝鲜族自治州,3,220000 -230100,哈尔滨市,3,230000 -230200,齐齐哈尔市,3,230000 -230300,鸡西市,3,230000 -230400,鹤岗市,3,230000 -230500,双鸭山市,3,230000 -230600,大庆市,3,230000 -230700,伊春市,3,230000 -230800,佳木斯市,3,230000 -230900,七台河市,3,230000 -231000,牡丹江市,3,230000 -231100,黑河市,3,230000 -231200,绥化市,3,230000 -232700,大兴安岭地区,3,230000 -310100,上海市,3,310000 -320100,南京市,3,320000 -320200,无锡市,3,320000 -320300,徐州市,3,320000 -320400,常州市,3,320000 -320500,苏州市,3,320000 -320600,南通市,3,320000 -320700,连云港市,3,320000 -320800,淮安市,3,320000 -320900,盐城市,3,320000 -321000,扬州市,3,320000 -321100,镇江市,3,320000 -321200,泰州市,3,320000 -321300,宿迁市,3,320000 -330100,杭州市,3,330000 -330200,宁波市,3,330000 -330300,温州市,3,330000 -330400,嘉兴市,3,330000 -330500,湖州市,3,330000 -330600,绍兴市,3,330000 -330700,金华市,3,330000 -330800,衢州市,3,330000 -330900,舟山市,3,330000 -331000,台州市,3,330000 -331100,丽水市,3,330000 -340100,合肥市,3,340000 -340200,芜湖市,3,340000 -340300,蚌埠市,3,340000 -340400,淮南市,3,340000 -340500,马鞍山市,3,340000 -340600,淮北市,3,340000 -340700,铜陵市,3,340000 -340800,安庆市,3,340000 -341000,黄山市,3,340000 -341100,滁州市,3,340000 -341200,阜阳市,3,340000 -341300,宿州市,3,340000 -341500,六安市,3,340000 -341600,亳州市,3,340000 -341700,池州市,3,340000 -341800,宣城市,3,340000 -350100,福州市,3,350000 -350200,厦门市,3,350000 -350300,莆田市,3,350000 -350400,三明市,3,350000 -350500,泉州市,3,350000 -350600,漳州市,3,350000 -350700,南平市,3,350000 -350800,龙岩市,3,350000 -350900,宁德市,3,350000 -360100,南昌市,3,360000 -360200,景德镇市,3,360000 -360300,萍乡市,3,360000 -360400,九江市,3,360000 -360500,新余市,3,360000 -360600,鹰潭市,3,360000 -360700,赣州市,3,360000 -360800,吉安市,3,360000 -360900,宜春市,3,360000 -361000,抚州市,3,360000 -361100,上饶市,3,360000 -370100,济南市,3,370000 -370200,青岛市,3,370000 -370300,淄博市,3,370000 -370400,枣庄市,3,370000 -370500,东营市,3,370000 -370600,烟台市,3,370000 -370700,潍坊市,3,370000 -370800,济宁市,3,370000 -370900,泰安市,3,370000 -371000,威海市,3,370000 -371100,日照市,3,370000 -371300,临沂市,3,370000 -371400,德州市,3,370000 -371500,聊城市,3,370000 -371600,滨州市,3,370000 -371700,菏泽市,3,370000 -410100,郑州市,3,410000 -410200,开封市,3,410000 -410300,洛阳市,3,410000 -410400,平顶山市,3,410000 -410500,安阳市,3,410000 -410600,鹤壁市,3,410000 -410700,新乡市,3,410000 -410800,焦作市,3,410000 -410900,濮阳市,3,410000 -411000,许昌市,3,410000 -411100,漯河市,3,410000 -411200,三门峡市,3,410000 -411300,南阳市,3,410000 -411400,商丘市,3,410000 -411500,信阳市,3,410000 -411600,周口市,3,410000 -411700,驻马店市,3,410000 -419000,省直辖县级行政区划,3,410000 -420100,武汉市,3,420000 -420200,黄石市,3,420000 -420300,十堰市,3,420000 -420500,宜昌市,3,420000 -420600,襄阳市,3,420000 -420700,鄂州市,3,420000 -420800,荆门市,3,420000 -420900,孝感市,3,420000 -421000,荆州市,3,420000 -421100,黄冈市,3,420000 -421200,咸宁市,3,420000 -421300,随州市,3,420000 -422800,恩施土家族苗族自治州,3,420000 -429000,省直辖县级行政区划,3,420000 -430100,长沙市,3,430000 -430200,株洲市,3,430000 -430300,湘潭市,3,430000 -430400,衡阳市,3,430000 -430500,邵阳市,3,430000 -430600,岳阳市,3,430000 -430700,常德市,3,430000 -430800,张家界市,3,430000 -430900,益阳市,3,430000 -431000,郴州市,3,430000 -431100,永州市,3,430000 -431200,怀化市,3,430000 -431300,娄底市,3,430000 -433100,湘西土家族苗族自治州,3,430000 -440100,广州市,3,440000 -440200,韶关市,3,440000 -440300,深圳市,3,440000 -440400,珠海市,3,440000 -440500,汕头市,3,440000 -440600,佛山市,3,440000 -440700,江门市,3,440000 -440800,湛江市,3,440000 -440900,茂名市,3,440000 -441200,肇庆市,3,440000 -441300,惠州市,3,440000 -441400,梅州市,3,440000 -441500,汕尾市,3,440000 -441600,河源市,3,440000 -441700,阳江市,3,440000 -441800,清远市,3,440000 -441900,东莞市,3,440000 -441901,莞城区,4,441900 -441902,南城区,4,441900 -441904,万江区,4,441900 -441905,石碣镇,4,441900 -441906,石龙镇,4,441900 -441907,茶山镇,4,441900 -441908,石排镇,4,441900 -441909,企石镇,4,441900 -441910,横沥镇,4,441900 -441911,桥头镇,4,441900 -441912,谢岗镇,4,441900 -441913,东坑镇,4,441900 -441914,常平镇,4,441900 -441915,寮步镇,4,441900 -441916,大朗镇,4,441900 -441917,麻涌镇,4,441900 -441918,中堂镇,4,441900 -441919,高埗镇,4,441900 -441920,樟木头镇,4,441900 -441921,大岭山镇,4,441900 -441922,望牛墩镇,4,441900 -441923,黄江镇,4,441900 -441924,洪梅镇,4,441900 -441925,清溪镇,4,441900 -441926,沙田镇,4,441900 -441927,道滘镇,4,441900 -441928,塘厦镇,4,441900 -441929,虎门镇,4,441900 -441930,厚街镇,4,441900 -441931,凤岗镇,4,441900 -441932,长安镇,4,441900 -442000,中山市,3,440000 -442001,石岐街道,4,442000 -442002,东区街道,4,442000 -442003,中山港街道,4,442000 -442004,西区街道,4,442000 -442005,南区街道,4,442000 -442006,五桂山街道,4,442000 -442007,民众街道,4,442000 -442008,南朗街道,4,442000 -442009,黄圃镇,4,442000 -442010,东凤镇,4,442000 -442011,古镇镇,4,442000 -442012,沙溪镇,4,442000 -442013,坦洲镇,4,442000 -442014,港口镇,4,442000 -442015,三角镇,4,442000 -442016,横栏镇,4,442000 -442017,南头镇,4,442000 -442018,阜沙镇,4,442000 -442019,三乡镇,4,442000 -442020,板芙镇,4,442000 -442021,大涌镇,4,442000 -442022,神湾镇,4,442000 -442023,小榄镇,4,442000 -445100,潮州市,3,440000 -445200,揭阳市,3,440000 -445300,云浮市,3,440000 -450100,南宁市,3,450000 -450200,柳州市,3,450000 -450300,桂林市,3,450000 -450400,梧州市,3,450000 -450500,北海市,3,450000 -450600,防城港市,3,450000 -450700,钦州市,3,450000 -450800,贵港市,3,450000 -450900,玉林市,3,450000 -451000,百色市,3,450000 -451100,贺州市,3,450000 -451200,河池市,3,450000 -451300,来宾市,3,450000 -451400,崇左市,3,450000 -460100,海口市,3,460000 -460200,三亚市,3,460000 -460300,三沙市,3,460000 -460400,儋州市,3,460000 -469000,省直辖县级行政区划,3,460000 -500100,重庆市,3,500000 -510100,成都市,3,510000 -510300,自贡市,3,510000 -510400,攀枝花市,3,510000 -510500,泸州市,3,510000 -510600,德阳市,3,510000 -510700,绵阳市,3,510000 -510800,广元市,3,510000 -510900,遂宁市,3,510000 -511000,内江市,3,510000 -511100,乐山市,3,510000 -511300,南充市,3,510000 -511400,眉山市,3,510000 -511500,宜宾市,3,510000 -511600,广安市,3,510000 -511700,达州市,3,510000 -511800,雅安市,3,510000 -511900,巴中市,3,510000 -512000,资阳市,3,510000 -513200,阿坝藏族羌族自治州,3,510000 -513300,甘孜藏族自治州,3,510000 -513400,凉山彝族自治州,3,510000 -520100,贵阳市,3,520000 -520200,六盘水市,3,520000 -520300,遵义市,3,520000 -520400,安顺市,3,520000 -520500,毕节市,3,520000 -520600,铜仁市,3,520000 -522300,黔西南布依族苗族自治州,3,520000 -522600,黔东南苗族侗族自治州,3,520000 -522700,黔南布依族苗族自治州,3,520000 -530100,昆明市,3,530000 -530300,曲靖市,3,530000 -530400,玉溪市,3,530000 -530500,保山市,3,530000 -530600,昭通市,3,530000 -530700,丽江市,3,530000 -530800,普洱市,3,530000 -530900,临沧市,3,530000 -532300,楚雄彝族自治州,3,530000 -532500,红河哈尼族彝族自治州,3,530000 -532600,文山壮族苗族自治州,3,530000 -532800,西双版纳傣族自治州,3,530000 -532900,大理白族自治州,3,530000 -533100,德宏傣族景颇族自治州,3,530000 -533300,怒江傈僳族自治州,3,530000 -533400,迪庆藏族自治州,3,530000 -540100,拉萨市,3,540000 -540200,日喀则市,3,540000 -540300,昌都市,3,540000 -540400,林芝市,3,540000 -540500,山南市,3,540000 -540600,那曲市,3,540000 -542500,阿里地区,3,540000 -610100,西安市,3,610000 -610200,铜川市,3,610000 -610300,宝鸡市,3,610000 -610400,咸阳市,3,610000 -610500,渭南市,3,610000 -610600,延安市,3,610000 -610700,汉中市,3,610000 -610800,榆林市,3,610000 -610900,安康市,3,610000 -611000,商洛市,3,610000 -620100,兰州市,3,620000 -620200,嘉峪关市,3,620000 -620300,金昌市,3,620000 -620400,白银市,3,620000 -620500,天水市,3,620000 -620600,武威市,3,620000 -620700,张掖市,3,620000 -620800,平凉市,3,620000 -620900,酒泉市,3,620000 -621000,庆阳市,3,620000 -621100,定西市,3,620000 -621200,陇南市,3,620000 -622900,临夏回族自治州,3,620000 -623000,甘南藏族自治州,3,620000 -630100,西宁市,3,630000 -630200,海东市,3,630000 -632200,海北藏族自治州,3,630000 -632300,黄南藏族自治州,3,630000 -632500,海南藏族自治州,3,630000 -632600,果洛藏族自治州,3,630000 -632700,玉树藏族自治州,3,630000 -632800,海西蒙古族藏族自治州,3,630000 -640100,银川市,3,640000 -640200,石嘴山市,3,640000 -640300,吴忠市,3,640000 -640400,固原市,3,640000 -640500,中卫市,3,640000 -650100,乌鲁木齐市,3,650000 -650200,克拉玛依市,3,650000 -650400,吐鲁番市,3,650000 -650500,哈密市,3,650000 -652300,昌吉回族自治州,3,650000 -652700,博尔塔拉蒙古自治州,3,650000 -652800,巴音郭楞蒙古自治州,3,650000 -652900,阿克苏地区,3,650000 -653000,克孜勒苏柯尔克孜自治州,3,650000 -653100,喀什地区,3,650000 -653200,和田地区,3,650000 -654000,伊犁哈萨克自治州,3,650000 -654200,塔城地区,3,650000 -654300,阿勒泰地区,3,650000 -659000,自治区直辖县级行政区划,3,650000 -110101,东城区,4,110100 -110102,西城区,4,110100 -110105,朝阳区,4,110100 -110106,丰台区,4,110100 -110107,石景山区,4,110100 -110108,海淀区,4,110100 -110109,门头沟区,4,110100 -110111,房山区,4,110100 -110112,通州区,4,110100 -110113,顺义区,4,110100 -110114,昌平区,4,110100 -110115,大兴区,4,110100 -110116,怀柔区,4,110100 -110117,平谷区,4,110100 -110118,密云区,4,110100 -110119,延庆区,4,110100 -120101,和平区,4,120100 -120102,河东区,4,120100 -120103,河西区,4,120100 -120104,南开区,4,120100 -120105,河北区,4,120100 -120106,红桥区,4,120100 -120110,东丽区,4,120100 -120111,西青区,4,120100 -120112,津南区,4,120100 -120113,北辰区,4,120100 -120114,武清区,4,120100 -120115,宝坻区,4,120100 -120116,滨海新区,4,120100 -120117,宁河区,4,120100 -120118,静海区,4,120100 -120119,蓟州区,4,120100 -130102,长安区,4,130100 -130104,桥西区,4,130100 -130105,新华区,4,130100 -130107,井陉矿区,4,130100 -130108,裕华区,4,130100 -130109,藁城区,4,130100 -130110,鹿泉区,4,130100 -130111,栾城区,4,130100 -130121,井陉县,4,130100 -130123,正定县,4,130100 -130125,行唐县,4,130100 -130126,灵寿县,4,130100 -130127,高邑县,4,130100 -130128,深泽县,4,130100 -130129,赞皇县,4,130100 -130130,无极县,4,130100 -130131,平山县,4,130100 -130132,元氏县,4,130100 -130133,赵县,4,130100 -130171,石家庄高新技术产业开发区,4,130100 -130172,石家庄循环化工园区,4,130100 -130181,辛集市,4,130100 -130183,晋州市,4,130100 -130184,新乐市,4,130100 -130202,路南区,4,130200 -130203,路北区,4,130200 -130204,古冶区,4,130200 -130205,开平区,4,130200 -130207,丰南区,4,130200 -130208,丰润区,4,130200 -130209,曹妃甸区,4,130200 -130224,滦南县,4,130200 -130225,乐亭县,4,130200 -130227,迁西县,4,130200 -130229,玉田县,4,130200 -130271,河北唐山芦台经济开发区,4,130200 -130272,唐山市汉沽管理区,4,130200 -130273,唐山高新技术产业开发区,4,130200 -130274,河北唐山海港经济开发区,4,130200 -130281,遵化市,4,130200 -130283,迁安市,4,130200 -130284,滦州市,4,130200 -130302,海港区,4,130300 -130303,山海关区,4,130300 -130304,北戴河区,4,130300 -130306,抚宁区,4,130300 -130321,青龙满族自治县,4,130300 -130322,昌黎县,4,130300 -130324,卢龙县,4,130300 -130371,秦皇岛市经济技术开发区,4,130300 -130372,北戴河新区,4,130300 -130402,邯山区,4,130400 -130403,丛台区,4,130400 -130404,复兴区,4,130400 -130406,峰峰矿区,4,130400 -130407,肥乡区,4,130400 -130408,永年区,4,130400 -130423,临漳县,4,130400 -130424,成安县,4,130400 -130425,大名县,4,130400 -130426,涉县,4,130400 -130427,磁县,4,130400 -130430,邱县,4,130400 -130431,鸡泽县,4,130400 -130432,广平县,4,130400 -130433,馆陶县,4,130400 -130434,魏县,4,130400 -130435,曲周县,4,130400 -130471,邯郸经济技术开发区,4,130400 -130473,邯郸冀南新区,4,130400 -130481,武安市,4,130400 -130502,襄都区,4,130500 -130503,信都区,4,130500 -130505,任泽区,4,130500 -130506,南和区,4,130500 -130522,临城县,4,130500 -130523,内丘县,4,130500 -130524,柏乡县,4,130500 -130525,隆尧县,4,130500 -130528,宁晋县,4,130500 -130529,巨鹿县,4,130500 -130530,新河县,4,130500 -130531,广宗县,4,130500 -130532,平乡县,4,130500 -130533,威县,4,130500 -130534,清河县,4,130500 -130535,临西县,4,130500 -130571,河北邢台经济开发区,4,130500 -130581,南宫市,4,130500 -130582,沙河市,4,130500 -130602,竞秀区,4,130600 -130606,莲池区,4,130600 -130607,满城区,4,130600 -130608,清苑区,4,130600 -130609,徐水区,4,130600 -130623,涞水县,4,130600 -130624,阜平县,4,130600 -130626,定兴县,4,130600 -130627,唐县,4,130600 -130628,高阳县,4,130600 -130629,容城县,4,130600 -130630,涞源县,4,130600 -130631,望都县,4,130600 -130632,安新县,4,130600 -130633,易县,4,130600 -130634,曲阳县,4,130600 -130635,蠡县,4,130600 -130636,顺平县,4,130600 -130637,博野县,4,130600 -130638,雄县,4,130600 -130671,保定高新技术产业开发区,4,130600 -130672,保定白沟新城,4,130600 -130681,涿州市,4,130600 -130682,定州市,4,130600 -130683,安国市,4,130600 -130684,高碑店市,4,130600 -130702,桥东区,4,130700 -130703,桥西区,4,130700 -130705,宣化区,4,130700 -130706,下花园区,4,130700 -130708,万全区,4,130700 -130709,崇礼区,4,130700 -130722,张北县,4,130700 -130723,康保县,4,130700 -130724,沽源县,4,130700 -130725,尚义县,4,130700 -130726,蔚县,4,130700 -130727,阳原县,4,130700 -130728,怀安县,4,130700 -130730,怀来县,4,130700 -130731,涿鹿县,4,130700 -130732,赤城县,4,130700 -130771,张家口经济开发区,4,130700 -130772,张家口市察北管理区,4,130700 -130773,张家口市塞北管理区,4,130700 -130802,双桥区,4,130800 -130803,双滦区,4,130800 -130804,鹰手营子矿区,4,130800 -130821,承德县,4,130800 -130822,兴隆县,4,130800 -130824,滦平县,4,130800 -130825,隆化县,4,130800 -130826,丰宁满族自治县,4,130800 -130827,宽城满族自治县,4,130800 -130828,围场满族蒙古族自治县,4,130800 -130871,承德高新技术产业开发区,4,130800 -130881,平泉市,4,130800 -130902,新华区,4,130900 -130903,运河区,4,130900 -130921,沧县,4,130900 -130922,青县,4,130900 -130923,东光县,4,130900 -130924,海兴县,4,130900 -130925,盐山县,4,130900 -130926,肃宁县,4,130900 -130927,南皮县,4,130900 -130928,吴桥县,4,130900 -130929,献县,4,130900 -130930,孟村回族自治县,4,130900 -130971,河北沧州经济开发区,4,130900 -130972,沧州高新技术产业开发区,4,130900 -130973,沧州渤海新区,4,130900 -130981,泊头市,4,130900 -130982,任丘市,4,130900 -130983,黄骅市,4,130900 -130984,河间市,4,130900 -131002,安次区,4,131000 -131003,广阳区,4,131000 -131022,固安县,4,131000 -131023,永清县,4,131000 -131024,香河县,4,131000 -131025,大城县,4,131000 -131026,文安县,4,131000 -131028,大厂回族自治县,4,131000 -131071,廊坊经济技术开发区,4,131000 -131081,霸州市,4,131000 -131082,三河市,4,131000 -131102,桃城区,4,131100 -131103,冀州区,4,131100 -131121,枣强县,4,131100 -131122,武邑县,4,131100 -131123,武强县,4,131100 -131124,饶阳县,4,131100 -131125,安平县,4,131100 -131126,故城县,4,131100 -131127,景县,4,131100 -131128,阜城县,4,131100 -131171,河北衡水高新技术产业开发区,4,131100 -131172,衡水滨湖新区,4,131100 -131182,深州市,4,131100 -140105,小店区,4,140100 -140106,迎泽区,4,140100 -140107,杏花岭区,4,140100 -140108,尖草坪区,4,140100 -140109,万柏林区,4,140100 -140110,晋源区,4,140100 -140121,清徐县,4,140100 -140122,阳曲县,4,140100 -140123,娄烦县,4,140100 -140171,山西转型综合改革示范区,4,140100 -140181,古交市,4,140100 -140212,新荣区,4,140200 -140213,平城区,4,140200 -140214,云冈区,4,140200 -140215,云州区,4,140200 -140221,阳高县,4,140200 -140222,天镇县,4,140200 -140223,广灵县,4,140200 -140224,灵丘县,4,140200 -140225,浑源县,4,140200 -140226,左云县,4,140200 -140271,山西大同经济开发区,4,140200 -140302,城区,4,140300 -140303,矿区,4,140300 -140311,郊区,4,140300 -140321,平定县,4,140300 -140322,盂县,4,140300 -140403,潞州区,4,140400 -140404,上党区,4,140400 -140405,屯留区,4,140400 -140406,潞城区,4,140400 -140423,襄垣县,4,140400 -140425,平顺县,4,140400 -140426,黎城县,4,140400 -140427,壶关县,4,140400 -140428,长子县,4,140400 -140429,武乡县,4,140400 -140430,沁县,4,140400 -140431,沁源县,4,140400 -140471,山西长治高新技术产业园区,4,140400 -140502,城区,4,140500 -140521,沁水县,4,140500 -140522,阳城县,4,140500 -140524,陵川县,4,140500 -140525,泽州县,4,140500 -140581,高平市,4,140500 -140602,朔城区,4,140600 -140603,平鲁区,4,140600 -140621,山阴县,4,140600 -140622,应县,4,140600 -140623,右玉县,4,140600 -140671,山西朔州经济开发区,4,140600 -140681,怀仁市,4,140600 -140702,榆次区,4,140700 -140703,太谷区,4,140700 -140721,榆社县,4,140700 -140722,左权县,4,140700 -140723,和顺县,4,140700 -140724,昔阳县,4,140700 -140725,寿阳县,4,140700 -140727,祁县,4,140700 -140728,平遥县,4,140700 -140729,灵石县,4,140700 -140781,介休市,4,140700 -140802,盐湖区,4,140800 -140821,临猗县,4,140800 -140822,万荣县,4,140800 -140823,闻喜县,4,140800 -140824,稷山县,4,140800 -140825,新绛县,4,140800 -140826,绛县,4,140800 -140827,垣曲县,4,140800 -140828,夏县,4,140800 -140829,平陆县,4,140800 -140830,芮城县,4,140800 -140881,永济市,4,140800 -140882,河津市,4,140800 -140902,忻府区,4,140900 -140921,定襄县,4,140900 -140922,五台县,4,140900 -140923,代县,4,140900 -140924,繁峙县,4,140900 -140925,宁武县,4,140900 -140926,静乐县,4,140900 -140927,神池县,4,140900 -140928,五寨县,4,140900 -140929,岢岚县,4,140900 -140930,河曲县,4,140900 -140931,保德县,4,140900 -140932,偏关县,4,140900 -140971,五台山风景名胜区,4,140900 -140981,原平市,4,140900 -141002,尧都区,4,141000 -141021,曲沃县,4,141000 -141022,翼城县,4,141000 -141023,襄汾县,4,141000 -141024,洪洞县,4,141000 -141025,古县,4,141000 -141026,安泽县,4,141000 -141027,浮山县,4,141000 -141028,吉县,4,141000 -141029,乡宁县,4,141000 -141030,大宁县,4,141000 -141031,隰县,4,141000 -141032,永和县,4,141000 -141033,蒲县,4,141000 -141034,汾西县,4,141000 -141081,侯马市,4,141000 -141082,霍州市,4,141000 -141102,离石区,4,141100 -141121,文水县,4,141100 -141122,交城县,4,141100 -141123,兴县,4,141100 -141124,临县,4,141100 -141125,柳林县,4,141100 -141126,石楼县,4,141100 -141127,岚县,4,141100 -141128,方山县,4,141100 -141129,中阳县,4,141100 -141130,交口县,4,141100 -141181,孝义市,4,141100 -141182,汾阳市,4,141100 -150102,新城区,4,150100 -150103,回民区,4,150100 -150104,玉泉区,4,150100 -150105,赛罕区,4,150100 -150121,土默特左旗,4,150100 -150122,托克托县,4,150100 -150123,和林格尔县,4,150100 -150124,清水河县,4,150100 -150125,武川县,4,150100 -150172,呼和浩特经济技术开发区,4,150100 -150202,东河区,4,150200 -150203,昆都仑区,4,150200 -150204,青山区,4,150200 -150205,石拐区,4,150200 -150206,白云鄂博矿区,4,150200 -150207,九原区,4,150200 -150221,土默特右旗,4,150200 -150222,固阳县,4,150200 -150223,达尔罕茂明安联合旗,4,150200 -150271,包头稀土高新技术产业开发区,4,150200 -150302,海勃湾区,4,150300 -150303,海南区,4,150300 -150304,乌达区,4,150300 -150402,红山区,4,150400 -150403,元宝山区,4,150400 -150404,松山区,4,150400 -150421,阿鲁科尔沁旗,4,150400 -150422,巴林左旗,4,150400 -150423,巴林右旗,4,150400 -150424,林西县,4,150400 -150425,克什克腾旗,4,150400 -150426,翁牛特旗,4,150400 -150428,喀喇沁旗,4,150400 -150429,宁城县,4,150400 -150430,敖汉旗,4,150400 -150502,科尔沁区,4,150500 -150521,科尔沁左翼中旗,4,150500 -150522,科尔沁左翼后旗,4,150500 -150523,开鲁县,4,150500 -150524,库伦旗,4,150500 -150525,奈曼旗,4,150500 -150526,扎鲁特旗,4,150500 -150571,通辽经济技术开发区,4,150500 -150581,霍林郭勒市,4,150500 -150602,东胜区,4,150600 -150603,康巴什区,4,150600 -150621,达拉特旗,4,150600 -150622,准格尔旗,4,150600 -150623,鄂托克前旗,4,150600 -150624,鄂托克旗,4,150600 -150625,杭锦旗,4,150600 -150626,乌审旗,4,150600 -150627,伊金霍洛旗,4,150600 -150702,海拉尔区,4,150700 -150703,扎赉诺尔区,4,150700 -150721,阿荣旗,4,150700 -150722,莫力达瓦达斡尔族自治旗,4,150700 -150723,鄂伦春自治旗,4,150700 -150724,鄂温克族自治旗,4,150700 -150725,陈巴尔虎旗,4,150700 -150726,新巴尔虎左旗,4,150700 -150727,新巴尔虎右旗,4,150700 -150781,满洲里市,4,150700 -150782,牙克石市,4,150700 -150783,扎兰屯市,4,150700 -150784,额尔古纳市,4,150700 -150785,根河市,4,150700 -150802,临河区,4,150800 -150821,五原县,4,150800 -150822,磴口县,4,150800 -150823,乌拉特前旗,4,150800 -150824,乌拉特中旗,4,150800 -150825,乌拉特后旗,4,150800 -150826,杭锦后旗,4,150800 -150902,集宁区,4,150900 -150921,卓资县,4,150900 -150922,化德县,4,150900 -150923,商都县,4,150900 -150924,兴和县,4,150900 -150925,凉城县,4,150900 -150926,察哈尔右翼前旗,4,150900 -150927,察哈尔右翼中旗,4,150900 -150928,察哈尔右翼后旗,4,150900 -150929,四子王旗,4,150900 -150981,丰镇市,4,150900 -152201,乌兰浩特市,4,152200 -152202,阿尔山市,4,152200 -152221,科尔沁右翼前旗,4,152200 -152222,科尔沁右翼中旗,4,152200 -152223,扎赉特旗,4,152200 -152224,突泉县,4,152200 -152501,二连浩特市,4,152500 -152502,锡林浩特市,4,152500 -152522,阿巴嘎旗,4,152500 -152523,苏尼特左旗,4,152500 -152524,苏尼特右旗,4,152500 -152525,东乌珠穆沁旗,4,152500 -152526,西乌珠穆沁旗,4,152500 -152527,太仆寺旗,4,152500 -152528,镶黄旗,4,152500 -152529,正镶白旗,4,152500 -152530,正蓝旗,4,152500 -152531,多伦县,4,152500 -152571,乌拉盖管委会,4,152500 -152921,阿拉善左旗,4,152900 -152922,阿拉善右旗,4,152900 -152923,额济纳旗,4,152900 -152971,内蒙古阿拉善高新技术产业开发区,4,152900 -210102,和平区,4,210100 -210103,沈河区,4,210100 -210104,大东区,4,210100 -210105,皇姑区,4,210100 -210106,铁西区,4,210100 -210111,苏家屯区,4,210100 -210112,浑南区,4,210100 -210113,沈北新区,4,210100 -210114,于洪区,4,210100 -210115,辽中区,4,210100 -210123,康平县,4,210100 -210124,法库县,4,210100 -210181,新民市,4,210100 -210202,中山区,4,210200 -210203,西岗区,4,210200 -210204,沙河口区,4,210200 -210211,甘井子区,4,210200 -210212,旅顺口区,4,210200 -210213,金州区,4,210200 -210214,普兰店区,4,210200 -210224,长海县,4,210200 -210281,瓦房店市,4,210200 -210283,庄河市,4,210200 -210302,铁东区,4,210300 -210303,铁西区,4,210300 -210304,立山区,4,210300 -210311,千山区,4,210300 -210321,台安县,4,210300 -210323,岫岩满族自治县,4,210300 -210381,海城市,4,210300 -210402,新抚区,4,210400 -210403,东洲区,4,210400 -210404,望花区,4,210400 -210411,顺城区,4,210400 -210421,抚顺县,4,210400 -210422,新宾满族自治县,4,210400 -210423,清原满族自治县,4,210400 -210502,平山区,4,210500 -210503,溪湖区,4,210500 -210504,明山区,4,210500 -210505,南芬区,4,210500 -210521,本溪满族自治县,4,210500 -210522,桓仁满族自治县,4,210500 -210602,元宝区,4,210600 -210603,振兴区,4,210600 -210604,振安区,4,210600 -210624,宽甸满族自治县,4,210600 -210681,东港市,4,210600 -210682,凤城市,4,210600 -210702,古塔区,4,210700 -210703,凌河区,4,210700 -210711,太和区,4,210700 -210726,黑山县,4,210700 -210727,义县,4,210700 -210781,凌海市,4,210700 -210782,北镇市,4,210700 -210802,站前区,4,210800 -210803,西市区,4,210800 -210804,鲅鱼圈区,4,210800 -210811,老边区,4,210800 -210881,盖州市,4,210800 -210882,大石桥市,4,210800 -210902,海州区,4,210900 -210903,新邱区,4,210900 -210904,太平区,4,210900 -210905,清河门区,4,210900 -210911,细河区,4,210900 -210921,阜新蒙古族自治县,4,210900 -210922,彰武县,4,210900 -211002,白塔区,4,211000 -211003,文圣区,4,211000 -211004,宏伟区,4,211000 -211005,弓长岭区,4,211000 -211011,太子河区,4,211000 -211021,辽阳县,4,211000 -211081,灯塔市,4,211000 -211102,双台子区,4,211100 -211103,兴隆台区,4,211100 -211104,大洼区,4,211100 -211122,盘山县,4,211100 -211202,银州区,4,211200 -211204,清河区,4,211200 -211221,铁岭县,4,211200 -211223,西丰县,4,211200 -211224,昌图县,4,211200 -211281,调兵山市,4,211200 -211282,开原市,4,211200 -211302,双塔区,4,211300 -211303,龙城区,4,211300 -211321,朝阳县,4,211300 -211322,建平县,4,211300 -211324,喀喇沁左翼蒙古族自治县,4,211300 -211381,北票市,4,211300 -211382,凌源市,4,211300 -211402,连山区,4,211400 -211403,龙港区,4,211400 -211404,南票区,4,211400 -211421,绥中县,4,211400 -211422,建昌县,4,211400 -211481,兴城市,4,211400 -220102,南关区,4,220100 -220103,宽城区,4,220100 -220104,朝阳区,4,220100 -220105,二道区,4,220100 -220106,绿园区,4,220100 -220112,双阳区,4,220100 -220113,九台区,4,220100 -220122,农安县,4,220100 -220171,长春经济技术开发区,4,220100 -220172,长春净月高新技术产业开发区,4,220100 -220173,长春高新技术产业开发区,4,220100 -220174,长春汽车经济技术开发区,4,220100 -220182,榆树市,4,220100 -220183,德惠市,4,220100 -220184,公主岭市,4,220100 -220202,昌邑区,4,220200 -220203,龙潭区,4,220200 -220204,船营区,4,220200 -220211,丰满区,4,220200 -220221,永吉县,4,220200 -220271,吉林经济开发区,4,220200 -220272,吉林高新技术产业开发区,4,220200 -220273,吉林中国新加坡食品区,4,220200 -220281,蛟河市,4,220200 -220282,桦甸市,4,220200 -220283,舒兰市,4,220200 -220284,磐石市,4,220200 -220302,铁西区,4,220300 -220303,铁东区,4,220300 -220322,梨树县,4,220300 -220323,伊通满族自治县,4,220300 -220382,双辽市,4,220300 -220402,龙山区,4,220400 -220403,西安区,4,220400 -220421,东丰县,4,220400 -220422,东辽县,4,220400 -220502,东昌区,4,220500 -220503,二道江区,4,220500 -220521,通化县,4,220500 -220523,辉南县,4,220500 -220524,柳河县,4,220500 -220581,梅河口市,4,220500 -220582,集安市,4,220500 -220602,浑江区,4,220600 -220605,江源区,4,220600 -220621,抚松县,4,220600 -220622,靖宇县,4,220600 -220623,长白朝鲜族自治县,4,220600 -220681,临江市,4,220600 -220702,宁江区,4,220700 -220721,前郭尔罗斯蒙古族自治县,4,220700 -220722,长岭县,4,220700 -220723,乾安县,4,220700 -220771,吉林松原经济开发区,4,220700 -220781,扶余市,4,220700 -220802,洮北区,4,220800 -220821,镇赉县,4,220800 -220822,通榆县,4,220800 -220871,吉林白城经济开发区,4,220800 -220881,洮南市,4,220800 -220882,大安市,4,220800 -222401,延吉市,4,222400 -222402,图们市,4,222400 -222403,敦化市,4,222400 -222404,珲春市,4,222400 -222405,龙井市,4,222400 -222406,和龙市,4,222400 -222424,汪清县,4,222400 -222426,安图县,4,222400 -230102,道里区,4,230100 -230103,南岗区,4,230100 -230104,道外区,4,230100 -230108,平房区,4,230100 -230109,松北区,4,230100 -230110,香坊区,4,230100 -230111,呼兰区,4,230100 -230112,阿城区,4,230100 -230113,双城区,4,230100 -230123,依兰县,4,230100 -230124,方正县,4,230100 -230125,宾县,4,230100 -230126,巴彦县,4,230100 -230127,木兰县,4,230100 -230128,通河县,4,230100 -230129,延寿县,4,230100 -230183,尚志市,4,230100 -230184,五常市,4,230100 -230202,龙沙区,4,230200 -230203,建华区,4,230200 -230204,铁锋区,4,230200 -230205,昂昂溪区,4,230200 -230206,富拉尔基区,4,230200 -230207,碾子山区,4,230200 -230208,梅里斯达斡尔族区,4,230200 -230221,龙江县,4,230200 -230223,依安县,4,230200 -230224,泰来县,4,230200 -230225,甘南县,4,230200 -230227,富裕县,4,230200 -230229,克山县,4,230200 -230230,克东县,4,230200 -230231,拜泉县,4,230200 -230281,讷河市,4,230200 -230302,鸡冠区,4,230300 -230303,恒山区,4,230300 -230304,滴道区,4,230300 -230305,梨树区,4,230300 -230306,城子河区,4,230300 -230307,麻山区,4,230300 -230321,鸡东县,4,230300 -230381,虎林市,4,230300 -230382,密山市,4,230300 -230402,向阳区,4,230400 -230403,工农区,4,230400 -230404,南山区,4,230400 -230405,兴安区,4,230400 -230406,东山区,4,230400 -230407,兴山区,4,230400 -230421,萝北县,4,230400 -230422,绥滨县,4,230400 -230502,尖山区,4,230500 -230503,岭东区,4,230500 -230505,四方台区,4,230500 -230506,宝山区,4,230500 -230521,集贤县,4,230500 -230522,友谊县,4,230500 -230523,宝清县,4,230500 -230524,饶河县,4,230500 -230602,萨尔图区,4,230600 -230603,龙凤区,4,230600 -230604,让胡路区,4,230600 -230605,红岗区,4,230600 -230606,大同区,4,230600 -230621,肇州县,4,230600 -230622,肇源县,4,230600 -230623,林甸县,4,230600 -230624,杜尔伯特蒙古族自治县,4,230600 -230671,大庆高新技术产业开发区,4,230600 -230717,伊美区,4,230700 -230718,乌翠区,4,230700 -230719,友好区,4,230700 -230722,嘉荫县,4,230700 -230723,汤旺县,4,230700 -230724,丰林县,4,230700 -230725,大箐山县,4,230700 -230726,南岔县,4,230700 -230751,金林区,4,230700 -230781,铁力市,4,230700 -230803,向阳区,4,230800 -230804,前进区,4,230800 -230805,东风区,4,230800 -230811,郊区,4,230800 -230822,桦南县,4,230800 -230826,桦川县,4,230800 -230828,汤原县,4,230800 -230881,同江市,4,230800 -230882,富锦市,4,230800 -230883,抚远市,4,230800 -230902,新兴区,4,230900 -230903,桃山区,4,230900 -230904,茄子河区,4,230900 -230921,勃利县,4,230900 -231002,东安区,4,231000 -231003,阳明区,4,231000 -231004,爱民区,4,231000 -231005,西安区,4,231000 -231025,林口县,4,231000 -231071,牡丹江经济技术开发区,4,231000 -231081,绥芬河市,4,231000 -231083,海林市,4,231000 -231084,宁安市,4,231000 -231085,穆棱市,4,231000 -231086,东宁市,4,231000 -231102,爱辉区,4,231100 -231123,逊克县,4,231100 -231124,孙吴县,4,231100 -231181,北安市,4,231100 -231182,五大连池市,4,231100 -231183,嫩江市,4,231100 -231202,北林区,4,231200 -231221,望奎县,4,231200 -231222,兰西县,4,231200 -231223,青冈县,4,231200 -231224,庆安县,4,231200 -231225,明水县,4,231200 -231226,绥棱县,4,231200 -231281,安达市,4,231200 -231282,肇东市,4,231200 -231283,海伦市,4,231200 -232701,漠河市,4,232700 -232721,呼玛县,4,232700 -232722,塔河县,4,232700 -232761,加格达奇区,4,232700 -232762,松岭区,4,232700 -232763,新林区,4,232700 -232764,呼中区,4,232700 -310101,黄浦区,4,310100 -310104,徐汇区,4,310100 -310105,长宁区,4,310100 -310106,静安区,4,310100 -310107,普陀区,4,310100 -310109,虹口区,4,310100 -310110,杨浦区,4,310100 -310112,闵行区,4,310100 -310113,宝山区,4,310100 -310114,嘉定区,4,310100 -310115,浦东新区,4,310100 -310116,金山区,4,310100 -310117,松江区,4,310100 -310118,青浦区,4,310100 -310120,奉贤区,4,310100 -310151,崇明区,4,310100 -320102,玄武区,4,320100 -320104,秦淮区,4,320100 -320105,建邺区,4,320100 -320106,鼓楼区,4,320100 -320111,浦口区,4,320100 -320113,栖霞区,4,320100 -320114,雨花台区,4,320100 -320115,江宁区,4,320100 -320116,六合区,4,320100 -320117,溧水区,4,320100 -320118,高淳区,4,320100 -320205,锡山区,4,320200 -320206,惠山区,4,320200 -320211,滨湖区,4,320200 -320213,梁溪区,4,320200 -320214,新吴区,4,320200 -320281,江阴市,4,320200 -320282,宜兴市,4,320200 -320302,鼓楼区,4,320300 -320303,云龙区,4,320300 -320305,贾汪区,4,320300 -320311,泉山区,4,320300 -320312,铜山区,4,320300 -320321,丰县,4,320300 -320322,沛县,4,320300 -320324,睢宁县,4,320300 -320371,徐州经济技术开发区,4,320300 -320381,新沂市,4,320300 -320382,邳州市,4,320300 -320402,天宁区,4,320400 -320404,钟楼区,4,320400 -320411,新北区,4,320400 -320412,武进区,4,320400 -320413,金坛区,4,320400 -320481,溧阳市,4,320400 -320505,虎丘区,4,320500 -320506,吴中区,4,320500 -320507,相城区,4,320500 -320508,姑苏区,4,320500 -320509,吴江区,4,320500 -320571,苏州工业园区,4,320500 -320581,常熟市,4,320500 -320582,张家港市,4,320500 -320583,昆山市,4,320500 -320585,太仓市,4,320500 -320612,通州区,4,320600 -320613,崇川区,4,320600 -320614,海门区,4,320600 -320623,如东县,4,320600 -320671,南通经济技术开发区,4,320600 -320681,启东市,4,320600 -320682,如皋市,4,320600 -320685,海安市,4,320600 -320703,连云区,4,320700 -320706,海州区,4,320700 -320707,赣榆区,4,320700 -320722,东海县,4,320700 -320723,灌云县,4,320700 -320724,灌南县,4,320700 -320771,连云港经济技术开发区,4,320700 -320772,连云港高新技术产业开发区,4,320700 -320803,淮安区,4,320800 -320804,淮阴区,4,320800 -320812,清江浦区,4,320800 -320813,洪泽区,4,320800 -320826,涟水县,4,320800 -320830,盱眙县,4,320800 -320831,金湖县,4,320800 -320871,淮安经济技术开发区,4,320800 -320902,亭湖区,4,320900 -320903,盐都区,4,320900 -320904,大丰区,4,320900 -320921,响水县,4,320900 -320922,滨海县,4,320900 -320923,阜宁县,4,320900 -320924,射阳县,4,320900 -320925,建湖县,4,320900 -320971,盐城经济技术开发区,4,320900 -320981,东台市,4,320900 -321002,广陵区,4,321000 -321003,邗江区,4,321000 -321012,江都区,4,321000 -321023,宝应县,4,321000 -321071,扬州经济技术开发区,4,321000 -321081,仪征市,4,321000 -321084,高邮市,4,321000 -321102,京口区,4,321100 -321111,润州区,4,321100 -321112,丹徒区,4,321100 -321171,镇江新区,4,321100 -321181,丹阳市,4,321100 -321182,扬中市,4,321100 -321183,句容市,4,321100 -321202,海陵区,4,321200 -321203,高港区,4,321200 -321204,姜堰区,4,321200 -321271,泰州医药高新技术产业开发区,4,321200 -321281,兴化市,4,321200 -321282,靖江市,4,321200 -321283,泰兴市,4,321200 -321302,宿城区,4,321300 -321311,宿豫区,4,321300 -321322,沭阳县,4,321300 -321323,泗阳县,4,321300 -321324,泗洪县,4,321300 -321371,宿迁经济技术开发区,4,321300 -330102,上城区,4,330100 -330105,拱墅区,4,330100 -330106,西湖区,4,330100 -330108,滨江区,4,330100 -330109,萧山区,4,330100 -330110,余杭区,4,330100 -330111,富阳区,4,330100 -330112,临安区,4,330100 -330113,临平区,4,330100 -330114,钱塘区,4,330100 -330122,桐庐县,4,330100 -330127,淳安县,4,330100 -330182,建德市,4,330100 -330203,海曙区,4,330200 -330205,江北区,4,330200 -330206,北仑区,4,330200 -330211,镇海区,4,330200 -330212,鄞州区,4,330200 -330213,奉化区,4,330200 -330225,象山县,4,330200 -330226,宁海县,4,330200 -330281,余姚市,4,330200 -330282,慈溪市,4,330200 -330302,鹿城区,4,330300 -330303,龙湾区,4,330300 -330304,瓯海区,4,330300 -330305,洞头区,4,330300 -330324,永嘉县,4,330300 -330326,平阳县,4,330300 -330327,苍南县,4,330300 -330328,文成县,4,330300 -330329,泰顺县,4,330300 -330371,温州经济技术开发区,4,330300 -330381,瑞安市,4,330300 -330382,乐清市,4,330300 -330383,龙港市,4,330300 -330402,南湖区,4,330400 -330411,秀洲区,4,330400 -330421,嘉善县,4,330400 -330424,海盐县,4,330400 -330481,海宁市,4,330400 -330482,平湖市,4,330400 -330483,桐乡市,4,330400 -330502,吴兴区,4,330500 -330503,南浔区,4,330500 -330521,德清县,4,330500 -330522,长兴县,4,330500 -330523,安吉县,4,330500 -330602,越城区,4,330600 -330603,柯桥区,4,330600 -330604,上虞区,4,330600 -330624,新昌县,4,330600 -330681,诸暨市,4,330600 -330683,嵊州市,4,330600 -330702,婺城区,4,330700 -330703,金东区,4,330700 -330723,武义县,4,330700 -330726,浦江县,4,330700 -330727,磐安县,4,330700 -330781,兰溪市,4,330700 -330782,义乌市,4,330700 -330783,东阳市,4,330700 -330784,永康市,4,330700 -330802,柯城区,4,330800 -330803,衢江区,4,330800 -330822,常山县,4,330800 -330824,开化县,4,330800 -330825,龙游县,4,330800 -330881,江山市,4,330800 -330902,定海区,4,330900 -330903,普陀区,4,330900 -330921,岱山县,4,330900 -330922,嵊泗县,4,330900 -331002,椒江区,4,331000 -331003,黄岩区,4,331000 -331004,路桥区,4,331000 -331022,三门县,4,331000 -331023,天台县,4,331000 -331024,仙居县,4,331000 -331081,温岭市,4,331000 -331082,临海市,4,331000 -331083,玉环市,4,331000 -331102,莲都区,4,331100 -331121,青田县,4,331100 -331122,缙云县,4,331100 -331123,遂昌县,4,331100 -331124,松阳县,4,331100 -331125,云和县,4,331100 -331126,庆元县,4,331100 -331127,景宁畲族自治县,4,331100 -331181,龙泉市,4,331100 -340102,瑶海区,4,340100 -340103,庐阳区,4,340100 -340104,蜀山区,4,340100 -340111,包河区,4,340100 -340121,长丰县,4,340100 -340122,肥东县,4,340100 -340123,肥西县,4,340100 -340124,庐江县,4,340100 -340171,合肥高新技术产业开发区,4,340100 -340172,合肥经济技术开发区,4,340100 -340173,合肥新站高新技术产业开发区,4,340100 -340181,巢湖市,4,340100 -340202,镜湖区,4,340200 -340207,鸠江区,4,340200 -340209,弋江区,4,340200 -340210,湾沚区,4,340200 -340212,繁昌区,4,340200 -340223,南陵县,4,340200 -340271,芜湖经济技术开发区,4,340200 -340272,安徽芜湖三山经济开发区,4,340200 -340281,无为市,4,340200 -340302,龙子湖区,4,340300 -340303,蚌山区,4,340300 -340304,禹会区,4,340300 -340311,淮上区,4,340300 -340321,怀远县,4,340300 -340322,五河县,4,340300 -340323,固镇县,4,340300 -340371,蚌埠市高新技术开发区,4,340300 -340372,蚌埠市经济开发区,4,340300 -340402,大通区,4,340400 -340403,田家庵区,4,340400 -340404,谢家集区,4,340400 -340405,八公山区,4,340400 -340406,潘集区,4,340400 -340421,凤台县,4,340400 -340422,寿县,4,340400 -340503,花山区,4,340500 -340504,雨山区,4,340500 -340506,博望区,4,340500 -340521,当涂县,4,340500 -340522,含山县,4,340500 -340523,和县,4,340500 -340602,杜集区,4,340600 -340603,相山区,4,340600 -340604,烈山区,4,340600 -340621,濉溪县,4,340600 -340705,铜官区,4,340700 -340706,义安区,4,340700 -340711,郊区,4,340700 -340722,枞阳县,4,340700 -340802,迎江区,4,340800 -340803,大观区,4,340800 -340811,宜秀区,4,340800 -340822,怀宁县,4,340800 -340825,太湖县,4,340800 -340826,宿松县,4,340800 -340827,望江县,4,340800 -340828,岳西县,4,340800 -340871,安徽安庆经济开发区,4,340800 -340881,桐城市,4,340800 -340882,潜山市,4,340800 -341002,屯溪区,4,341000 -341003,黄山区,4,341000 -341004,徽州区,4,341000 -341021,歙县,4,341000 -341022,休宁县,4,341000 -341023,黟县,4,341000 -341024,祁门县,4,341000 -341102,琅琊区,4,341100 -341103,南谯区,4,341100 -341122,来安县,4,341100 -341124,全椒县,4,341100 -341125,定远县,4,341100 -341126,凤阳县,4,341100 -341171,中新苏滁高新技术产业开发区,4,341100 -341172,滁州经济技术开发区,4,341100 -341181,天长市,4,341100 -341182,明光市,4,341100 -341202,颍州区,4,341200 -341203,颍东区,4,341200 -341204,颍泉区,4,341200 -341221,临泉县,4,341200 -341222,太和县,4,341200 -341225,阜南县,4,341200 -341226,颍上县,4,341200 -341271,阜阳合肥现代产业园区,4,341200 -341272,阜阳经济技术开发区,4,341200 -341282,界首市,4,341200 -341302,埇桥区,4,341300 -341321,砀山县,4,341300 -341322,萧县,4,341300 -341323,灵璧县,4,341300 -341324,泗县,4,341300 -341371,宿州马鞍山现代产业园区,4,341300 -341372,宿州经济技术开发区,4,341300 -341502,金安区,4,341500 -341503,裕安区,4,341500 -341504,叶集区,4,341500 -341522,霍邱县,4,341500 -341523,舒城县,4,341500 -341524,金寨县,4,341500 -341525,霍山县,4,341500 -341602,谯城区,4,341600 -341621,涡阳县,4,341600 -341622,蒙城县,4,341600 -341623,利辛县,4,341600 -341702,贵池区,4,341700 -341721,东至县,4,341700 -341722,石台县,4,341700 -341723,青阳县,4,341700 -341802,宣州区,4,341800 -341821,郎溪县,4,341800 -341823,泾县,4,341800 -341824,绩溪县,4,341800 -341825,旌德县,4,341800 -341871,宣城市经济开发区,4,341800 -341881,宁国市,4,341800 -341882,广德市,4,341800 -350102,鼓楼区,4,350100 -350103,台江区,4,350100 -350104,仓山区,4,350100 -350105,马尾区,4,350100 -350111,晋安区,4,350100 -350112,长乐区,4,350100 -350121,闽侯县,4,350100 -350122,连江县,4,350100 -350123,罗源县,4,350100 -350124,闽清县,4,350100 -350125,永泰县,4,350100 -350128,平潭县,4,350100 -350181,福清市,4,350100 -350203,思明区,4,350200 -350205,海沧区,4,350200 -350206,湖里区,4,350200 -350211,集美区,4,350200 -350212,同安区,4,350200 -350213,翔安区,4,350200 -350302,城厢区,4,350300 -350303,涵江区,4,350300 -350304,荔城区,4,350300 -350305,秀屿区,4,350300 -350322,仙游县,4,350300 -350404,三元区,4,350400 -350405,沙县区,4,350400 -350421,明溪县,4,350400 -350423,清流县,4,350400 -350424,宁化县,4,350400 -350425,大田县,4,350400 -350426,尤溪县,4,350400 -350428,将乐县,4,350400 -350429,泰宁县,4,350400 -350430,建宁县,4,350400 -350481,永安市,4,350400 -350502,鲤城区,4,350500 -350503,丰泽区,4,350500 -350504,洛江区,4,350500 -350505,泉港区,4,350500 -350521,惠安县,4,350500 -350524,安溪县,4,350500 -350525,永春县,4,350500 -350526,德化县,4,350500 -350527,金门县,4,350500 -350581,石狮市,4,350500 -350582,晋江市,4,350500 -350583,南安市,4,350500 -350602,芗城区,4,350600 -350603,龙文区,4,350600 -350604,龙海区,4,350600 -350605,长泰区,4,350600 -350622,云霄县,4,350600 -350623,漳浦县,4,350600 -350624,诏安县,4,350600 -350626,东山县,4,350600 -350627,南靖县,4,350600 -350628,平和县,4,350600 -350629,华安县,4,350600 -350702,延平区,4,350700 -350703,建阳区,4,350700 -350721,顺昌县,4,350700 -350722,浦城县,4,350700 -350723,光泽县,4,350700 -350724,松溪县,4,350700 -350725,政和县,4,350700 -350781,邵武市,4,350700 -350782,武夷山市,4,350700 -350783,建瓯市,4,350700 -350802,新罗区,4,350800 -350803,永定区,4,350800 -350821,长汀县,4,350800 -350823,上杭县,4,350800 -350824,武平县,4,350800 -350825,连城县,4,350800 -350881,漳平市,4,350800 -350902,蕉城区,4,350900 -350921,霞浦县,4,350900 -350922,古田县,4,350900 -350923,屏南县,4,350900 -350924,寿宁县,4,350900 -350925,周宁县,4,350900 -350926,柘荣县,4,350900 -350981,福安市,4,350900 -350982,福鼎市,4,350900 -360102,东湖区,4,360100 -360103,西湖区,4,360100 -360104,青云谱区,4,360100 -360111,青山湖区,4,360100 -360112,新建区,4,360100 -360113,红谷滩区,4,360100 -360121,南昌县,4,360100 -360123,安义县,4,360100 -360124,进贤县,4,360100 -360202,昌江区,4,360200 -360203,珠山区,4,360200 -360222,浮梁县,4,360200 -360281,乐平市,4,360200 -360302,安源区,4,360300 -360313,湘东区,4,360300 -360321,莲花县,4,360300 -360322,上栗县,4,360300 -360323,芦溪县,4,360300 -360402,濂溪区,4,360400 -360403,浔阳区,4,360400 -360404,柴桑区,4,360400 -360423,武宁县,4,360400 -360424,修水县,4,360400 -360425,永修县,4,360400 -360426,德安县,4,360400 -360428,都昌县,4,360400 -360429,湖口县,4,360400 -360430,彭泽县,4,360400 -360481,瑞昌市,4,360400 -360482,共青城市,4,360400 -360483,庐山市,4,360400 -360502,渝水区,4,360500 -360521,分宜县,4,360500 -360602,月湖区,4,360600 -360603,余江区,4,360600 -360681,贵溪市,4,360600 -360702,章贡区,4,360700 -360703,南康区,4,360700 -360704,赣县区,4,360700 -360722,信丰县,4,360700 -360723,大余县,4,360700 -360724,上犹县,4,360700 -360725,崇义县,4,360700 -360726,安远县,4,360700 -360728,定南县,4,360700 -360729,全南县,4,360700 -360730,宁都县,4,360700 -360731,于都县,4,360700 -360732,兴国县,4,360700 -360733,会昌县,4,360700 -360734,寻乌县,4,360700 -360735,石城县,4,360700 -360781,瑞金市,4,360700 -360783,龙南市,4,360700 -360802,吉州区,4,360800 -360803,青原区,4,360800 -360821,吉安县,4,360800 -360822,吉水县,4,360800 -360823,峡江县,4,360800 -360824,新干县,4,360800 -360825,永丰县,4,360800 -360826,泰和县,4,360800 -360827,遂川县,4,360800 -360828,万安县,4,360800 -360829,安福县,4,360800 -360830,永新县,4,360800 -360881,井冈山市,4,360800 -360902,袁州区,4,360900 -360921,奉新县,4,360900 -360922,万载县,4,360900 -360923,上高县,4,360900 -360924,宜丰县,4,360900 -360925,靖安县,4,360900 -360926,铜鼓县,4,360900 -360981,丰城市,4,360900 -360982,樟树市,4,360900 -360983,高安市,4,360900 -361002,临川区,4,361000 -361003,东乡区,4,361000 -361021,南城县,4,361000 -361022,黎川县,4,361000 -361023,南丰县,4,361000 -361024,崇仁县,4,361000 -361025,乐安县,4,361000 -361026,宜黄县,4,361000 -361027,金溪县,4,361000 -361028,资溪县,4,361000 -361030,广昌县,4,361000 -361102,信州区,4,361100 -361103,广丰区,4,361100 -361104,广信区,4,361100 -361123,玉山县,4,361100 -361124,铅山县,4,361100 -361125,横峰县,4,361100 -361126,弋阳县,4,361100 -361127,余干县,4,361100 -361128,鄱阳县,4,361100 -361129,万年县,4,361100 -361130,婺源县,4,361100 -361181,德兴市,4,361100 -370102,历下区,4,370100 -370103,市中区,4,370100 -370104,槐荫区,4,370100 -370105,天桥区,4,370100 -370112,历城区,4,370100 -370113,长清区,4,370100 -370114,章丘区,4,370100 -370115,济阳区,4,370100 -370116,莱芜区,4,370100 -370117,钢城区,4,370100 -370124,平阴县,4,370100 -370126,商河县,4,370100 -370171,济南高新技术产业开发区,4,370100 -370202,市南区,4,370200 -370203,市北区,4,370200 -370211,黄岛区,4,370200 -370212,崂山区,4,370200 -370213,李沧区,4,370200 -370214,城阳区,4,370200 -370215,即墨区,4,370200 -370271,青岛高新技术产业开发区,4,370200 -370281,胶州市,4,370200 -370283,平度市,4,370200 -370285,莱西市,4,370200 -370302,淄川区,4,370300 -370303,张店区,4,370300 -370304,博山区,4,370300 -370305,临淄区,4,370300 -370306,周村区,4,370300 -370321,桓台县,4,370300 -370322,高青县,4,370300 -370323,沂源县,4,370300 -370402,市中区,4,370400 -370403,薛城区,4,370400 -370404,峄城区,4,370400 -370405,台儿庄区,4,370400 -370406,山亭区,4,370400 -370481,滕州市,4,370400 -370502,东营区,4,370500 -370503,河口区,4,370500 -370505,垦利区,4,370500 -370522,利津县,4,370500 -370523,广饶县,4,370500 -370571,东营经济技术开发区,4,370500 -370572,东营港经济开发区,4,370500 -370602,芝罘区,4,370600 -370611,福山区,4,370600 -370612,牟平区,4,370600 -370613,莱山区,4,370600 -370614,蓬莱区,4,370600 -370671,烟台高新技术产业开发区,4,370600 -370672,烟台经济技术开发区,4,370600 -370681,龙口市,4,370600 -370682,莱阳市,4,370600 -370683,莱州市,4,370600 -370685,招远市,4,370600 -370686,栖霞市,4,370600 -370687,海阳市,4,370600 -370702,潍城区,4,370700 -370703,寒亭区,4,370700 -370704,坊子区,4,370700 -370705,奎文区,4,370700 -370724,临朐县,4,370700 -370725,昌乐县,4,370700 -370772,潍坊滨海经济技术开发区,4,370700 -370781,青州市,4,370700 -370782,诸城市,4,370700 -370783,寿光市,4,370700 -370784,安丘市,4,370700 -370785,高密市,4,370700 -370786,昌邑市,4,370700 -370811,任城区,4,370800 -370812,兖州区,4,370800 -370826,微山县,4,370800 -370827,鱼台县,4,370800 -370828,金乡县,4,370800 -370829,嘉祥县,4,370800 -370830,汶上县,4,370800 -370831,泗水县,4,370800 -370832,梁山县,4,370800 -370871,济宁高新技术产业开发区,4,370800 -370881,曲阜市,4,370800 -370883,邹城市,4,370800 -370902,泰山区,4,370900 -370911,岱岳区,4,370900 -370921,宁阳县,4,370900 -370923,东平县,4,370900 -370982,新泰市,4,370900 -370983,肥城市,4,370900 -371002,环翠区,4,371000 -371003,文登区,4,371000 -371071,威海火炬高技术产业开发区,4,371000 -371072,威海经济技术开发区,4,371000 -371073,威海临港经济技术开发区,4,371000 -371082,荣成市,4,371000 -371083,乳山市,4,371000 -371102,东港区,4,371100 -371103,岚山区,4,371100 -371121,五莲县,4,371100 -371122,莒县,4,371100 -371171,日照经济技术开发区,4,371100 -371302,兰山区,4,371300 -371311,罗庄区,4,371300 -371312,河东区,4,371300 -371321,沂南县,4,371300 -371322,郯城县,4,371300 -371323,沂水县,4,371300 -371324,兰陵县,4,371300 -371325,费县,4,371300 -371326,平邑县,4,371300 -371327,莒南县,4,371300 -371328,蒙阴县,4,371300 -371329,临沭县,4,371300 -371371,临沂高新技术产业开发区,4,371300 -371402,德城区,4,371400 -371403,陵城区,4,371400 -371422,宁津县,4,371400 -371423,庆云县,4,371400 -371424,临邑县,4,371400 -371425,齐河县,4,371400 -371426,平原县,4,371400 -371427,夏津县,4,371400 -371428,武城县,4,371400 -371471,德州经济技术开发区,4,371400 -371472,德州运河经济开发区,4,371400 -371481,乐陵市,4,371400 -371482,禹城市,4,371400 -371502,东昌府区,4,371500 -371503,茌平区,4,371500 -371521,阳谷县,4,371500 -371522,莘县,4,371500 -371524,东阿县,4,371500 -371525,冠县,4,371500 -371526,高唐县,4,371500 -371581,临清市,4,371500 -371602,滨城区,4,371600 -371603,沾化区,4,371600 -371621,惠民县,4,371600 -371622,阳信县,4,371600 -371623,无棣县,4,371600 -371625,博兴县,4,371600 -371681,邹平市,4,371600 -371702,牡丹区,4,371700 -371703,定陶区,4,371700 -371721,曹县,4,371700 -371722,单县,4,371700 -371723,成武县,4,371700 -371724,巨野县,4,371700 -371725,郓城县,4,371700 -371726,鄄城县,4,371700 -371728,东明县,4,371700 -371771,菏泽经济技术开发区,4,371700 -371772,菏泽高新技术开发区,4,371700 -410102,中原区,4,410100 -410103,二七区,4,410100 -410104,管城回族区,4,410100 -410105,金水区,4,410100 -410106,上街区,4,410100 -410108,惠济区,4,410100 -410122,中牟县,4,410100 -410171,郑州经济技术开发区,4,410100 -410172,郑州高新技术产业开发区,4,410100 -410173,郑州航空港经济综合实验区,4,410100 -410181,巩义市,4,410100 -410182,荥阳市,4,410100 -410183,新密市,4,410100 -410184,新郑市,4,410100 -410185,登封市,4,410100 -410202,龙亭区,4,410200 -410203,顺河回族区,4,410200 -410204,鼓楼区,4,410200 -410205,禹王台区,4,410200 -410212,祥符区,4,410200 -410221,杞县,4,410200 -410222,通许县,4,410200 -410223,尉氏县,4,410200 -410225,兰考县,4,410200 -410302,老城区,4,410300 -410303,西工区,4,410300 -410304,瀍河回族区,4,410300 -410305,涧西区,4,410300 -410307,偃师区,4,410300 -410308,孟津区,4,410300 -410311,洛龙区,4,410300 -410323,新安县,4,410300 -410324,栾川县,4,410300 -410325,嵩县,4,410300 -410326,汝阳县,4,410300 -410327,宜阳县,4,410300 -410328,洛宁县,4,410300 -410329,伊川县,4,410300 -410371,洛阳高新技术产业开发区,4,410300 -410402,新华区,4,410400 -410403,卫东区,4,410400 -410404,石龙区,4,410400 -410411,湛河区,4,410400 -410421,宝丰县,4,410400 -410422,叶县,4,410400 -410423,鲁山县,4,410400 -410425,郏县,4,410400 -410471,平顶山高新技术产业开发区,4,410400 -410472,平顶山市城乡一体化示范区,4,410400 -410481,舞钢市,4,410400 -410482,汝州市,4,410400 -410502,文峰区,4,410500 -410503,北关区,4,410500 -410505,殷都区,4,410500 -410506,龙安区,4,410500 -410522,安阳县,4,410500 -410523,汤阴县,4,410500 -410526,滑县,4,410500 -410527,内黄县,4,410500 -410571,安阳高新技术产业开发区,4,410500 -410581,林州市,4,410500 -410602,鹤山区,4,410600 -410603,山城区,4,410600 -410611,淇滨区,4,410600 -410621,浚县,4,410600 -410622,淇县,4,410600 -410671,鹤壁经济技术开发区,4,410600 -410702,红旗区,4,410700 -410703,卫滨区,4,410700 -410704,凤泉区,4,410700 -410711,牧野区,4,410700 -410721,新乡县,4,410700 -410724,获嘉县,4,410700 -410725,原阳县,4,410700 -410726,延津县,4,410700 -410727,封丘县,4,410700 -410771,新乡高新技术产业开发区,4,410700 -410772,新乡经济技术开发区,4,410700 -410773,新乡市平原城乡一体化示范区,4,410700 -410781,卫辉市,4,410700 -410782,辉县市,4,410700 -410783,长垣市,4,410700 -410802,解放区,4,410800 -410803,中站区,4,410800 -410804,马村区,4,410800 -410811,山阳区,4,410800 -410821,修武县,4,410800 -410822,博爱县,4,410800 -410823,武陟县,4,410800 -410825,温县,4,410800 -410871,焦作城乡一体化示范区,4,410800 -410882,沁阳市,4,410800 -410883,孟州市,4,410800 -410902,华龙区,4,410900 -410922,清丰县,4,410900 -410923,南乐县,4,410900 -410926,范县,4,410900 -410927,台前县,4,410900 -410928,濮阳县,4,410900 -410971,河南濮阳工业园区,4,410900 -410972,濮阳经济技术开发区,4,410900 -411002,魏都区,4,411000 -411003,建安区,4,411000 -411024,鄢陵县,4,411000 -411025,襄城县,4,411000 -411071,许昌经济技术开发区,4,411000 -411081,禹州市,4,411000 -411082,长葛市,4,411000 -411102,源汇区,4,411100 -411103,郾城区,4,411100 -411104,召陵区,4,411100 -411121,舞阳县,4,411100 -411122,临颍县,4,411100 -411171,漯河经济技术开发区,4,411100 -411202,湖滨区,4,411200 -411203,陕州区,4,411200 -411221,渑池县,4,411200 -411224,卢氏县,4,411200 -411271,河南三门峡经济开发区,4,411200 -411281,义马市,4,411200 -411282,灵宝市,4,411200 -411302,宛城区,4,411300 -411303,卧龙区,4,411300 -411321,南召县,4,411300 -411322,方城县,4,411300 -411323,西峡县,4,411300 -411324,镇平县,4,411300 -411325,内乡县,4,411300 -411326,淅川县,4,411300 -411327,社旗县,4,411300 -411328,唐河县,4,411300 -411329,新野县,4,411300 -411330,桐柏县,4,411300 -411371,南阳高新技术产业开发区,4,411300 -411372,南阳市城乡一体化示范区,4,411300 -411381,邓州市,4,411300 -411402,梁园区,4,411400 -411403,睢阳区,4,411400 -411421,民权县,4,411400 -411422,睢县,4,411400 -411423,宁陵县,4,411400 -411424,柘城县,4,411400 -411425,虞城县,4,411400 -411426,夏邑县,4,411400 -411471,豫东综合物流产业聚集区,4,411400 -411472,河南商丘经济开发区,4,411400 -411481,永城市,4,411400 -411502,浉河区,4,411500 -411503,平桥区,4,411500 -411521,罗山县,4,411500 -411522,光山县,4,411500 -411523,新县,4,411500 -411524,商城县,4,411500 -411525,固始县,4,411500 -411526,潢川县,4,411500 -411527,淮滨县,4,411500 -411528,息县,4,411500 -411571,信阳高新技术产业开发区,4,411500 -411602,川汇区,4,411600 -411603,淮阳区,4,411600 -411621,扶沟县,4,411600 -411622,西华县,4,411600 -411623,商水县,4,411600 -411624,沈丘县,4,411600 -411625,郸城县,4,411600 -411627,太康县,4,411600 -411628,鹿邑县,4,411600 -411671,河南周口经济开发区,4,411600 -411681,项城市,4,411600 -411702,驿城区,4,411700 -411721,西平县,4,411700 -411722,上蔡县,4,411700 -411723,平舆县,4,411700 -411724,正阳县,4,411700 -411725,确山县,4,411700 -411726,泌阳县,4,411700 -411727,汝南县,4,411700 -411728,遂平县,4,411700 -411729,新蔡县,4,411700 -411771,河南驻马店经济开发区,4,411700 -419001,济源市,4,419000 -420102,江岸区,4,420100 -420103,江汉区,4,420100 -420104,硚口区,4,420100 -420105,汉阳区,4,420100 -420106,武昌区,4,420100 -420107,青山区,4,420100 -420111,洪山区,4,420100 -420112,东西湖区,4,420100 -420113,汉南区,4,420100 -420114,蔡甸区,4,420100 -420115,江夏区,4,420100 -420116,黄陂区,4,420100 -420117,新洲区,4,420100 -420202,黄石港区,4,420200 -420203,西塞山区,4,420200 -420204,下陆区,4,420200 -420205,铁山区,4,420200 -420222,阳新县,4,420200 -420281,大冶市,4,420200 -420302,茅箭区,4,420300 -420303,张湾区,4,420300 -420304,郧阳区,4,420300 -420322,郧西县,4,420300 -420323,竹山县,4,420300 -420324,竹溪县,4,420300 -420325,房县,4,420300 -420381,丹江口市,4,420300 -420502,西陵区,4,420500 -420503,伍家岗区,4,420500 -420504,点军区,4,420500 -420505,猇亭区,4,420500 -420506,夷陵区,4,420500 -420525,远安县,4,420500 -420526,兴山县,4,420500 -420527,秭归县,4,420500 -420528,长阳土家族自治县,4,420500 -420529,五峰土家族自治县,4,420500 -420581,宜都市,4,420500 -420582,当阳市,4,420500 -420583,枝江市,4,420500 -420602,襄城区,4,420600 -420606,樊城区,4,420600 -420607,襄州区,4,420600 -420624,南漳县,4,420600 -420625,谷城县,4,420600 -420626,保康县,4,420600 -420682,老河口市,4,420600 -420683,枣阳市,4,420600 -420684,宜城市,4,420600 -420702,梁子湖区,4,420700 -420703,华容区,4,420700 -420704,鄂城区,4,420700 -420802,东宝区,4,420800 -420804,掇刀区,4,420800 -420822,沙洋县,4,420800 -420881,钟祥市,4,420800 -420882,京山市,4,420800 -420902,孝南区,4,420900 -420921,孝昌县,4,420900 -420922,大悟县,4,420900 -420923,云梦县,4,420900 -420981,应城市,4,420900 -420982,安陆市,4,420900 -420984,汉川市,4,420900 -421002,沙市区,4,421000 -421003,荆州区,4,421000 -421022,公安县,4,421000 -421024,江陵县,4,421000 -421071,荆州经济技术开发区,4,421000 -421081,石首市,4,421000 -421083,洪湖市,4,421000 -421087,松滋市,4,421000 -421088,监利市,4,421000 -421102,黄州区,4,421100 -421121,团风县,4,421100 -421122,红安县,4,421100 -421123,罗田县,4,421100 -421124,英山县,4,421100 -421125,浠水县,4,421100 -421126,蕲春县,4,421100 -421127,黄梅县,4,421100 -421171,龙感湖管理区,4,421100 -421181,麻城市,4,421100 -421182,武穴市,4,421100 -421202,咸安区,4,421200 -421221,嘉鱼县,4,421200 -421222,通城县,4,421200 -421223,崇阳县,4,421200 -421224,通山县,4,421200 -421281,赤壁市,4,421200 -421303,曾都区,4,421300 -421321,随县,4,421300 -421381,广水市,4,421300 -422801,恩施市,4,422800 -422802,利川市,4,422800 -422822,建始县,4,422800 -422823,巴东县,4,422800 -422825,宣恩县,4,422800 -422826,咸丰县,4,422800 -422827,来凤县,4,422800 -422828,鹤峰县,4,422800 -429004,仙桃市,4,429000 -429005,潜江市,4,429000 -429006,天门市,4,429000 -429021,神农架林区,4,429000 -430102,芙蓉区,4,430100 -430103,天心区,4,430100 -430104,岳麓区,4,430100 -430105,开福区,4,430100 -430111,雨花区,4,430100 -430112,望城区,4,430100 -430121,长沙县,4,430100 -430181,浏阳市,4,430100 -430182,宁乡市,4,430100 -430202,荷塘区,4,430200 -430203,芦淞区,4,430200 -430204,石峰区,4,430200 -430211,天元区,4,430200 -430212,渌口区,4,430200 -430223,攸县,4,430200 -430224,茶陵县,4,430200 -430225,炎陵县,4,430200 -430271,云龙示范区,4,430200 -430281,醴陵市,4,430200 -430302,雨湖区,4,430300 -430304,岳塘区,4,430300 -430321,湘潭县,4,430300 -430371,湖南湘潭高新技术产业园区,4,430300 -430372,湘潭昭山示范区,4,430300 -430373,湘潭九华示范区,4,430300 -430381,湘乡市,4,430300 -430382,韶山市,4,430300 -430405,珠晖区,4,430400 -430406,雁峰区,4,430400 -430407,石鼓区,4,430400 -430408,蒸湘区,4,430400 -430412,南岳区,4,430400 -430421,衡阳县,4,430400 -430422,衡南县,4,430400 -430423,衡山县,4,430400 -430424,衡东县,4,430400 -430426,祁东县,4,430400 -430471,衡阳综合保税区,4,430400 -430472,湖南衡阳高新技术产业园区,4,430400 -430473,湖南衡阳松木经济开发区,4,430400 -430481,耒阳市,4,430400 -430482,常宁市,4,430400 -430502,双清区,4,430500 -430503,大祥区,4,430500 -430511,北塔区,4,430500 -430522,新邵县,4,430500 -430523,邵阳县,4,430500 -430524,隆回县,4,430500 -430525,洞口县,4,430500 -430527,绥宁县,4,430500 -430528,新宁县,4,430500 -430529,城步苗族自治县,4,430500 -430581,武冈市,4,430500 -430582,邵东市,4,430500 -430602,岳阳楼区,4,430600 -430603,云溪区,4,430600 -430611,君山区,4,430600 -430621,岳阳县,4,430600 -430623,华容县,4,430600 -430624,湘阴县,4,430600 -430626,平江县,4,430600 -430671,岳阳市屈原管理区,4,430600 -430681,汨罗市,4,430600 -430682,临湘市,4,430600 -430702,武陵区,4,430700 -430703,鼎城区,4,430700 -430721,安乡县,4,430700 -430722,汉寿县,4,430700 -430723,澧县,4,430700 -430724,临澧县,4,430700 -430725,桃源县,4,430700 -430726,石门县,4,430700 -430771,常德市西洞庭管理区,4,430700 -430781,津市市,4,430700 -430802,永定区,4,430800 -430811,武陵源区,4,430800 -430821,慈利县,4,430800 -430822,桑植县,4,430800 -430902,资阳区,4,430900 -430903,赫山区,4,430900 -430921,南县,4,430900 -430922,桃江县,4,430900 -430923,安化县,4,430900 -430971,益阳市大通湖管理区,4,430900 -430972,湖南益阳高新技术产业园区,4,430900 -430981,沅江市,4,430900 -431002,北湖区,4,431000 -431003,苏仙区,4,431000 -431021,桂阳县,4,431000 -431022,宜章县,4,431000 -431023,永兴县,4,431000 -431024,嘉禾县,4,431000 -431025,临武县,4,431000 -431026,汝城县,4,431000 -431027,桂东县,4,431000 -431028,安仁县,4,431000 -431081,资兴市,4,431000 -431102,零陵区,4,431100 -431103,冷水滩区,4,431100 -431122,东安县,4,431100 -431123,双牌县,4,431100 -431124,道县,4,431100 -431125,江永县,4,431100 -431126,宁远县,4,431100 -431127,蓝山县,4,431100 -431128,新田县,4,431100 -431129,江华瑶族自治县,4,431100 -431171,永州经济技术开发区,4,431100 -431173,永州市回龙圩管理区,4,431100 -431181,祁阳市,4,431100 -431202,鹤城区,4,431200 -431221,中方县,4,431200 -431222,沅陵县,4,431200 -431223,辰溪县,4,431200 -431224,溆浦县,4,431200 -431225,会同县,4,431200 -431226,麻阳苗族自治县,4,431200 -431227,新晃侗族自治县,4,431200 -431228,芷江侗族自治县,4,431200 -431229,靖州苗族侗族自治县,4,431200 -431230,通道侗族自治县,4,431200 -431271,怀化市洪江管理区,4,431200 -431281,洪江市,4,431200 -431302,娄星区,4,431300 -431321,双峰县,4,431300 -431322,新化县,4,431300 -431381,冷水江市,4,431300 -431382,涟源市,4,431300 -433101,吉首市,4,433100 -433122,泸溪县,4,433100 -433123,凤凰县,4,433100 -433124,花垣县,4,433100 -433125,保靖县,4,433100 -433126,古丈县,4,433100 -433127,永顺县,4,433100 -433130,龙山县,4,433100 -440103,荔湾区,4,440100 -440104,越秀区,4,440100 -440105,海珠区,4,440100 -440106,天河区,4,440100 -440111,白云区,4,440100 -440112,黄埔区,4,440100 -440113,番禺区,4,440100 -440114,花都区,4,440100 -440115,南沙区,4,440100 -440117,从化区,4,440100 -440118,增城区,4,440100 -440203,武江区,4,440200 -440204,浈江区,4,440200 -440205,曲江区,4,440200 -440222,始兴县,4,440200 -440224,仁化县,4,440200 -440229,翁源县,4,440200 -440232,乳源瑶族自治县,4,440200 -440233,新丰县,4,440200 -440281,乐昌市,4,440200 -440282,南雄市,4,440200 -440303,罗湖区,4,440300 -440304,福田区,4,440300 -440305,南山区,4,440300 -440306,宝安区,4,440300 -440307,龙岗区,4,440300 -440308,盐田区,4,440300 -440309,龙华区,4,440300 -440310,坪山区,4,440300 -440311,光明区,4,440300 -440402,香洲区,4,440400 -440403,斗门区,4,440400 -440404,金湾区,4,440400 -440507,龙湖区,4,440500 -440511,金平区,4,440500 -440512,濠江区,4,440500 -440513,潮阳区,4,440500 -440514,潮南区,4,440500 -440515,澄海区,4,440500 -440523,南澳县,4,440500 -440604,禅城区,4,440600 -440605,南海区,4,440600 -440606,顺德区,4,440600 -440607,三水区,4,440600 -440608,高明区,4,440600 -440703,蓬江区,4,440700 -440704,江海区,4,440700 -440705,新会区,4,440700 -440781,台山市,4,440700 -440783,开平市,4,440700 -440784,鹤山市,4,440700 -440785,恩平市,4,440700 -440802,赤坎区,4,440800 -440803,霞山区,4,440800 -440804,坡头区,4,440800 -440811,麻章区,4,440800 -440823,遂溪县,4,440800 -440825,徐闻县,4,440800 -440881,廉江市,4,440800 -440882,雷州市,4,440800 -440883,吴川市,4,440800 -440902,茂南区,4,440900 -440904,电白区,4,440900 -440981,高州市,4,440900 -440982,化州市,4,440900 -440983,信宜市,4,440900 -441202,端州区,4,441200 -441203,鼎湖区,4,441200 -441204,高要区,4,441200 -441223,广宁县,4,441200 -441224,怀集县,4,441200 -441225,封开县,4,441200 -441226,德庆县,4,441200 -441284,四会市,4,441200 -441302,惠城区,4,441300 -441303,惠阳区,4,441300 -441322,博罗县,4,441300 -441323,惠东县,4,441300 -441324,龙门县,4,441300 -441402,梅江区,4,441400 -441403,梅县区,4,441400 -441422,大埔县,4,441400 -441423,丰顺县,4,441400 -441424,五华县,4,441400 -441426,平远县,4,441400 -441427,蕉岭县,4,441400 -441481,兴宁市,4,441400 -441502,城区,4,441500 -441521,海丰县,4,441500 -441523,陆河县,4,441500 -441581,陆丰市,4,441500 -441602,源城区,4,441600 -441621,紫金县,4,441600 -441622,龙川县,4,441600 -441623,连平县,4,441600 -441624,和平县,4,441600 -441625,东源县,4,441600 -441702,江城区,4,441700 -441704,阳东区,4,441700 -441721,阳西县,4,441700 -441781,阳春市,4,441700 -441802,清城区,4,441800 -441803,清新区,4,441800 -441821,佛冈县,4,441800 -441823,阳山县,4,441800 -441825,连山壮族瑶族自治县,4,441800 -441826,连南瑶族自治县,4,441800 -441881,英德市,4,441800 -441882,连州市,4,441800 -445102,湘桥区,4,445100 -445103,潮安区,4,445100 -445122,饶平县,4,445100 -445202,榕城区,4,445200 -445203,揭东区,4,445200 -445222,揭西县,4,445200 -445224,惠来县,4,445200 -445281,普宁市,4,445200 -445302,云城区,4,445300 -445303,云安区,4,445300 -445321,新兴县,4,445300 -445322,郁南县,4,445300 -445381,罗定市,4,445300 -450102,兴宁区,4,450100 -450103,青秀区,4,450100 -450105,江南区,4,450100 -450107,西乡塘区,4,450100 -450108,良庆区,4,450100 -450109,邕宁区,4,450100 -450110,武鸣区,4,450100 -450123,隆安县,4,450100 -450124,马山县,4,450100 -450125,上林县,4,450100 -450126,宾阳县,4,450100 -450181,横州市,4,450100 -450202,城中区,4,450200 -450203,鱼峰区,4,450200 -450204,柳南区,4,450200 -450205,柳北区,4,450200 -450206,柳江区,4,450200 -450222,柳城县,4,450200 -450223,鹿寨县,4,450200 -450224,融安县,4,450200 -450225,融水苗族自治县,4,450200 -450226,三江侗族自治县,4,450200 -450302,秀峰区,4,450300 -450303,叠彩区,4,450300 -450304,象山区,4,450300 -450305,七星区,4,450300 -450311,雁山区,4,450300 -450312,临桂区,4,450300 -450321,阳朔县,4,450300 -450323,灵川县,4,450300 -450324,全州县,4,450300 -450325,兴安县,4,450300 -450326,永福县,4,450300 -450327,灌阳县,4,450300 -450328,龙胜各族自治县,4,450300 -450329,资源县,4,450300 -450330,平乐县,4,450300 -450332,恭城瑶族自治县,4,450300 -450381,荔浦市,4,450300 -450403,万秀区,4,450400 -450405,长洲区,4,450400 -450406,龙圩区,4,450400 -450421,苍梧县,4,450400 -450422,藤县,4,450400 -450423,蒙山县,4,450400 -450481,岑溪市,4,450400 -450502,海城区,4,450500 -450503,银海区,4,450500 -450512,铁山港区,4,450500 -450521,合浦县,4,450500 -450602,港口区,4,450600 -450603,防城区,4,450600 -450621,上思县,4,450600 -450681,东兴市,4,450600 -450702,钦南区,4,450700 -450703,钦北区,4,450700 -450721,灵山县,4,450700 -450722,浦北县,4,450700 -450802,港北区,4,450800 -450803,港南区,4,450800 -450804,覃塘区,4,450800 -450821,平南县,4,450800 -450881,桂平市,4,450800 -450902,玉州区,4,450900 -450903,福绵区,4,450900 -450921,容县,4,450900 -450922,陆川县,4,450900 -450923,博白县,4,450900 -450924,兴业县,4,450900 -450981,北流市,4,450900 -451002,右江区,4,451000 -451003,田阳区,4,451000 -451022,田东县,4,451000 -451024,德保县,4,451000 -451026,那坡县,4,451000 -451027,凌云县,4,451000 -451028,乐业县,4,451000 -451029,田林县,4,451000 -451030,西林县,4,451000 -451031,隆林各族自治县,4,451000 -451081,靖西市,4,451000 -451082,平果市,4,451000 -451102,八步区,4,451100 -451103,平桂区,4,451100 -451121,昭平县,4,451100 -451122,钟山县,4,451100 -451123,富川瑶族自治县,4,451100 -451202,金城江区,4,451200 -451203,宜州区,4,451200 -451221,南丹县,4,451200 -451222,天峨县,4,451200 -451223,凤山县,4,451200 -451224,东兰县,4,451200 -451225,罗城仫佬族自治县,4,451200 -451226,环江毛南族自治县,4,451200 -451227,巴马瑶族自治县,4,451200 -451228,都安瑶族自治县,4,451200 -451229,大化瑶族自治县,4,451200 -451302,兴宾区,4,451300 -451321,忻城县,4,451300 -451322,象州县,4,451300 -451323,武宣县,4,451300 -451324,金秀瑶族自治县,4,451300 -451381,合山市,4,451300 -451402,江州区,4,451400 -451421,扶绥县,4,451400 -451422,宁明县,4,451400 -451423,龙州县,4,451400 -451424,大新县,4,451400 -451425,天等县,4,451400 -451481,凭祥市,4,451400 -460105,秀英区,4,460100 -460106,龙华区,4,460100 -460107,琼山区,4,460100 -460108,美兰区,4,460100 -460202,海棠区,4,460200 -460203,吉阳区,4,460200 -460204,天涯区,4,460200 -460205,崖州区,4,460200 -460321,西沙群岛,4,460300 -460322,南沙群岛,4,460300 -460323,中沙群岛的岛礁及其海域,4,460300 -469001,五指山市,4,469000 -469002,琼海市,4,469000 -469005,文昌市,4,469000 -469006,万宁市,4,469000 -469007,东方市,4,469000 -469021,定安县,4,469000 -469022,屯昌县,4,469000 -469023,澄迈县,4,469000 -469024,临高县,4,469000 -469025,白沙黎族自治县,4,469000 -469026,昌江黎族自治县,4,469000 -469027,乐东黎族自治县,4,469000 -469028,陵水黎族自治县,4,469000 -469029,保亭黎族苗族自治县,4,469000 -469030,琼中黎族苗族自治县,4,469000 -500101,万州区,4,500100 -500102,涪陵区,4,500100 -500103,渝中区,4,500100 -500104,大渡口区,4,500100 -500105,江北区,4,500100 -500106,沙坪坝区,4,500100 -500107,九龙坡区,4,500100 -500108,南岸区,4,500100 -500109,北碚区,4,500100 -500110,綦江区,4,500100 -500111,大足区,4,500100 -500112,渝北区,4,500100 -500113,巴南区,4,500100 -500114,黔江区,4,500100 -500115,长寿区,4,500100 -500116,江津区,4,500100 -500117,合川区,4,500100 -500118,永川区,4,500100 -500119,南川区,4,500100 -500120,璧山区,4,500100 -500151,铜梁区,4,500100 -500152,潼南区,4,500100 -500153,荣昌区,4,500100 -500154,开州区,4,500100 -500155,梁平区,4,500100 -500156,武隆区,4,500100 -500229,城口县,4,500100 -500230,丰都县,4,500100 -500231,垫江县,4,500100 -500233,忠县,4,500100 -500235,云阳县,4,500100 -500236,奉节县,4,500100 -500237,巫山县,4,500100 -500238,巫溪县,4,500100 -500240,石柱土家族自治县,4,500100 -500241,秀山土家族苗族自治县,4,500100 -500242,酉阳土家族苗族自治县,4,500100 -500243,彭水苗族土家族自治县,4,500100 -510104,锦江区,4,510100 -510105,青羊区,4,510100 -510106,金牛区,4,510100 -510107,武侯区,4,510100 -510108,成华区,4,510100 -510112,龙泉驿区,4,510100 -510113,青白江区,4,510100 -510114,新都区,4,510100 -510115,温江区,4,510100 -510116,双流区,4,510100 -510117,郫都区,4,510100 -510118,新津区,4,510100 -510121,金堂县,4,510100 -510129,大邑县,4,510100 -510131,蒲江县,4,510100 -510181,都江堰市,4,510100 -510182,彭州市,4,510100 -510183,邛崃市,4,510100 -510184,崇州市,4,510100 -510185,简阳市,4,510100 -510302,自流井区,4,510300 -510303,贡井区,4,510300 -510304,大安区,4,510300 -510311,沿滩区,4,510300 -510321,荣县,4,510300 -510322,富顺县,4,510300 -510402,东区,4,510400 -510403,西区,4,510400 -510411,仁和区,4,510400 -510421,米易县,4,510400 -510422,盐边县,4,510400 -510502,江阳区,4,510500 -510503,纳溪区,4,510500 -510504,龙马潭区,4,510500 -510521,泸县,4,510500 -510522,合江县,4,510500 -510524,叙永县,4,510500 -510525,古蔺县,4,510500 -510603,旌阳区,4,510600 -510604,罗江区,4,510600 -510623,中江县,4,510600 -510681,广汉市,4,510600 -510682,什邡市,4,510600 -510683,绵竹市,4,510600 -510703,涪城区,4,510700 -510704,游仙区,4,510700 -510705,安州区,4,510700 -510722,三台县,4,510700 -510723,盐亭县,4,510700 -510725,梓潼县,4,510700 -510726,北川羌族自治县,4,510700 -510727,平武县,4,510700 -510781,江油市,4,510700 -510802,利州区,4,510800 -510811,昭化区,4,510800 -510812,朝天区,4,510800 -510821,旺苍县,4,510800 -510822,青川县,4,510800 -510823,剑阁县,4,510800 -510824,苍溪县,4,510800 -510903,船山区,4,510900 -510904,安居区,4,510900 -510921,蓬溪县,4,510900 -510923,大英县,4,510900 -510981,射洪市,4,510900 -511002,市中区,4,511000 -511011,东兴区,4,511000 -511024,威远县,4,511000 -511025,资中县,4,511000 -511071,内江经济开发区,4,511000 -511083,隆昌市,4,511000 -511102,市中区,4,511100 -511111,沙湾区,4,511100 -511112,五通桥区,4,511100 -511113,金口河区,4,511100 -511123,犍为县,4,511100 -511124,井研县,4,511100 -511126,夹江县,4,511100 -511129,沐川县,4,511100 -511132,峨边彝族自治县,4,511100 -511133,马边彝族自治县,4,511100 -511181,峨眉山市,4,511100 -511302,顺庆区,4,511300 -511303,高坪区,4,511300 -511304,嘉陵区,4,511300 -511321,南部县,4,511300 -511322,营山县,4,511300 -511323,蓬安县,4,511300 -511324,仪陇县,4,511300 -511325,西充县,4,511300 -511381,阆中市,4,511300 -511402,东坡区,4,511400 -511403,彭山区,4,511400 -511421,仁寿县,4,511400 -511423,洪雅县,4,511400 -511424,丹棱县,4,511400 -511425,青神县,4,511400 -511502,翠屏区,4,511500 -511503,南溪区,4,511500 -511504,叙州区,4,511500 -511523,江安县,4,511500 -511524,长宁县,4,511500 -511525,高县,4,511500 -511526,珙县,4,511500 -511527,筠连县,4,511500 -511528,兴文县,4,511500 -511529,屏山县,4,511500 -511602,广安区,4,511600 -511603,前锋区,4,511600 -511621,岳池县,4,511600 -511622,武胜县,4,511600 -511623,邻水县,4,511600 -511681,华蓥市,4,511600 -511702,通川区,4,511700 -511703,达川区,4,511700 -511722,宣汉县,4,511700 -511723,开江县,4,511700 -511724,大竹县,4,511700 -511725,渠县,4,511700 -511771,达州经济开发区,4,511700 -511781,万源市,4,511700 -511802,雨城区,4,511800 -511803,名山区,4,511800 -511822,荥经县,4,511800 -511823,汉源县,4,511800 -511824,石棉县,4,511800 -511825,天全县,4,511800 -511826,芦山县,4,511800 -511827,宝兴县,4,511800 -511902,巴州区,4,511900 -511903,恩阳区,4,511900 -511921,通江县,4,511900 -511922,南江县,4,511900 -511923,平昌县,4,511900 -511971,巴中经济开发区,4,511900 -512002,雁江区,4,512000 -512021,安岳县,4,512000 -512022,乐至县,4,512000 -513201,马尔康市,4,513200 -513221,汶川县,4,513200 -513222,理县,4,513200 -513223,茂县,4,513200 -513224,松潘县,4,513200 -513225,九寨沟县,4,513200 -513226,金川县,4,513200 -513227,小金县,4,513200 -513228,黑水县,4,513200 -513230,壤塘县,4,513200 -513231,阿坝县,4,513200 -513232,若尔盖县,4,513200 -513233,红原县,4,513200 -513301,康定市,4,513300 -513322,泸定县,4,513300 -513323,丹巴县,4,513300 -513324,九龙县,4,513300 -513325,雅江县,4,513300 -513326,道孚县,4,513300 -513327,炉霍县,4,513300 -513328,甘孜县,4,513300 -513329,新龙县,4,513300 -513330,德格县,4,513300 -513331,白玉县,4,513300 -513332,石渠县,4,513300 -513333,色达县,4,513300 -513334,理塘县,4,513300 -513335,巴塘县,4,513300 -513336,乡城县,4,513300 -513337,稻城县,4,513300 -513338,得荣县,4,513300 -513401,西昌市,4,513400 -513402,会理市,4,513400 -513422,木里藏族自治县,4,513400 -513423,盐源县,4,513400 -513424,德昌县,4,513400 -513426,会东县,4,513400 -513427,宁南县,4,513400 -513428,普格县,4,513400 -513429,布拖县,4,513400 -513430,金阳县,4,513400 -513431,昭觉县,4,513400 -513432,喜德县,4,513400 -513433,冕宁县,4,513400 -513434,越西县,4,513400 -513435,甘洛县,4,513400 -513436,美姑县,4,513400 -513437,雷波县,4,513400 -520102,南明区,4,520100 -520103,云岩区,4,520100 -520111,花溪区,4,520100 -520112,乌当区,4,520100 -520113,白云区,4,520100 -520115,观山湖区,4,520100 -520121,开阳县,4,520100 -520122,息烽县,4,520100 -520123,修文县,4,520100 -520181,清镇市,4,520100 -520201,钟山区,4,520200 -520203,六枝特区,4,520200 -520204,水城区,4,520200 -520281,盘州市,4,520200 -520302,红花岗区,4,520300 -520303,汇川区,4,520300 -520304,播州区,4,520300 -520322,桐梓县,4,520300 -520323,绥阳县,4,520300 -520324,正安县,4,520300 -520325,道真仡佬族苗族自治县,4,520300 -520326,务川仡佬族苗族自治县,4,520300 -520327,凤冈县,4,520300 -520328,湄潭县,4,520300 -520329,余庆县,4,520300 -520330,习水县,4,520300 -520381,赤水市,4,520300 -520382,仁怀市,4,520300 -520402,西秀区,4,520400 -520403,平坝区,4,520400 -520422,普定县,4,520400 -520423,镇宁布依族苗族自治县,4,520400 -520424,关岭布依族苗族自治县,4,520400 -520425,紫云苗族布依族自治县,4,520400 -520502,七星关区,4,520500 -520521,大方县,4,520500 -520523,金沙县,4,520500 -520524,织金县,4,520500 -520525,纳雍县,4,520500 -520526,威宁彝族回族苗族自治县,4,520500 -520527,赫章县,4,520500 -520581,黔西市,4,520500 -520602,碧江区,4,520600 -520603,万山区,4,520600 -520621,江口县,4,520600 -520622,玉屏侗族自治县,4,520600 -520623,石阡县,4,520600 -520624,思南县,4,520600 -520625,印江土家族苗族自治县,4,520600 -520626,德江县,4,520600 -520627,沿河土家族自治县,4,520600 -520628,松桃苗族自治县,4,520600 -522301,兴义市,4,522300 -522302,兴仁市,4,522300 -522323,普安县,4,522300 -522324,晴隆县,4,522300 -522325,贞丰县,4,522300 -522326,望谟县,4,522300 -522327,册亨县,4,522300 -522328,安龙县,4,522300 -522601,凯里市,4,522600 -522622,黄平县,4,522600 -522623,施秉县,4,522600 -522624,三穗县,4,522600 -522625,镇远县,4,522600 -522626,岑巩县,4,522600 -522627,天柱县,4,522600 -522628,锦屏县,4,522600 -522629,剑河县,4,522600 -522630,台江县,4,522600 -522631,黎平县,4,522600 -522632,榕江县,4,522600 -522633,从江县,4,522600 -522634,雷山县,4,522600 -522635,麻江县,4,522600 -522636,丹寨县,4,522600 -522701,都匀市,4,522700 -522702,福泉市,4,522700 -522722,荔波县,4,522700 -522723,贵定县,4,522700 -522725,瓮安县,4,522700 -522726,独山县,4,522700 -522727,平塘县,4,522700 -522728,罗甸县,4,522700 -522729,长顺县,4,522700 -522730,龙里县,4,522700 -522731,惠水县,4,522700 -522732,三都水族自治县,4,522700 -530102,五华区,4,530100 -530103,盘龙区,4,530100 -530111,官渡区,4,530100 -530112,西山区,4,530100 -530113,东川区,4,530100 -530114,呈贡区,4,530100 -530115,晋宁区,4,530100 -530124,富民县,4,530100 -530125,宜良县,4,530100 -530126,石林彝族自治县,4,530100 -530127,嵩明县,4,530100 -530128,禄劝彝族苗族自治县,4,530100 -530129,寻甸回族彝族自治县,4,530100 -530181,安宁市,4,530100 -530302,麒麟区,4,530300 -530303,沾益区,4,530300 -530304,马龙区,4,530300 -530322,陆良县,4,530300 -530323,师宗县,4,530300 -530324,罗平县,4,530300 -530325,富源县,4,530300 -530326,会泽县,4,530300 -530381,宣威市,4,530300 -530402,红塔区,4,530400 -530403,江川区,4,530400 -530423,通海县,4,530400 -530424,华宁县,4,530400 -530425,易门县,4,530400 -530426,峨山彝族自治县,4,530400 -530427,新平彝族傣族自治县,4,530400 -530428,元江哈尼族彝族傣族自治县,4,530400 -530481,澄江市,4,530400 -530502,隆阳区,4,530500 -530521,施甸县,4,530500 -530523,龙陵县,4,530500 -530524,昌宁县,4,530500 -530581,腾冲市,4,530500 -530602,昭阳区,4,530600 -530621,鲁甸县,4,530600 -530622,巧家县,4,530600 -530623,盐津县,4,530600 -530624,大关县,4,530600 -530625,永善县,4,530600 -530626,绥江县,4,530600 -530627,镇雄县,4,530600 -530628,彝良县,4,530600 -530629,威信县,4,530600 -530681,水富市,4,530600 -530702,古城区,4,530700 -530721,玉龙纳西族自治县,4,530700 -530722,永胜县,4,530700 -530723,华坪县,4,530700 -530724,宁蒗彝族自治县,4,530700 -530802,思茅区,4,530800 -530821,宁洱哈尼族彝族自治县,4,530800 -530822,墨江哈尼族自治县,4,530800 -530823,景东彝族自治县,4,530800 -530824,景谷傣族彝族自治县,4,530800 -530825,镇沅彝族哈尼族拉祜族自治县,4,530800 -530826,江城哈尼族彝族自治县,4,530800 -530827,孟连傣族拉祜族佤族自治县,4,530800 -530828,澜沧拉祜族自治县,4,530800 -530829,西盟佤族自治县,4,530800 -530902,临翔区,4,530900 -530921,凤庆县,4,530900 -530922,云县,4,530900 -530923,永德县,4,530900 -530924,镇康县,4,530900 -530925,双江拉祜族佤族布朗族傣族自治县,4,530900 -530926,耿马傣族佤族自治县,4,530900 -530927,沧源佤族自治县,4,530900 -532301,楚雄市,4,532300 -532302,禄丰市,4,532300 -532322,双柏县,4,532300 -532323,牟定县,4,532300 -532324,南华县,4,532300 -532325,姚安县,4,532300 -532326,大姚县,4,532300 -532327,永仁县,4,532300 -532328,元谋县,4,532300 -532329,武定县,4,532300 -532501,个旧市,4,532500 -532502,开远市,4,532500 -532503,蒙自市,4,532500 -532504,弥勒市,4,532500 -532523,屏边苗族自治县,4,532500 -532524,建水县,4,532500 -532525,石屏县,4,532500 -532527,泸西县,4,532500 -532528,元阳县,4,532500 -532529,红河县,4,532500 -532530,金平苗族瑶族傣族自治县,4,532500 -532531,绿春县,4,532500 -532532,河口瑶族自治县,4,532500 -532601,文山市,4,532600 -532622,砚山县,4,532600 -532623,西畴县,4,532600 -532624,麻栗坡县,4,532600 -532625,马关县,4,532600 -532626,丘北县,4,532600 -532627,广南县,4,532600 -532628,富宁县,4,532600 -532801,景洪市,4,532800 -532822,勐海县,4,532800 -532823,勐腊县,4,532800 -532901,大理市,4,532900 -532922,漾濞彝族自治县,4,532900 -532923,祥云县,4,532900 -532924,宾川县,4,532900 -532925,弥渡县,4,532900 -532926,南涧彝族自治县,4,532900 -532927,巍山彝族回族自治县,4,532900 -532928,永平县,4,532900 -532929,云龙县,4,532900 -532930,洱源县,4,532900 -532931,剑川县,4,532900 -532932,鹤庆县,4,532900 -533102,瑞丽市,4,533100 -533103,芒市,4,533100 -533122,梁河县,4,533100 -533123,盈江县,4,533100 -533124,陇川县,4,533100 -533301,泸水市,4,533300 -533323,福贡县,4,533300 -533324,贡山独龙族怒族自治县,4,533300 -533325,兰坪白族普米族自治县,4,533300 -533401,香格里拉市,4,533400 -533422,德钦县,4,533400 -533423,维西傈僳族自治县,4,533400 -540102,城关区,4,540100 -540103,堆龙德庆区,4,540100 -540104,达孜区,4,540100 -540121,林周县,4,540100 -540122,当雄县,4,540100 -540123,尼木县,4,540100 -540124,曲水县,4,540100 -540127,墨竹工卡县,4,540100 -540171,格尔木藏青工业园区,4,540100 -540172,拉萨经济技术开发区,4,540100 -540173,西藏文化旅游创意园区,4,540100 -540174,达孜工业园区,4,540100 -540202,桑珠孜区,4,540200 -540221,南木林县,4,540200 -540222,江孜县,4,540200 -540223,定日县,4,540200 -540224,萨迦县,4,540200 -540225,拉孜县,4,540200 -540226,昂仁县,4,540200 -540227,谢通门县,4,540200 -540228,白朗县,4,540200 -540229,仁布县,4,540200 -540230,康马县,4,540200 -540231,定结县,4,540200 -540232,仲巴县,4,540200 -540233,亚东县,4,540200 -540234,吉隆县,4,540200 -540235,聂拉木县,4,540200 -540236,萨嘎县,4,540200 -540237,岗巴县,4,540200 -540302,卡若区,4,540300 -540321,江达县,4,540300 -540322,贡觉县,4,540300 -540323,类乌齐县,4,540300 -540324,丁青县,4,540300 -540325,察雅县,4,540300 -540326,八宿县,4,540300 -540327,左贡县,4,540300 -540328,芒康县,4,540300 -540329,洛隆县,4,540300 -540330,边坝县,4,540300 -540402,巴宜区,4,540400 -540421,工布江达县,4,540400 -540422,米林县,4,540400 -540423,墨脱县,4,540400 -540424,波密县,4,540400 -540425,察隅县,4,540400 -540426,朗县,4,540400 -540502,乃东区,4,540500 -540521,扎囊县,4,540500 -540522,贡嘎县,4,540500 -540523,桑日县,4,540500 -540524,琼结县,4,540500 -540525,曲松县,4,540500 -540526,措美县,4,540500 -540527,洛扎县,4,540500 -540528,加查县,4,540500 -540529,隆子县,4,540500 -540530,错那县,4,540500 -540531,浪卡子县,4,540500 -540602,色尼区,4,540600 -540621,嘉黎县,4,540600 -540622,比如县,4,540600 -540623,聂荣县,4,540600 -540624,安多县,4,540600 -540625,申扎县,4,540600 -540626,索县,4,540600 -540627,班戈县,4,540600 -540628,巴青县,4,540600 -540629,尼玛县,4,540600 -540630,双湖县,4,540600 -542521,普兰县,4,542500 -542522,札达县,4,542500 -542523,噶尔县,4,542500 -542524,日土县,4,542500 -542525,革吉县,4,542500 -542526,改则县,4,542500 -542527,措勤县,4,542500 -610102,新城区,4,610100 -610103,碑林区,4,610100 -610104,莲湖区,4,610100 -610111,灞桥区,4,610100 -610112,未央区,4,610100 -610113,雁塔区,4,610100 -610114,阎良区,4,610100 -610115,临潼区,4,610100 -610116,长安区,4,610100 -610117,高陵区,4,610100 -610118,鄠邑区,4,610100 -610122,蓝田县,4,610100 -610124,周至县,4,610100 -610202,王益区,4,610200 -610203,印台区,4,610200 -610204,耀州区,4,610200 -610222,宜君县,4,610200 -610302,渭滨区,4,610300 -610303,金台区,4,610300 -610304,陈仓区,4,610300 -610305,凤翔区,4,610300 -610323,岐山县,4,610300 -610324,扶风县,4,610300 -610326,眉县,4,610300 -610327,陇县,4,610300 -610328,千阳县,4,610300 -610329,麟游县,4,610300 -610330,凤县,4,610300 -610331,太白县,4,610300 -610402,秦都区,4,610400 -610403,杨陵区,4,610400 -610404,渭城区,4,610400 -610422,三原县,4,610400 -610423,泾阳县,4,610400 -610424,乾县,4,610400 -610425,礼泉县,4,610400 -610426,永寿县,4,610400 -610428,长武县,4,610400 -610429,旬邑县,4,610400 -610430,淳化县,4,610400 -610431,武功县,4,610400 -610481,兴平市,4,610400 -610482,彬州市,4,610400 -610502,临渭区,4,610500 -610503,华州区,4,610500 -610522,潼关县,4,610500 -610523,大荔县,4,610500 -610524,合阳县,4,610500 -610525,澄城县,4,610500 -610526,蒲城县,4,610500 -610527,白水县,4,610500 -610528,富平县,4,610500 -610581,韩城市,4,610500 -610582,华阴市,4,610500 -610602,宝塔区,4,610600 -610603,安塞区,4,610600 -610621,延长县,4,610600 -610622,延川县,4,610600 -610625,志丹县,4,610600 -610626,吴起县,4,610600 -610627,甘泉县,4,610600 -610628,富县,4,610600 -610629,洛川县,4,610600 -610630,宜川县,4,610600 -610631,黄龙县,4,610600 -610632,黄陵县,4,610600 -610681,子长市,4,610600 -610702,汉台区,4,610700 -610703,南郑区,4,610700 -610722,城固县,4,610700 -610723,洋县,4,610700 -610724,西乡县,4,610700 -610725,勉县,4,610700 -610726,宁强县,4,610700 -610727,略阳县,4,610700 -610728,镇巴县,4,610700 -610729,留坝县,4,610700 -610730,佛坪县,4,610700 -610802,榆阳区,4,610800 -610803,横山区,4,610800 -610822,府谷县,4,610800 -610824,靖边县,4,610800 -610825,定边县,4,610800 -610826,绥德县,4,610800 -610827,米脂县,4,610800 -610828,佳县,4,610800 -610829,吴堡县,4,610800 -610830,清涧县,4,610800 -610831,子洲县,4,610800 -610881,神木市,4,610800 -610902,汉滨区,4,610900 -610921,汉阴县,4,610900 -610922,石泉县,4,610900 -610923,宁陕县,4,610900 -610924,紫阳县,4,610900 -610925,岚皋县,4,610900 -610926,平利县,4,610900 -610927,镇坪县,4,610900 -610929,白河县,4,610900 -610981,旬阳市,4,610900 -611002,商州区,4,611000 -611021,洛南县,4,611000 -611022,丹凤县,4,611000 -611023,商南县,4,611000 -611024,山阳县,4,611000 -611025,镇安县,4,611000 -611026,柞水县,4,611000 -620102,城关区,4,620100 -620103,七里河区,4,620100 -620104,西固区,4,620100 -620105,安宁区,4,620100 -620111,红古区,4,620100 -620121,永登县,4,620100 -620122,皋兰县,4,620100 -620123,榆中县,4,620100 -620171,兰州新区,4,620100 -620201,嘉峪关市,4,620200 -620302,金川区,4,620300 -620321,永昌县,4,620300 -620402,白银区,4,620400 -620403,平川区,4,620400 -620421,靖远县,4,620400 -620422,会宁县,4,620400 -620423,景泰县,4,620400 -620502,秦州区,4,620500 -620503,麦积区,4,620500 -620521,清水县,4,620500 -620522,秦安县,4,620500 -620523,甘谷县,4,620500 -620524,武山县,4,620500 -620525,张家川回族自治县,4,620500 -620602,凉州区,4,620600 -620621,民勤县,4,620600 -620622,古浪县,4,620600 -620623,天祝藏族自治县,4,620600 -620702,甘州区,4,620700 -620721,肃南裕固族自治县,4,620700 -620722,民乐县,4,620700 -620723,临泽县,4,620700 -620724,高台县,4,620700 -620725,山丹县,4,620700 -620802,崆峒区,4,620800 -620821,泾川县,4,620800 -620822,灵台县,4,620800 -620823,崇信县,4,620800 -620825,庄浪县,4,620800 -620826,静宁县,4,620800 -620881,华亭市,4,620800 -620902,肃州区,4,620900 -620921,金塔县,4,620900 -620922,瓜州县,4,620900 -620923,肃北蒙古族自治县,4,620900 -620924,阿克塞哈萨克族自治县,4,620900 -620981,玉门市,4,620900 -620982,敦煌市,4,620900 -621002,西峰区,4,621000 -621021,庆城县,4,621000 -621022,环县,4,621000 -621023,华池县,4,621000 -621024,合水县,4,621000 -621025,正宁县,4,621000 -621026,宁县,4,621000 -621027,镇原县,4,621000 -621102,安定区,4,621100 -621121,通渭县,4,621100 -621122,陇西县,4,621100 -621123,渭源县,4,621100 -621124,临洮县,4,621100 -621125,漳县,4,621100 -621126,岷县,4,621100 -621202,武都区,4,621200 -621221,成县,4,621200 -621222,文县,4,621200 -621223,宕昌县,4,621200 -621224,康县,4,621200 -621225,西和县,4,621200 -621226,礼县,4,621200 -621227,徽县,4,621200 -621228,两当县,4,621200 -622901,临夏市,4,622900 -622921,临夏县,4,622900 -622922,康乐县,4,622900 -622923,永靖县,4,622900 -622924,广河县,4,622900 -622925,和政县,4,622900 -622926,东乡族自治县,4,622900 -622927,积石山保安族东乡族撒拉族自治县,4,622900 -623001,合作市,4,623000 -623021,临潭县,4,623000 -623022,卓尼县,4,623000 -623023,舟曲县,4,623000 -623024,迭部县,4,623000 -623025,玛曲县,4,623000 -623026,碌曲县,4,623000 -623027,夏河县,4,623000 -630102,城东区,4,630100 -630103,城中区,4,630100 -630104,城西区,4,630100 -630105,城北区,4,630100 -630106,湟中区,4,630100 -630121,大通回族土族自治县,4,630100 -630123,湟源县,4,630100 -630202,乐都区,4,630200 -630203,平安区,4,630200 -630222,民和回族土族自治县,4,630200 -630223,互助土族自治县,4,630200 -630224,化隆回族自治县,4,630200 -630225,循化撒拉族自治县,4,630200 -632221,门源回族自治县,4,632200 -632222,祁连县,4,632200 -632223,海晏县,4,632200 -632224,刚察县,4,632200 -632301,同仁市,4,632300 -632322,尖扎县,4,632300 -632323,泽库县,4,632300 -632324,河南蒙古族自治县,4,632300 -632521,共和县,4,632500 -632522,同德县,4,632500 -632523,贵德县,4,632500 -632524,兴海县,4,632500 -632525,贵南县,4,632500 -632621,玛沁县,4,632600 -632622,班玛县,4,632600 -632623,甘德县,4,632600 -632624,达日县,4,632600 -632625,久治县,4,632600 -632626,玛多县,4,632600 -632701,玉树市,4,632700 -632722,杂多县,4,632700 -632723,称多县,4,632700 -632724,治多县,4,632700 -632725,囊谦县,4,632700 -632726,曲麻莱县,4,632700 -632801,格尔木市,4,632800 -632802,德令哈市,4,632800 -632803,茫崖市,4,632800 -632821,乌兰县,4,632800 -632822,都兰县,4,632800 -632823,天峻县,4,632800 -632857,大柴旦行政委员会,4,632800 -640104,兴庆区,4,640100 -640105,西夏区,4,640100 -640106,金凤区,4,640100 -640121,永宁县,4,640100 -640122,贺兰县,4,640100 -640181,灵武市,4,640100 -640202,大武口区,4,640200 -640205,惠农区,4,640200 -640221,平罗县,4,640200 -640302,利通区,4,640300 -640303,红寺堡区,4,640300 -640323,盐池县,4,640300 -640324,同心县,4,640300 -640381,青铜峡市,4,640300 -640402,原州区,4,640400 -640422,西吉县,4,640400 -640423,隆德县,4,640400 -640424,泾源县,4,640400 -640425,彭阳县,4,640400 -640502,沙坡头区,4,640500 -640521,中宁县,4,640500 -640522,海原县,4,640500 -650102,天山区,4,650100 -650103,沙依巴克区,4,650100 -650104,新市区,4,650100 -650105,水磨沟区,4,650100 -650106,头屯河区,4,650100 -650107,达坂城区,4,650100 -650109,米东区,4,650100 -650121,乌鲁木齐县,4,650100 -650202,独山子区,4,650200 -650203,克拉玛依区,4,650200 -650204,白碱滩区,4,650200 -650205,乌尔禾区,4,650200 -650402,高昌区,4,650400 -650421,鄯善县,4,650400 -650422,托克逊县,4,650400 -650502,伊州区,4,650500 -650521,巴里坤哈萨克自治县,4,650500 -650522,伊吾县,4,650500 -652301,昌吉市,4,652300 -652302,阜康市,4,652300 -652323,呼图壁县,4,652300 -652324,玛纳斯县,4,652300 -652325,奇台县,4,652300 -652327,吉木萨尔县,4,652300 -652328,木垒哈萨克自治县,4,652300 -652701,博乐市,4,652700 -652702,阿拉山口市,4,652700 -652722,精河县,4,652700 -652723,温泉县,4,652700 -652801,库尔勒市,4,652800 -652822,轮台县,4,652800 -652823,尉犁县,4,652800 -652824,若羌县,4,652800 -652825,且末县,4,652800 -652826,焉耆回族自治县,4,652800 -652827,和静县,4,652800 -652828,和硕县,4,652800 -652829,博湖县,4,652800 -652871,库尔勒经济技术开发区,4,652800 -652901,阿克苏市,4,652900 -652902,库车市,4,652900 -652922,温宿县,4,652900 -652924,沙雅县,4,652900 -652925,新和县,4,652900 -652926,拜城县,4,652900 -652927,乌什县,4,652900 -652928,阿瓦提县,4,652900 -652929,柯坪县,4,652900 -653001,阿图什市,4,653000 -653022,阿克陶县,4,653000 -653023,阿合奇县,4,653000 -653024,乌恰县,4,653000 -653101,喀什市,4,653100 -653121,疏附县,4,653100 -653122,疏勒县,4,653100 -653123,英吉沙县,4,653100 -653124,泽普县,4,653100 -653125,莎车县,4,653100 -653126,叶城县,4,653100 -653127,麦盖提县,4,653100 -653128,岳普湖县,4,653100 -653129,伽师县,4,653100 -653130,巴楚县,4,653100 -653131,塔什库尔干塔吉克自治县,4,653100 -653201,和田市,4,653200 -653221,和田县,4,653200 -653222,墨玉县,4,653200 -653223,皮山县,4,653200 -653224,洛浦县,4,653200 -653225,策勒县,4,653200 -653226,于田县,4,653200 -653227,民丰县,4,653200 -654002,伊宁市,4,654000 -654003,奎屯市,4,654000 -654004,霍尔果斯市,4,654000 -654021,伊宁县,4,654000 -654022,察布查尔锡伯自治县,4,654000 -654023,霍城县,4,654000 -654024,巩留县,4,654000 -654025,新源县,4,654000 -654026,昭苏县,4,654000 -654027,特克斯县,4,654000 -654028,尼勒克县,4,654000 -654201,塔城市,4,654200 -654202,乌苏市,4,654200 -654203,沙湾市,4,654200 -654221,额敏县,4,654200 -654224,托里县,4,654200 -654225,裕民县,4,654200 -654226,和布克赛尔蒙古自治县,4,654200 -654301,阿勒泰市,4,654300 -654321,布尔津县,4,654300 -654322,富蕴县,4,654300 -654323,福海县,4,654300 -654324,哈巴河县,4,654300 -654325,青河县,4,654300 -654326,吉木乃县,4,654300 -659001,石河子市,4,659000 -659002,阿拉尔市,4,659000 -659003,图木舒克市,4,659000 -659004,五家渠市,4,659000 -659005,北屯市,4,659000 -659006,铁门关市,4,659000 -659007,双河市,4,659000 -659008,可克达拉市,4,659000 -659009,昆玉市,4,659000 -659010,胡杨河市,4,659000 -659011,新星市,4,659000 \ No newline at end of file diff --git a/iailab-framework/iailab-common-biz-ip/src/main/resources/ip2region.xdb b/iailab-framework/iailab-common-biz-ip/src/main/resources/ip2region.xdb deleted file mode 100644 index 58596a5..0000000 --- a/iailab-framework/iailab-common-biz-ip/src/main/resources/ip2region.xdb +++ /dev/null Binary files differ diff --git a/iailab-framework/iailab-common-biz-ip/src/test/java/com/iailab/framework/ip/core/utils/AreaUtilsTest.java b/iailab-framework/iailab-common-biz-ip/src/test/java/com/iailab/framework/ip/core/utils/AreaUtilsTest.java deleted file mode 100644 index 67d12d5..0000000 --- a/iailab-framework/iailab-common-biz-ip/src/test/java/com/iailab/framework/ip/core/utils/AreaUtilsTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.iailab.framework.ip.core.utils; - - -import com.iailab.framework.ip.core.Area; -import com.iailab.framework.ip.core.enums.AreaTypeEnum; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * {@link AreaUtils} 的单元测试 - * - * @author iailab - */ -public class AreaUtilsTest { - - @Test - public void testGetArea() { - // 调用:北京 - Area area = AreaUtils.getArea(110100); - // 断言 - assertEquals(area.getId(), 110100); - assertEquals(area.getName(), "北京市"); - assertEquals(area.getType(), AreaTypeEnum.CITY.getType()); - assertEquals(area.getParent().getId(), 110000); - assertEquals(area.getChildren().size(), 16); - } - - @Test - public void testFormat() { - assertEquals(AreaUtils.format(110105), "北京 北京市 朝阳区"); - assertEquals(AreaUtils.format(1), "中国"); - assertEquals(AreaUtils.format(2), "蒙古"); - } - -} diff --git a/iailab-framework/iailab-common-biz-ip/src/test/java/com/iailab/framework/ip/core/utils/IPUtilsTest.java b/iailab-framework/iailab-common-biz-ip/src/test/java/com/iailab/framework/ip/core/utils/IPUtilsTest.java deleted file mode 100644 index 4d4e3b8..0000000 --- a/iailab-framework/iailab-common-biz-ip/src/test/java/com/iailab/framework/ip/core/utils/IPUtilsTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.iailab.framework.ip.core.utils; - -import com.iailab.framework.ip.core.Area; -import org.junit.jupiter.api.Test; -import org.lionsoul.ip2region.xdb.Searcher; - - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * {@link IPUtils} 的单元测试 - * - * @author wanglhup - */ -public class IPUtilsTest { - - @Test - public void testGetAreaId_string() { - // 120.202.4.0|120.202.4.255|420600 - Integer areaId = IPUtils.getAreaId("120.202.4.50"); - assertEquals(420600, areaId); - } - - @Test - public void testGetAreaId_long() throws Exception { - // 120.203.123.0|120.203.133.255|360900 - long ip = Searcher.checkIP("120.203.123.250"); - Integer areaId = IPUtils.getAreaId(ip); - assertEquals(360900, areaId); - } - - @Test - public void testGetArea_string() { - // 120.202.4.0|120.202.4.255|420600 - Area area = IPUtils.getArea("120.202.4.50"); - assertEquals("襄阳市", area.getName()); - } - - @Test - public void testGetArea_long() throws Exception { - // 120.203.123.0|120.203.133.255|360900 - long ip = Searcher.checkIP("120.203.123.252"); - Area area = IPUtils.getArea(ip); - assertEquals("宜春市", area.getName()); - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/pom.xml b/iailab-framework/iailab-common-biz-tenant/pom.xml deleted file mode 100644 index 3c4431e..0000000 --- a/iailab-framework/iailab-common-biz-tenant/pom.xml +++ /dev/null @@ -1,96 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <artifactId>iailab-framework</artifactId> - <groupId>com.iailab</groupId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-biz-tenant</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description>多租户</description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common</artifactId> - </dependency> - - <!-- Web 相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-security</artifactId> - </dependency> - - <!-- DB 相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-mybatis</artifactId> - </dependency> - - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-redis</artifactId> - </dependency> - <dependency> - <groupId>org.postgresql</groupId> - <artifactId>postgresql</artifactId> - </dependency> - - <!-- RPC 远程调用相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-rpc</artifactId> - <optional>true</optional> - </dependency> - - <!-- Job 定时任务相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-job</artifactId> - <optional>true</optional> - </dependency> - - <!-- 消息队列相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-mq</artifactId> - <optional>true</optional> - </dependency> - <dependency> - <groupId>org.springframework.kafka</groupId> - <artifactId>spring-kafka</artifactId> - <optional>true</optional> - </dependency> - <dependency> - <groupId>org.springframework.amqp</groupId> - <artifactId>spring-rabbit</artifactId> - <optional>true</optional> - </dependency> - <dependency> - <groupId>org.apache.rocketmq</groupId> - <artifactId>rocketmq-spring-boot-starter</artifactId> - <optional>true</optional> - </dependency> - - <!-- Test 测试相关 --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-test</artifactId> - <scope>test</scope> - </dependency> - - <!-- 工具类相关 --> - <dependency> - <groupId>com.google.guava</groupId> - <artifactId>guava</artifactId> - </dependency> - - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/config/IailabTenantAutoConfiguration.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/config/IailabTenantAutoConfiguration.java deleted file mode 100644 index 14060f8..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/config/IailabTenantAutoConfiguration.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.iailab.framework.tenant.config; - -import com.baomidou.dynamic.datasource.processor.DsProcessor; -import com.baomidou.dynamic.datasource.processor.DsSpelExpressionProcessor; -import com.iailab.framework.common.enums.WebFilterOrderEnum; -import com.iailab.framework.mybatis.core.util.MyBatisUtils; -import com.iailab.framework.redis.config.IailabCacheProperties; -import com.iailab.framework.tenant.core.aop.TenantIgnoreAspect; -import com.iailab.framework.tenant.core.db.TenantDatabaseInterceptor; -import com.iailab.framework.tenant.core.db.dynamic.TenantDsProcessor; -import com.iailab.framework.tenant.core.job.TenantJobAspect; -import com.iailab.framework.tenant.core.mq.rabbitmq.TenantRabbitMQInitializer; -import com.iailab.framework.tenant.core.mq.redis.TenantRedisMessageInterceptor; -import com.iailab.framework.tenant.core.mq.rocketmq.TenantRocketMQInitializer; -import com.iailab.framework.tenant.core.redis.TenantRedisCacheManager; -import com.iailab.framework.tenant.core.security.TenantSecurityWebFilter; -import com.iailab.framework.tenant.core.service.TenantFrameworkService; -import com.iailab.framework.tenant.core.service.TenantFrameworkServiceImpl; -import com.iailab.framework.tenant.core.web.TenantContextWebFilter; -import com.iailab.framework.web.config.WebProperties; -import com.iailab.framework.web.core.handler.GlobalExceptionHandler; -import com.iailab.module.system.api.tenant.TenantApi; -import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; -import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.data.redis.cache.BatchStrategies; -import org.springframework.data.redis.cache.RedisCacheConfiguration; -import org.springframework.data.redis.cache.RedisCacheManager; -import org.springframework.data.redis.cache.RedisCacheWriter; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; - -import java.util.Objects; - -@AutoConfiguration -@ConditionalOnProperty(prefix = "iailab.tenant", value = "enable", matchIfMissing = true) // 允许使用 iailab.tenant.enable=false 禁用多租户 -@EnableConfigurationProperties(TenantProperties.class) -public class IailabTenantAutoConfiguration { - - @Bean - public TenantFrameworkService tenantFrameworkService(TenantApi tenantApi) { - return new TenantFrameworkServiceImpl(tenantApi); - } - - // ========== AOP ========== - - @Bean - public TenantIgnoreAspect tenantIgnoreAspect() { - return new TenantIgnoreAspect(); - } - - // ========== DB ========== - - @Bean - public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties, - MybatisPlusInterceptor interceptor) { - TenantLineInnerInterceptor inner = new TenantLineInnerInterceptor(new TenantDatabaseInterceptor(properties)); - // 添加到 interceptor 中 - // 需要加在首个,主要是为了在分页插件前面。这个是 MyBatis Plus 的规定 - MyBatisUtils.addInterceptor(interceptor, inner, 0); - return inner; - } - - @Bean - public DsProcessor dsProcessor( -// TenantFrameworkService tenantFrameworkService, -// DataSource dataSource, -// DefaultDataSourceCreator dataSourceCreator - ) { -// TenantDsProcessor tenantDsProcessor = new TenantDsProcessor(tenantFrameworkService, dataSourceCreator); - TenantDsProcessor tenantDsProcessor = new TenantDsProcessor(); - tenantDsProcessor.setNextProcessor(new DsSpelExpressionProcessor()); - return tenantDsProcessor; - } - - // ========== WEB ========== - - @Bean - public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter() { - FilterRegistrationBean<TenantContextWebFilter> registrationBean = new FilterRegistrationBean<>(); - registrationBean.setFilter(new TenantContextWebFilter()); - registrationBean.setOrder(WebFilterOrderEnum.TENANT_CONTEXT_FILTER); - return registrationBean; - } - - // ========== Security ========== - - @Bean - public FilterRegistrationBean<TenantSecurityWebFilter> tenantSecurityWebFilter(TenantProperties tenantProperties, - WebProperties webProperties, - GlobalExceptionHandler globalExceptionHandler, - TenantFrameworkService tenantFrameworkService) { - FilterRegistrationBean<TenantSecurityWebFilter> registrationBean = new FilterRegistrationBean<>(); - registrationBean.setFilter(new TenantSecurityWebFilter(tenantProperties, webProperties, - globalExceptionHandler, tenantFrameworkService)); - registrationBean.setOrder(WebFilterOrderEnum.TENANT_SECURITY_FILTER); - return registrationBean; - } - - // ========== Job ========== - - @Bean - @ConditionalOnClass(name = "com.xxl.job.core.handler.annotation.XxlJob") - public TenantJobAspect tenantJobAspect(TenantFrameworkService tenantFrameworkService) { - return new TenantJobAspect(tenantFrameworkService); - } - - // ========== MQ ========== - - /** - * 多租户 Redis 消息队列的配置类 - * - * 为什么要单独一个配置类呢?如果直接把 TenantRedisMessageInterceptor Bean 的初始化放外面,会报 RedisMessageInterceptor 类不存在的错误 - */ - @Configuration - @ConditionalOnClass(name = "com.iailab.framework.mq.redis.core.RedisMQTemplate") - public static class TenantRedisMQAutoConfiguration { - - @Bean - public TenantRedisMessageInterceptor tenantRedisMessageInterceptor() { - return new TenantRedisMessageInterceptor(); - } - - } - - @Bean - @ConditionalOnClass(name = "org.springframework.amqp.rabbit.core.RabbitTemplate") - public TenantRabbitMQInitializer tenantRabbitMQInitializer() { - return new TenantRabbitMQInitializer(); - } - - @Bean - @ConditionalOnClass(name = "org.apache.rocketmq.spring.core.RocketMQTemplate") - public TenantRocketMQInitializer tenantRocketMQInitializer() { - return new TenantRocketMQInitializer(); - } - - // ========== Redis ========== - - @Bean - @Primary // 引入租户时,tenantRedisCacheManager 为主 Bean - public RedisCacheManager tenantRedisCacheManager(RedisTemplate<String, Object> redisTemplate, - RedisCacheConfiguration redisCacheConfiguration, - IailabCacheProperties iailabCacheProperties, - TenantProperties tenantProperties) { - // 创建 RedisCacheWriter 对象 - RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory()); - RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory, - BatchStrategies.scan(iailabCacheProperties.getRedisScanBatchSize())); - // 创建 TenantRedisCacheManager 对象 - return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration, tenantProperties.getIgnoreCaches()); - } -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/config/IailabTenantRpcAutoConfiguration.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/config/IailabTenantRpcAutoConfiguration.java deleted file mode 100644 index cfa5d68..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/config/IailabTenantRpcAutoConfiguration.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.iailab.framework.tenant.config; - -import com.iailab.framework.tenant.core.rpc.TenantRequestInterceptor; -import com.iailab.module.system.api.tenant.TenantApi; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.cloud.openfeign.EnableFeignClients; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@AutoConfiguration -@ConditionalOnProperty(prefix = "iailab.tenant", value = "enable", matchIfMissing = true) // 允许使用 iailab.tenant.enable=false 禁用多租户 -@EnableFeignClients(clients = TenantApi.class) // 主要是引入相关的 API 服务 -public class IailabTenantRpcAutoConfiguration { - - @Bean - public TenantRequestInterceptor tenantRequestInterceptor() { - return new TenantRequestInterceptor(); - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/config/TenantProperties.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/config/TenantProperties.java deleted file mode 100644 index 4fc2ddf..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/config/TenantProperties.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.iailab.framework.tenant.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; - -import java.util.Collections; -import java.util.Set; - -/** - * 多租户配置 - * - * @author iailab - */ -@ConfigurationProperties(prefix = "iailab.tenant") -@Data -public class TenantProperties { - - /** - * 租户是否开启 - */ - private static final Boolean ENABLE_DEFAULT = true; - - /** - * 是否开启 - */ - private Boolean enable = ENABLE_DEFAULT; - - /** - * 需要忽略多租户的请求 - * - * 默认情况下,每个请求需要带上 tenant-id 的请求头。但是,部分请求是无需带上的,例如说短信回调、支付回调等 Open API! - */ - private Set<String> ignoreUrls = Collections.emptySet(); - - /** - * 需要忽略多租户的表 - * - * 即默认所有表都开启多租户的功能,所以记得添加对应的 tenant_id 字段哟 - */ - private Set<String> ignoreTables = Collections.emptySet(); - - /** - * 需要忽略多租户的 Spring Cache 缓存 - * - * 即默认所有缓存都开启多租户的功能,所以记得添加对应的 tenant_id 字段哟 - */ - private Set<String> ignoreCaches = Collections.emptySet(); - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/aop/TenantIgnore.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/aop/TenantIgnore.java deleted file mode 100644 index adf4915..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/aop/TenantIgnore.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.iailab.framework.tenant.core.aop; - -import java.lang.annotation.*; - -/** - * 忽略租户,标记指定方法不进行租户的自动过滤 - * - * 注意,只有 DB 的场景会过滤,其它场景暂时不过滤: - * 1、Redis 场景:因为是基于 Key 实现多租户的能力,所以忽略没有意义,不像 DB 是一个 column 实现的 - * 2、MQ 场景:有点难以抉择,目前可以通过 Consumer 手动在消费的方法上,添加 @TenantIgnore 进行忽略 - * - * @author iailab - */ -@Target({ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -@Inherited -public @interface TenantIgnore { -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/aop/TenantIgnoreAspect.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/aop/TenantIgnoreAspect.java deleted file mode 100644 index 4814290..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/aop/TenantIgnoreAspect.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.iailab.framework.tenant.core.aop; - -import com.iailab.framework.tenant.core.context.TenantContextHolder; -import com.iailab.framework.tenant.core.util.TenantUtils; -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; - -/** - * 忽略多租户的 Aspect,基于 {@link TenantIgnore} 注解实现,用于一些全局的逻辑。 - * 例如说,一个定时任务,读取所有数据,进行处理。 - * 又例如说,读取所有数据,进行缓存。 - * - * 整体逻辑的实现,和 {@link TenantUtils#executeIgnore(Runnable)} 需要保持一致 - * - * @author iailab - */ -@Aspect -@Slf4j -public class TenantIgnoreAspect { - - @Around("@annotation(tenantIgnore)") - public Object around(ProceedingJoinPoint joinPoint, TenantIgnore tenantIgnore) throws Throwable { - Boolean oldIgnore = TenantContextHolder.isIgnore(); - try { - TenantContextHolder.setIgnore(true); - // 执行逻辑 - return joinPoint.proceed(); - } finally { - TenantContextHolder.setIgnore(oldIgnore); - } - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/context/DataContextHolder.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/context/DataContextHolder.java deleted file mode 100644 index 297ed1d..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/context/DataContextHolder.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.iailab.framework.tenant.core.context; - -import com.alibaba.ttl.TransmittableThreadLocal; -import com.iailab.framework.common.enums.DocumentEnum; - -/** - * 数据源上下文 Holder - * - * @author iailab - */ -public class DataContextHolder { - - /** - * 数据源id - */ - private static final ThreadLocal<Long> DATA_SOURCE_ID = new TransmittableThreadLocal<>(); - - /** - * 数据源id - * - * @return 租户编号 - */ - public static Long getDataSourceId() { - return DATA_SOURCE_ID.get(); - } - - /** - * 数据源id。如果不存在,则抛出 NullPointerException 异常 - * - * @return 租户编号 - */ - public static Long getRequiredDataSourceId() { - Long dataSourceId = getDataSourceId(); - if (dataSourceId == null) { - throw new NullPointerException("DataContextHolder 不存在数据源id!可参考文档:" - + DocumentEnum.TENANT.getUrl()); - } - return dataSourceId; - } - - public static void setDataSourceId(Long dataSourceId) { - DATA_SOURCE_ID.set(dataSourceId); - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/context/TenantContextHolder.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/context/TenantContextHolder.java deleted file mode 100644 index 1407a84..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/context/TenantContextHolder.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.iailab.framework.tenant.core.context; - -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.enums.DocumentEnum; -import com.alibaba.ttl.TransmittableThreadLocal; - -/** - * 多租户上下文 Holder - * - * @author iailab - */ -public class TenantContextHolder { - - /** - * 当前租户编号 - */ - private static final ThreadLocal<Long> TENANT_ID = new TransmittableThreadLocal<>(); - - /** - * 是否忽略租户 - */ - private static final ThreadLocal<Boolean> IGNORE = new TransmittableThreadLocal<>(); - - /** - * 获得租户编号 - * - * @return 租户编号 - */ - public static Long getTenantId() { - return TENANT_ID.get(); - } - - /** - * 获得租户编号。如果不存在,则抛出 NullPointerException 异常 - * - * @return 租户编号 - */ - public static Long getRequiredTenantId() { - Long tenantId = getTenantId(); - if (tenantId == null) { - throw new NullPointerException("TenantContextHolder 不存在租户编号!可参考文档:" - + DocumentEnum.TENANT.getUrl()); - } - return tenantId; - } - - public static void setTenantId(Long tenantId) { - TENANT_ID.set(tenantId); - } - - public static void setIgnore(Boolean ignore) { - IGNORE.set(ignore); - } - - /** - * 当前是否忽略租户 - * - * @return 是否忽略 - */ - public static boolean isIgnore() { - return Boolean.TRUE.equals(IGNORE.get()); - } - - public static void clear() { - TENANT_ID.remove(); - IGNORE.remove(); - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/TenantBaseDO.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/TenantBaseDO.java deleted file mode 100644 index bcfa9a8..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/TenantBaseDO.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.iailab.framework.tenant.core.db; - -import com.iailab.framework.mybatis.core.dataobject.BaseDO; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 拓展多租户的 BaseDO 基类 - * - * @author iailab - */ -@Data -@EqualsAndHashCode(callSuper = true) -public abstract class TenantBaseDO extends BaseDO { - - /** - * 多租户编号 - */ - private Long tenantId; - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/TenantDatabaseInterceptor.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/TenantDatabaseInterceptor.java deleted file mode 100644 index d24f92c..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/TenantDatabaseInterceptor.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.iailab.framework.tenant.core.db; - -import cn.hutool.core.collection.CollUtil; -import com.iailab.framework.tenant.config.TenantProperties; -import com.iailab.framework.tenant.core.context.TenantContextHolder; -import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; -import net.sf.jsqlparser.expression.Expression; -import net.sf.jsqlparser.expression.LongValue; - -import java.util.HashSet; -import java.util.Set; - -/** - * 基于 MyBatis Plus 多租户的功能,实现 DB 层面的多租户的功能 - * - * @author iailab - */ -public class TenantDatabaseInterceptor implements TenantLineHandler { - - private final Set<String> ignoreTables = new HashSet<>(); - - public TenantDatabaseInterceptor(TenantProperties properties) { - // 不同 DB 下,大小写的习惯不同,所以需要都添加进去 - properties.getIgnoreTables().forEach(table -> { - ignoreTables.add(table.toLowerCase()); - ignoreTables.add(table.toUpperCase()); - }); - // 在 OracleKeyGenerator 中,生成主键时,会查询这个表,查询这个表后,会自动拼接 TENANT_ID 导致报错 - ignoreTables.add("DUAL"); - } - - @Override - public Expression getTenantId() { - return new LongValue(TenantContextHolder.getRequiredTenantId()); - } - - @Override - public boolean ignoreTable(String tableName) { - return TenantContextHolder.isIgnore() // 情况一,全局忽略多租户 - || CollUtil.contains(ignoreTables, tableName); // 情况二,忽略多租户的表 - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/dynamic/DataDS.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/dynamic/DataDS.java deleted file mode 100644 index fce0fbb..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/dynamic/DataDS.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.tenant.core.db.dynamic; - -import com.baomidou.dynamic.datasource.annotation.DS; - -import java.lang.annotation.*; - -/** - * 使用数据源所在的数据源 - * - * 使用方式:当我们希望一个表使用租户所在的数据源,可以在该表的 Mapper 上添加该注解 - * - * @author 芋道源码 - */ -@Target({ElementType.TYPE, ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@DS(DataDS.KEY) -public @interface DataDS { - - /** - * 数据源的占位符 - */ - String KEY = "#context.dataSourceId"; - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/dynamic/TenantDS.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/dynamic/TenantDS.java deleted file mode 100644 index bf7da0c..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/dynamic/TenantDS.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.tenant.core.db.dynamic; - -import com.baomidou.dynamic.datasource.annotation.DS; - -import java.lang.annotation.*; - -/** - * 使用租户所在的数据源 - * - * 使用方式:当我们希望一个表使用租户所在的数据源,可以在该表的 Mapper 上添加该注解 - * - * @author 芋道源码 - */ -@Target({ElementType.TYPE, ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@DS(TenantDS.KEY) -public @interface TenantDS { - - /** - * 租户对应的数据源的占位符 - */ - String KEY = "#context.tenantId"; - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/dynamic/TenantDsProcessor.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/dynamic/TenantDsProcessor.java deleted file mode 100644 index 24b2eff..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/dynamic/TenantDsProcessor.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.iailab.framework.tenant.core.db.dynamic; - -import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; -import com.baomidou.dynamic.datasource.creator.DataSourceProperty; -import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator; -import com.baomidou.dynamic.datasource.processor.DsProcessor; -import com.iailab.framework.tenant.core.context.DataContextHolder; -import com.iailab.framework.tenant.core.context.TenantContextHolder; -import com.iailab.framework.tenant.core.service.TenantFrameworkService; -import com.iailab.module.infra.api.db.DataSourceConfigServiceApi; -import com.iailab.module.infra.api.db.dto.DataSourceConfigRespDTO; -import lombok.RequiredArgsConstructor; -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.context.annotation.Lazy; - -import javax.annotation.Resource; -import javax.sql.DataSource; -import java.util.Objects; - -/** - * 基于 {@link TenantDS} 的数据源处理器 - * - * 1. 如果有 @TenantDS 注解,返回该租户的数据源 - * 2. 如果该租户的数据源未创建,则进行创建 - * - * @author 芋道源码 - */ -@RequiredArgsConstructor -public class TenantDsProcessor extends DsProcessor { - - /** - * 用于获取租户数据源配置的 Service - */ - @Resource - @Lazy - private TenantFrameworkService tenantFrameworkService; - - /** - * 动态数据源 - */ - @Resource - @Lazy // 为什么添加 @Lazy 注解?因为它和 DynamicRoutingDataSource 相互依赖,导致无法初始化 - private DynamicRoutingDataSource dynamicRoutingDataSource; - - /** - * 用于创建租户数据源的 Creator - */ - @Resource - @Lazy - private DefaultDataSourceCreator dataSourceCreator; - - @Resource - @Lazy - private DataSourceConfigServiceApi dataSourceConfigServiceApi; - - @Override - public boolean matches(String key) { - return Objects.equals(key, TenantDS.KEY) || Objects.equals(key, DataDS.KEY); - } - - @Override - public String doDetermineDatasource(MethodInvocation invocation, String key) { - if (DataDS.KEY.equals(key)){ - // 获得数据源配置 - Long dataSourceId = DataContextHolder.getRequiredDataSourceId(); - DataSourceConfigRespDTO dataSourceConfigRespDTO = dataSourceConfigServiceApi.getDataSourceConfig(dataSourceId); - DataSourceProperty dataSourceProperty = new DataSourceProperty(); - dataSourceProperty.setPoolName(dataSourceConfigRespDTO.getName()); - dataSourceProperty.setUrl(dataSourceConfigRespDTO.getUrl()); - dataSourceProperty.setUsername(dataSourceConfigRespDTO.getUsername()); - dataSourceProperty.setPassword(dataSourceConfigRespDTO.getPassword()); - // 创建 or 创建数据源,并返回数据源名字 - return createDatasourceIfAbsent(dataSourceProperty); - }else if(TenantDS.KEY.equals(key)){ - // 获得数据源配置 - Long tenantId = TenantContextHolder.getRequiredTenantId(); - DataSourceProperty dataSourceProperty = tenantFrameworkService.getDataSourceProperty(tenantId); - // 创建 or 创建数据源,并返回数据源名字 - return createDatasourceIfAbsent(dataSourceProperty); - } - return key; - } - - private String createDatasourceIfAbsent(DataSourceProperty dataSourceProperty) { - // 1. 重点:如果数据源不存在,则进行创建 - if (isDataSourceNotExist(dataSourceProperty)) { - // 问题一:为什么要加锁?因为,如果多个线程同时执行到这里,会导致多次创建数据源 - // 问题二:为什么要使用 poolName 加锁?保证多个不同的 poolName 可以并发创建数据源 - // 问题三:为什么要使用 intern 方法?因为,intern 方法,会返回一个字符串的常量池中的引用 - // intern 的说明,可见 https://www.cnblogs.com/xrq730/p/6662232.html 文章 - synchronized (dataSourceProperty.getPoolName().intern()) { - if (isDataSourceNotExist(dataSourceProperty)) { - DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty); - dynamicRoutingDataSource.addDataSource(dataSourceProperty.getPoolName(), dataSource); - } - } - } - // 2. 返回数据源的名字 - return dataSourceProperty.getPoolName(); - } - - private boolean isDataSourceNotExist(DataSourceProperty dataSourceProperty) { - return !dynamicRoutingDataSource.getDataSources().containsKey(dataSourceProperty.getPoolName()); - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/job/TenantJob.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/job/TenantJob.java deleted file mode 100644 index fd790d7..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/job/TenantJob.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iailab.framework.tenant.core.job; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 多租户 Job 注解 - */ -@Target({ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface TenantJob { -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/job/TenantJobAspect.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/job/TenantJobAspect.java deleted file mode 100644 index 5d7a9ac..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/job/TenantJobAspect.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.iailab.framework.tenant.core.job; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.exceptions.ExceptionUtil; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.framework.tenant.core.service.TenantFrameworkService; -import com.iailab.framework.tenant.core.util.TenantUtils; -import com.xxl.job.core.context.XxlJobHelper; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 多租户 JobHandler AOP - * 任务执行时,会按照租户逐个执行 Job 的逻辑 - * - * 注意,需要保证 JobHandler 的幂等性。因为 Job 因为某个租户执行失败重试时,之前执行成功的租户也会再次执行。 - * - * @author iailab - */ -@Aspect -@RequiredArgsConstructor -@Slf4j -public class TenantJobAspect { - - private final TenantFrameworkService tenantFrameworkService; - - @Around("@annotation(tenantJob)") - public void around(ProceedingJoinPoint joinPoint, TenantJob tenantJob) { - // 获得租户列表 - List<Long> tenantIds = tenantFrameworkService.getTenantIds(); - if (CollUtil.isEmpty(tenantIds)) { - return; - } - - // 逐个租户,执行 Job - Map<Long, String> results = new ConcurrentHashMap<>(); - tenantIds.parallelStream().forEach(tenantId -> { - // TODO iailab:先通过 parallel 实现并行;1)多个租户,是一条执行日志;2)异常的情况 - TenantUtils.execute(tenantId, () -> { - try { - System.out.println("租户id:" + tenantId); - joinPoint.proceed(); - } catch (Throwable e) { - results.put(tenantId, ExceptionUtil.getRootCauseMessage(e)); - // 打印异常 - XxlJobHelper.log(StrUtil.format("[多租户({}) 执行任务({}),发生异常:{}]", - tenantId, joinPoint.getSignature(), ExceptionUtils.getStackTrace(e))); - } - }); - }); - // 如果 results 非空,说明发生了异常,标记 XXL-Job 执行失败 - if (CollUtil.isNotEmpty(results)) { - XxlJobHelper.handleFail(JsonUtils.toJsonString(results)); - } - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/kafka/TenantKafkaEnvironmentPostProcessor.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/kafka/TenantKafkaEnvironmentPostProcessor.java deleted file mode 100644 index 480b4c6..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/kafka/TenantKafkaEnvironmentPostProcessor.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.iailab.framework.tenant.core.mq.kafka; - -import cn.hutool.core.util.StrUtil; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.env.EnvironmentPostProcessor; -import org.springframework.core.env.ConfigurableEnvironment; - -/** - * 多租户的 Kafka 的 {@link EnvironmentPostProcessor} 实现类 - * - * Kafka Producer 发送消息时,增加 {@link TenantKafkaProducerInterceptor} 拦截器 - * - * @author iailab - */ -@Slf4j -public class TenantKafkaEnvironmentPostProcessor implements EnvironmentPostProcessor { - - private static final String PROPERTY_KEY_INTERCEPTOR_CLASSES = "spring.kafka.producer.properties.interceptor.classes"; - - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { - // 添加 TenantKafkaProducerInterceptor 拦截器 - try { - String value = environment.getProperty(PROPERTY_KEY_INTERCEPTOR_CLASSES); - if (StrUtil.isEmpty(value)) { - value = TenantKafkaProducerInterceptor.class.getName(); - } else { - value += "," + TenantKafkaProducerInterceptor.class.getName(); - } - environment.getSystemProperties().put(PROPERTY_KEY_INTERCEPTOR_CLASSES, value); - } catch (NoClassDefFoundError ignore) { - // 如果触发 NoClassDefFoundError 异常,说明 TenantKafkaProducerInterceptor 类不存在,即没引入 kafka-spring 依赖 - } - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/kafka/TenantKafkaProducerInterceptor.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/kafka/TenantKafkaProducerInterceptor.java deleted file mode 100644 index 6bdcec3..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/kafka/TenantKafkaProducerInterceptor.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.iailab.framework.tenant.core.mq.kafka; - -import cn.hutool.core.util.ReflectUtil; -import com.iailab.framework.tenant.core.context.TenantContextHolder; -import org.apache.kafka.clients.producer.ProducerInterceptor; -import org.apache.kafka.clients.producer.ProducerRecord; -import org.apache.kafka.clients.producer.RecordMetadata; -import org.apache.kafka.common.header.Headers; -import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; - -import java.util.Map; - -import static com.iailab.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; - -/** - * Kafka 消息队列的多租户 {@link ProducerInterceptor} 实现类 - * - * 1. Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 - * 2. Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中,通过 {@link InvocableHandlerMethod} 实现 - * - * @author iailab - */ -public class TenantKafkaProducerInterceptor implements ProducerInterceptor<Object, Object> { - - @Override - public ProducerRecord<Object, Object> onSend(ProducerRecord<Object, Object> record) { - Long tenantId = TenantContextHolder.getTenantId(); - if (tenantId != null) { - Headers headers = (Headers) ReflectUtil.getFieldValue(record, "headers"); // private 属性,没有 get 方法,智能反射 - headers.add(HEADER_TENANT_ID, tenantId.toString().getBytes()); - } - return record; - } - - @Override - public void onAcknowledgement(RecordMetadata metadata, Exception exception) { - } - - @Override - public void close() { - } - - @Override - public void configure(Map<String, ?> configs) { - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rabbitmq/TenantRabbitMQInitializer.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rabbitmq/TenantRabbitMQInitializer.java deleted file mode 100644 index b990d14..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rabbitmq/TenantRabbitMQInitializer.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.iailab.framework.tenant.core.mq.rabbitmq; - -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; - -/** - * 多租户的 RabbitMQ 初始化器 - * - * @author iailab - */ -public class TenantRabbitMQInitializer implements BeanPostProcessor { - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof RabbitTemplate) { - RabbitTemplate rabbitTemplate = (RabbitTemplate) bean; - rabbitTemplate.addBeforePublishPostProcessors(new TenantRabbitMQMessagePostProcessor()); - } - return bean; - } - -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rabbitmq/TenantRabbitMQMessagePostProcessor.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rabbitmq/TenantRabbitMQMessagePostProcessor.java deleted file mode 100644 index 577ae3d..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rabbitmq/TenantRabbitMQMessagePostProcessor.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.iailab.framework.tenant.core.mq.rabbitmq; - -import com.iailab.framework.tenant.core.context.TenantContextHolder; -import org.apache.kafka.clients.producer.ProducerInterceptor; -import org.springframework.amqp.AmqpException; -import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.MessagePostProcessor; -import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; - -import static com.iailab.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; - -/** - * RabbitMQ 消息队列的多租户 {@link ProducerInterceptor} 实现类 - * - * 1. Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 - * 2. Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中,通过 {@link InvocableHandlerMethod} 实现 - * - * @author iailab - */ -public class TenantRabbitMQMessagePostProcessor implements MessagePostProcessor { - - @Override - public Message postProcessMessage(Message message) throws AmqpException { - Long tenantId = TenantContextHolder.getTenantId(); - if (tenantId != null) { - message.getMessageProperties().getHeaders().put(HEADER_TENANT_ID, tenantId); - } - return message; - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/redis/TenantRedisMessageInterceptor.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/redis/TenantRedisMessageInterceptor.java deleted file mode 100644 index 1c7ad18..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/redis/TenantRedisMessageInterceptor.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.iailab.framework.tenant.core.mq.redis; - -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.mq.redis.core.interceptor.RedisMessageInterceptor; -import com.iailab.framework.mq.redis.core.message.AbstractRedisMessage; -import com.iailab.framework.tenant.core.context.TenantContextHolder; - -import static com.iailab.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; - -/** - * 多租户 {@link AbstractRedisMessage} 拦截器 - * - * 1. Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 - * 2. Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中 - * - * @author iailab - */ -public class TenantRedisMessageInterceptor implements RedisMessageInterceptor { - - @Override - public void sendMessageBefore(AbstractRedisMessage message) { - Long tenantId = TenantContextHolder.getTenantId(); - if (tenantId != null) { - message.addHeader(HEADER_TENANT_ID, tenantId.toString()); - } - } - - @Override - public void consumeMessageBefore(AbstractRedisMessage message) { - String tenantIdStr = message.getHeader(HEADER_TENANT_ID); - if (StrUtil.isNotEmpty(tenantIdStr)) { - TenantContextHolder.setTenantId(Long.valueOf(tenantIdStr)); - } - } - - @Override - public void consumeMessageAfter(AbstractRedisMessage message) { - // 注意,Consumer 是一个逻辑的入口,所以不考虑原本上下文就存在租户编号的情况 - TenantContextHolder.clear(); - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rocketmq/TenantRocketMQConsumeMessageHook.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rocketmq/TenantRocketMQConsumeMessageHook.java deleted file mode 100644 index c934b21..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rocketmq/TenantRocketMQConsumeMessageHook.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.iailab.framework.tenant.core.mq.rocketmq; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.tenant.core.context.TenantContextHolder; -import org.apache.rocketmq.client.hook.ConsumeMessageContext; -import org.apache.rocketmq.client.hook.ConsumeMessageHook; -import org.apache.rocketmq.common.message.MessageExt; -import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; - -import java.util.List; - -import static com.iailab.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; - -/** - * RocketMQ 消息队列的多租户 {@link ConsumeMessageHook} 实现类 - * - * Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中,通过 {@link InvocableHandlerMethod} 实现 - * - * @author iailab - */ -public class TenantRocketMQConsumeMessageHook implements ConsumeMessageHook { - - @Override - public String hookName() { - return getClass().getSimpleName(); - } - - @Override - public void consumeMessageBefore(ConsumeMessageContext context) { - // 校验,消息必须是单条,不然设置租户可能不正确 - List<MessageExt> messages = context.getMsgList(); - Assert.isTrue(messages.size() == 1, "消息条数({})不正确", messages.size()); - // 设置租户编号 - String tenantId = messages.get(0).getUserProperty(HEADER_TENANT_ID); - if (StrUtil.isNotEmpty(tenantId)) { - TenantContextHolder.setTenantId(Long.parseLong(tenantId)); - } - } - - @Override - public void consumeMessageAfter(ConsumeMessageContext context) { - TenantContextHolder.clear(); - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rocketmq/TenantRocketMQInitializer.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rocketmq/TenantRocketMQInitializer.java deleted file mode 100644 index eab04d7..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rocketmq/TenantRocketMQInitializer.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.iailab.framework.tenant.core.mq.rocketmq; - -import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; -import org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl; -import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl; -import org.apache.rocketmq.client.producer.DefaultMQProducer; -import org.apache.rocketmq.spring.core.RocketMQTemplate; -import org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; - -/** - * 多租户的 RocketMQ 初始化器 - * - * @author iailab - */ -public class TenantRocketMQInitializer implements BeanPostProcessor { - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof DefaultRocketMQListenerContainer) { - DefaultRocketMQListenerContainer container = (DefaultRocketMQListenerContainer) bean; - initTenantConsumer(container.getConsumer()); - } else if (bean instanceof RocketMQTemplate) { - RocketMQTemplate template = (RocketMQTemplate) bean; - initTenantProducer(template.getProducer()); - } - return bean; - } - - private void initTenantProducer(DefaultMQProducer producer) { - if (producer == null) { - return; - } - DefaultMQProducerImpl producerImpl = producer.getDefaultMQProducerImpl(); - if (producerImpl == null) { - return; - } - producerImpl.registerSendMessageHook(new TenantRocketMQSendMessageHook()); - } - - private void initTenantConsumer(DefaultMQPushConsumer consumer) { - if (consumer == null) { - return; - } - DefaultMQPushConsumerImpl consumerImpl = consumer.getDefaultMQPushConsumerImpl(); - if (consumerImpl == null) { - return; - } - consumerImpl.registerConsumeMessageHook(new TenantRocketMQConsumeMessageHook()); - } - -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rocketmq/TenantRocketMQSendMessageHook.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rocketmq/TenantRocketMQSendMessageHook.java deleted file mode 100644 index 6aac75d..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/mq/rocketmq/TenantRocketMQSendMessageHook.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.iailab.framework.tenant.core.mq.rocketmq; - -import com.iailab.framework.tenant.core.context.TenantContextHolder; -import org.apache.rocketmq.client.hook.SendMessageContext; -import org.apache.rocketmq.client.hook.SendMessageHook; - -import static com.iailab.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; - -/** - * RocketMQ 消息队列的多租户 {@link SendMessageHook} 实现类 - * - * Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 - * - * @author iailab - */ -public class TenantRocketMQSendMessageHook implements SendMessageHook { - - @Override - public String hookName() { - return getClass().getSimpleName(); - } - - @Override - public void sendMessageBefore(SendMessageContext sendMessageContext) { - Long tenantId = TenantContextHolder.getTenantId(); - if (tenantId == null) { - return; - } - sendMessageContext.getMessage().putUserProperty(HEADER_TENANT_ID, tenantId.toString()); - } - - @Override - public void sendMessageAfter(SendMessageContext sendMessageContext) { - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/redis/TenantRedisCacheManager.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/redis/TenantRedisCacheManager.java deleted file mode 100644 index 9394cf2..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/redis/TenantRedisCacheManager.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.iailab.framework.tenant.core.redis; - -import cn.hutool.core.collection.CollUtil; -import com.iailab.framework.redis.core.TimeoutRedisCacheManager; -import com.iailab.framework.tenant.core.context.TenantContextHolder; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.Cache; -import org.springframework.data.redis.cache.RedisCacheConfiguration; -import org.springframework.data.redis.cache.RedisCacheManager; -import org.springframework.data.redis.cache.RedisCacheWriter; - -import java.util.Set; - -/** - * 多租户的 {@link RedisCacheManager} 实现类 - * - * 操作指定 name 的 {@link Cache} 时,自动拼接租户后缀,格式为 name + ":" + tenantId + 后缀 - * - * @author airhead - */ -@Slf4j -public class TenantRedisCacheManager extends TimeoutRedisCacheManager { - - private final Set<String> ignoreCaches; - - public TenantRedisCacheManager(RedisCacheWriter cacheWriter, - RedisCacheConfiguration defaultCacheConfiguration, - Set<String> ignoreCaches) { - super(cacheWriter, defaultCacheConfiguration); - this.ignoreCaches = ignoreCaches; - } - - @Override - public Cache getCache(String name) { - // 如果开启多租户,则 name 拼接租户后缀 - if (!TenantContextHolder.isIgnore() - && TenantContextHolder.getTenantId() != null - && !CollUtil.contains(ignoreCaches, name)) { - name = name + ":" + TenantContextHolder.getTenantId(); - } - - // 继续基于父方法 - return super.getCache(name); - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/rpc/TenantRequestInterceptor.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/rpc/TenantRequestInterceptor.java deleted file mode 100644 index afb26f8..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/rpc/TenantRequestInterceptor.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.tenant.core.rpc; - -import com.iailab.framework.tenant.core.context.TenantContextHolder; -import com.iailab.framework.web.core.util.WebFrameworkUtils; -import feign.RequestInterceptor; -import feign.RequestTemplate; - -import static com.iailab.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; - -/** - * Tenant 的 RequestInterceptor 实现类:Feign 请求时,将 {@link TenantContextHolder} 设置到 header 中,继续透传给被调用的服务 - * - * @author iailab - */ -public class TenantRequestInterceptor implements RequestInterceptor { - - @Override - public void apply(RequestTemplate requestTemplate) { - Long tenantId = TenantContextHolder.getTenantId(); - if (tenantId != null) { - requestTemplate.header(HEADER_TENANT_ID, String.valueOf(tenantId)); - } - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/security/TenantSecurityWebFilter.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/security/TenantSecurityWebFilter.java deleted file mode 100644 index bab0e73..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/security/TenantSecurityWebFilter.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.iailab.framework.tenant.core.security; - -import cn.hutool.core.collection.CollUtil; -import com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants; -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.util.servlet.ServletUtils; -import com.iailab.framework.security.core.LoginUser; -import com.iailab.framework.security.core.util.SecurityFrameworkUtils; -import com.iailab.framework.tenant.config.TenantProperties; -import com.iailab.framework.tenant.core.context.TenantContextHolder; -import com.iailab.framework.tenant.core.service.TenantFrameworkService; -import com.iailab.framework.web.config.WebProperties; -import com.iailab.framework.web.core.filter.ApiRequestFilter; -import com.iailab.framework.web.core.handler.GlobalExceptionHandler; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.AntPathMatcher; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Objects; - -/** - * 多租户 Security Web 过滤器 - * 1. 如果是登陆的用户,校验是否有权限访问该租户,避免越权问题。 - * 2. 如果请求未带租户的编号,检查是否是忽略的 URL,否则也不允许访问。 - * 3. 校验租户是合法,例如说被禁用、到期 - * - * @author iailab - */ -@Slf4j -public class TenantSecurityWebFilter extends ApiRequestFilter { - - private final TenantProperties tenantProperties; - - private final AntPathMatcher pathMatcher; - - private final GlobalExceptionHandler globalExceptionHandler; - private final TenantFrameworkService tenantFrameworkService; - - public TenantSecurityWebFilter(TenantProperties tenantProperties, - WebProperties webProperties, - GlobalExceptionHandler globalExceptionHandler, - TenantFrameworkService tenantFrameworkService) { - super(webProperties); - this.tenantProperties = tenantProperties; - this.pathMatcher = new AntPathMatcher(); - this.globalExceptionHandler = globalExceptionHandler; - this.tenantFrameworkService = tenantFrameworkService; - } - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) - throws ServletException, IOException { - Long tenantId = TenantContextHolder.getTenantId(); - // 1. 登陆的用户,校验是否有权限访问该租户,避免越权问题。 - LoginUser user = SecurityFrameworkUtils.getLoginUser(); - if (user != null) { - // 如果获取不到租户编号,则尝试使用登陆用户的租户编号 - if (tenantId == null) { - tenantId = user.getTenantId(); - TenantContextHolder.setTenantId(tenantId); - // 如果传递了租户编号,则进行比对租户编号,避免越权问题 - } else if (!Objects.equals(user.getTenantId(), TenantContextHolder.getTenantId())) { - log.error("[doFilterInternal][租户({}) User({}/{}) 越权访问租户({}) URL({}/{})]", - user.getTenantId(), user.getId(), user.getUserType(), - TenantContextHolder.getTenantId(), request.getRequestURI(), request.getMethod()); - ServletUtils.writeJSON(response, CommonResult.error(GlobalErrorCodeConstants.FORBIDDEN.getCode(), - "您无权访问该租户的数据")); - return; - } - } - - // 如果非允许忽略租户的 URL,则校验租户是否合法 - if (!isIgnoreUrl(request)) { - // 2. 如果请求未带租户的编号,不允许访问。 - if (tenantId == null) { - log.error("[doFilterInternal][URL({}/{}) 未传递租户编号]", request.getRequestURI(), request.getMethod()); - ServletUtils.writeJSON(response, CommonResult.error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), - "请求的租户标识未传递,请进行排查")); - return; - } - // 3. 校验租户是合法,例如说被禁用、到期 - try { - tenantFrameworkService.validTenant(tenantId); - } catch (Throwable ex) { - CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex); - ServletUtils.writeJSON(response, result); - return; - } - } else { // 如果是允许忽略租户的 URL,若未传递租户编号,则默认忽略租户编号,避免报错 - if (tenantId == null) { - TenantContextHolder.setIgnore(true); - } - } - - // 继续过滤 - chain.doFilter(request, response); - } - - private boolean isIgnoreUrl(HttpServletRequest request) { - // 快速匹配,保证性能 - if (CollUtil.contains(tenantProperties.getIgnoreUrls(), request.getRequestURI())) { - return true; - } - // 逐个 Ant 路径匹配 - for (String url : tenantProperties.getIgnoreUrls()) { - if (pathMatcher.match(url, request.getRequestURI())) { - return true; - } - } - return false; - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/service/TenantFrameworkService.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/service/TenantFrameworkService.java deleted file mode 100644 index 92ed16a..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/service/TenantFrameworkService.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.iailab.framework.tenant.core.service; - -import com.baomidou.dynamic.datasource.creator.DataSourceProperty; - -import java.util.List; - -/** - * Tenant 框架 Service 接口,定义获取租户信息 - * - * @author iailab - */ -public interface TenantFrameworkService { - - /** - * 获得所有租户 - * - * @return 租户编号数组 - */ - List<Long> getTenantIds(); - - /** - * 校验租户是否合法 - * - * @param id 租户编号 - */ - void validTenant(Long id); - - /** - * 获得租户对应的数据源配置 - * - * @param id 租户编号 - * @return 数据源配置 - */ - DataSourceProperty getDataSourceProperty(Long id); - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/service/TenantFrameworkServiceImpl.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/service/TenantFrameworkServiceImpl.java deleted file mode 100644 index eef1cf4..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/service/TenantFrameworkServiceImpl.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.iailab.framework.tenant.core.service; - -import com.baomidou.dynamic.datasource.creator.DataSourceProperty; -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.util.cache.CacheUtils; -import com.iailab.module.system.api.tenant.TenantApi; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; - -import com.iailab.module.system.api.tenant.dto.TenantDataSourceConfigRespDTO; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; - -import java.time.Duration; -import java.util.List; - -import static com.iailab.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache; - -/** - * Tenant 框架 Service 实现类 - * - * @author iailab - */ -@RequiredArgsConstructor -public class TenantFrameworkServiceImpl implements TenantFrameworkService { - - private final TenantApi tenantApi; - - /** - * 针对 {@link #getTenantIds()} 的缓存 - */ - private final LoadingCache<Object, List<Long>> getTenantIdsCache = buildAsyncReloadingCache( - Duration.ofMinutes(1L), // 过期时间 1 分钟 - new CacheLoader<Object, List<Long>>() { - - @Override - public List<Long> load(Object key) { - return tenantApi.getTenantIdList().getCheckedData(); - } - - }); - - /** - * 针对 {@link #validTenant(Long)} 的缓存 - */ - private final LoadingCache<Long, CommonResult<Boolean>> validTenantCache = buildAsyncReloadingCache( - Duration.ofMinutes(1L), // 过期时间 1 分钟 - new CacheLoader<Long, CommonResult<Boolean>>() { - - @Override - public CommonResult<Boolean> load(Long id) { - return tenantApi.validTenant(id); - } - - }); - - /** - * 针对 {@link #getDataSourceProperty(Long)} 的缓存 - */ - private final LoadingCache<Long, DataSourceProperty> dataSourcePropertyCache = CacheUtils.buildAsyncReloadingCache( - Duration.ofMinutes(1L), // 过期时间 1 分钟 - new CacheLoader<Long, DataSourceProperty>() { - - @Override - public DataSourceProperty load(Long id) { - // 获得租户对应的数据源配置 - TenantDataSourceConfigRespDTO dataSourceConfig = tenantApi.getTenantDataSourceConfig(id); - if (dataSourceConfig == null) { - return null; - } - // 转换成 dynamic-datasource 配置 -// return new DataSourceProperty() -// .setPoolName(dataSourceConfig.getName()).setUrl(dataSourceConfig.getUrl()) -// .setUsername(dataSourceConfig.getUsername()).setPassword(dataSourceConfig.getPassword()); - - DataSourceProperty ds = new DataSourceProperty(); - ds.setPoolName(dataSourceConfig.getName()); - ds.setUrl(dataSourceConfig.getUrl()); - ds.setUsername(dataSourceConfig.getUsername()); - ds.setPassword(dataSourceConfig.getPassword()); - return ds; - } - - }); - - @Override - @SneakyThrows - public List<Long> getTenantIds() { - return getTenantIdsCache.get(Boolean.TRUE); - } - - @Override - @SneakyThrows - public void validTenant(Long id) { - validTenantCache.get(id).checkError(); - } - - @Override - @SneakyThrows - public DataSourceProperty getDataSourceProperty(Long id) { - return dataSourcePropertyCache.get(id); - } -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/util/TenantUtils.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/util/TenantUtils.java deleted file mode 100644 index 6f830cd..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/util/TenantUtils.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.iailab.framework.tenant.core.util; - -import com.iailab.framework.tenant.core.context.TenantContextHolder; - -import java.util.Map; -import java.util.concurrent.Callable; - -import static com.iailab.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; - -/** - * 多租户 Util - * - * @author iailab - */ -public class TenantUtils { - - /** - * 使用指定租户,执行对应的逻辑 - * - * 注意,如果当前是忽略租户的情况下,会被强制设置成不忽略租户 - * 当然,执行完成后,还是会恢复回去 - * - * @param tenantId 租户编号 - * @param runnable 逻辑 - */ - public static void execute(Long tenantId, Runnable runnable) { - Long oldTenantId = TenantContextHolder.getTenantId(); - Boolean oldIgnore = TenantContextHolder.isIgnore(); - try { - TenantContextHolder.setTenantId(tenantId); - TenantContextHolder.setIgnore(false); - // 执行逻辑 - runnable.run(); - } finally { - TenantContextHolder.setTenantId(oldTenantId); - TenantContextHolder.setIgnore(oldIgnore); - } - } - - /** - * 使用指定租户,执行对应的逻辑 - * - * 注意,如果当前是忽略租户的情况下,会被强制设置成不忽略租户 - * 当然,执行完成后,还是会恢复回去 - * - * @param tenantId 租户编号 - * @param callable 逻辑 - */ - public static <V> V execute(Long tenantId, Callable<V> callable) { - Long oldTenantId = TenantContextHolder.getTenantId(); - Boolean oldIgnore = TenantContextHolder.isIgnore(); - try { - TenantContextHolder.setTenantId(tenantId); - TenantContextHolder.setIgnore(false); - // 执行逻辑 - return callable.call(); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - TenantContextHolder.setTenantId(oldTenantId); - TenantContextHolder.setIgnore(oldIgnore); - } - } - - /** - * 忽略租户,执行对应的逻辑 - * - * @param runnable 逻辑 - */ - public static void executeIgnore(Runnable runnable) { - Boolean oldIgnore = TenantContextHolder.isIgnore(); - try { - TenantContextHolder.setIgnore(true); - // 执行逻辑 - runnable.run(); - } finally { - TenantContextHolder.setIgnore(oldIgnore); - } - } - - /** - * 将多租户编号,添加到 header 中 - * - * @param headers HTTP 请求 headers - * @param tenantId 租户编号 - */ - public static void addTenantHeader(Map<String, String> headers, Long tenantId) { - if (tenantId != null) { - headers.put(HEADER_TENANT_ID, tenantId.toString()); - } - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/web/TenantContextWebFilter.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/web/TenantContextWebFilter.java deleted file mode 100644 index 4973691..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/web/TenantContextWebFilter.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.iailab.framework.tenant.core.web; - -import com.iailab.framework.tenant.core.context.TenantContextHolder; -import com.iailab.framework.web.core.util.WebFrameworkUtils; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * 多租户 Context Web 过滤器 - * 将请求 Header 中的 tenant-id 解析出来,添加到 {@link TenantContextHolder} 中,这样后续的 DB 等操作,可以获得到租户编号。 - * - * @author iailab - */ -public class TenantContextWebFilter extends OncePerRequestFilter { - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) - throws ServletException, IOException { - // 设置 - Long tenantId = WebFrameworkUtils.getTenantId(request); - if (tenantId != null) { - TenantContextHolder.setTenantId(tenantId); - } - try { - chain.doFilter(request, response); - } finally { - // 清理 - TenantContextHolder.clear(); - } - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/package-info.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/package-info.java deleted file mode 100644 index eaefcd7..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * 多租户,支持如下层面: - * 1. DB:基于 MyBatis Plus 多租户的功能实现。 - * 2. Redis:通过在 Redis Key 上拼接租户编号的方式,进行隔离。 - * 3. Web:请求 HTTP API 时,解析 Header 的 tenant-id 租户编号,添加到租户上下文。 - * 4. Security:校验当前登陆的用户,是否越权访问其它租户的数据。 - * 5. Job:在 JobHandler 执行任务时,会按照每个租户,都独立并行执行一次。 - * 6. MQ:在 Producer 发送消息时,Header 带上 tenant-id 租户编号;在 Consumer 消费消息时,将 Header 的 tenant-id 租户编号,添加到租户上下文。 - * 7. Async:异步需要保证 ThreadLocal 的传递性,通过使用阿里开源的 TransmittableThreadLocal 实现。相关的改造点,可见: - * 1)Spring Async: - * {@link com.iailab.framework.quartz.config.IailabAsyncAutoConfiguration#threadPoolTaskExecutorBeanPostProcessor()} - * 2)Spring Security: - * TransmittableThreadLocalSecurityContextHolderStrategy - * 和 IailabSecurityAutoConfiguration#securityContextHolderMethodInvokingFactoryBean() 方法 - * - */ -package com.iailab.framework.tenant; diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java deleted file mode 100644 index 6f282a2..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright 2002-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.messaging.handler.invocation; - -import com.iailab.framework.tenant.core.context.TenantContextHolder; -import com.iailab.framework.tenant.core.util.TenantUtils; -import org.springframework.core.DefaultParameterNameDiscoverer; -import org.springframework.core.MethodParameter; -import org.springframework.core.ParameterNameDiscoverer; -import org.springframework.core.ResolvableType; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.handler.HandlerMethod; -import org.springframework.util.ObjectUtils; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.Arrays; - -import static com.iailab.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; - -/** - * Extension of {@link HandlerMethod} that invokes the underlying method with - * argument values resolved from the current HTTP request through a list of - * {@link HandlerMethodArgumentResolver}. - * - * 针对 rabbitmq-spring 和 kafka-spring,不存在合适的拓展点,可以实现 Consumer 消费前,读取 Header 中的 tenant-id 设置到 {@link TenantContextHolder} 中 - * TODO iailab:持续跟进,看看有没新的拓展点 - * - * @author Rossen Stoyanchev - * @author Juergen Hoeller - * @since 4.0 - */ -public class InvocableHandlerMethod extends HandlerMethod { - - private static final Object[] EMPTY_ARGS = new Object[0]; - - private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite(); - - private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); - - /** - * Create an instance from a {@code HandlerMethod}. - */ - public InvocableHandlerMethod(HandlerMethod handlerMethod) { - super(handlerMethod); - } - - /** - * Create an instance from a bean instance and a method. - */ - public InvocableHandlerMethod(Object bean, Method method) { - super(bean, method); - } - - /** - * Construct a new handler method with the given bean instance, method name and parameters. - * @param bean the object bean - * @param methodName the method name - * @param parameterTypes the method parameter types - * @throws NoSuchMethodException when the method cannot be found - */ - public InvocableHandlerMethod(Object bean, String methodName, Class<?>... parameterTypes) - throws NoSuchMethodException { - - super(bean, methodName, parameterTypes); - } - - /** - * Set {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers} to use for resolving method argument values. - */ - public void setMessageMethodArgumentResolvers(HandlerMethodArgumentResolverComposite argumentResolvers) { - this.resolvers = argumentResolvers; - } - - /** - * Set the ParameterNameDiscoverer for resolving parameter names when needed - * (e.g. default request attribute name). - * <p>Default is a {@link DefaultParameterNameDiscoverer}. - */ - public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) { - this.parameterNameDiscoverer = parameterNameDiscoverer; - } - - /** - * Invoke the method after resolving its argument values in the context of the given message. - * <p>Argument values are commonly resolved through - * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}. - * The {@code providedArgs} parameter however may supply argument values to be used directly, - * i.e. without argument resolution. - * <p>Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the - * resolved arguments. - * @param message the current message being processed - * @param providedArgs "given" arguments matched by type, not resolved - * @return the raw value returned by the invoked method - * @throws Exception raised if no suitable argument resolver can be found, - * or if the method raised an exception - * @see #getMethodArgumentValues - * @see #doInvoke - */ - @Nullable - public Object invoke(Message<?> message, Object... providedArgs) throws Exception { - Object[] args = getMethodArgumentValues(message, providedArgs); - if (logger.isTraceEnabled()) { - logger.trace("Arguments: " + Arrays.toString(args)); - } - // 注意:如下是本类的改动点!!! - // 情况一:无租户编号的情况 - Long tenantId= parseTenantId(message); - if (tenantId == null) { - return doInvoke(args); - } - // 情况二:有租户的情况下 - return TenantUtils.execute(tenantId, () -> doInvoke(args)); - } - - private Long parseTenantId(Message<?> message) { - Object tenantId = message.getHeaders().get(HEADER_TENANT_ID); - if (tenantId == null) { - return null; - } - if (tenantId instanceof Long) { - return (Long) tenantId; - } - if (tenantId instanceof Number) { - return ((Number) tenantId).longValue(); - } - if (tenantId instanceof String) { - return Long.parseLong((String) tenantId); - } - if (tenantId instanceof byte[]) { - return Long.parseLong(new String((byte[]) tenantId)); - } - throw new IllegalArgumentException("未知的数据类型:" + tenantId); - } - - /** - * Get the method argument values for the current message, checking the provided - * argument values and falling back to the configured argument resolvers. - * <p>The resulting array will be passed into {@link #doInvoke}. - * @since 5.1.2 - */ - protected Object[] getMethodArgumentValues(Message<?> message, Object... providedArgs) throws Exception { - MethodParameter[] parameters = getMethodParameters(); - if (ObjectUtils.isEmpty(parameters)) { - return EMPTY_ARGS; - } - - Object[] args = new Object[parameters.length]; - for (int i = 0; i < parameters.length; i++) { - MethodParameter parameter = parameters[i]; - parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); - args[i] = findProvidedArgument(parameter, providedArgs); - if (args[i] != null) { - continue; - } - if (!this.resolvers.supportsParameter(parameter)) { - throw new MethodArgumentResolutionException( - message, parameter, formatArgumentError(parameter, "No suitable resolver")); - } - try { - args[i] = this.resolvers.resolveArgument(parameter, message); - } - catch (Exception ex) { - // Leave stack trace for later, exception may actually be resolved and handled... - if (logger.isDebugEnabled()) { - String exMsg = ex.getMessage(); - if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { - logger.debug(formatArgumentError(parameter, exMsg)); - } - } - throw ex; - } - } - return args; - } - - /** - * Invoke the handler method with the given argument values. - */ - @Nullable - protected Object doInvoke(Object... args) throws Exception { - try { - return getBridgedMethod().invoke(getBean(), args); - } - catch (IllegalArgumentException ex) { - assertTargetBean(getBridgedMethod(), getBean(), args); - String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument"); - throw new IllegalStateException(formatInvokeError(text, args), ex); - } - catch (InvocationTargetException ex) { - // Unwrap for HandlerExceptionResolvers ... - Throwable targetException = ex.getTargetException(); - if (targetException instanceof RuntimeException) { - throw (RuntimeException) targetException; - } - else if (targetException instanceof Error) { - throw (Error) targetException; - } - else if (targetException instanceof Exception) { - throw (Exception) targetException; - } - else { - throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException); - } - } - } - - MethodParameter getAsyncReturnValueType(@Nullable Object returnValue) { - return new AsyncResultMethodParameter(returnValue); - } - - private class AsyncResultMethodParameter extends HandlerMethodParameter { - - @Nullable - private final Object returnValue; - - private final ResolvableType returnType; - - public AsyncResultMethodParameter(@Nullable Object returnValue) { - super(-1); - this.returnValue = returnValue; - this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric(); - } - - protected AsyncResultMethodParameter(AsyncResultMethodParameter original) { - super(original); - this.returnValue = original.returnValue; - this.returnType = original.returnType; - } - - @Override - public Class<?> getParameterType() { - if (this.returnValue != null) { - return this.returnValue.getClass(); - } - if (!ResolvableType.NONE.equals(this.returnType)) { - return this.returnType.toClass(); - } - return super.getParameterType(); - } - - @Override - public Type getGenericParameterType() { - return this.returnType.getType(); - } - - @Override - public AsyncResultMethodParameter clone() { - return new AsyncResultMethodParameter(this); - } - } - -} diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/resources/META-INF/spring.factories b/iailab-framework/iailab-common-biz-tenant/src/main/resources/META-INF/spring.factories deleted file mode 100644 index c03718f..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.boot.env.EnvironmentPostProcessor=\ - com.iailab.framework.tenant.core.mq.kafka.TenantKafkaEnvironmentPostProcessor diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/iailab-framework/iailab-common-biz-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index b954183..0000000 --- a/iailab-framework/iailab-common-biz-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,2 +0,0 @@ -com.iailab.framework.tenant.config.IailabTenantRpcAutoConfiguration -com.iailab.framework.tenant.config.IailabTenantAutoConfiguration diff --git a/iailab-framework/iailab-common-env/pom.xml b/iailab-framework/iailab-common-env/pom.xml deleted file mode 100644 index 2170152..0000000 --- a/iailab-framework/iailab-common-env/pom.xml +++ /dev/null @@ -1,66 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>com.iailab</groupId> - <artifactId>iailab-framework</artifactId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-env</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description> - 开发环境拓展,实现类似阿里的特性环境的能力 - 1. https://segmentfault.com/a/1190000018022987 - </description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <properties> - <maven.compiler.source>8</maven.compiler.source> - <maven.compiler.target>8</maven.compiler.target> - </properties> - - <dependencies> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common</artifactId> - </dependency> - - <!-- Spring 核心 --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter</artifactId> - </dependency> - - <!-- Web 相关 --> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-web</artifactId> - </dependency> - - <dependency> - <groupId>jakarta.servlet</groupId> - <artifactId>jakarta.servlet-api</artifactId> - </dependency> - - <!-- RPC 相关 --> - <dependency> - <groupId>org.springframework.cloud</groupId> - <artifactId>spring-cloud-loadbalancer</artifactId> - </dependency> - <dependency> - <groupId>io.github.openfeign</groupId> - <artifactId>feign-core</artifactId> - </dependency> - - <!-- Registry 注册中心相关 --> - <dependency> - <groupId>com.alibaba.cloud</groupId> - <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> - </dependency> - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/config/EnvEnvironmentPostProcessor.java b/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/config/EnvEnvironmentPostProcessor.java deleted file mode 100644 index ace59cc..0000000 --- a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/config/EnvEnvironmentPostProcessor.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.iailab.framework.env.config; - -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.util.collection.SetUtils; -import com.iailab.framework.env.core.util.EnvUtils; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.env.EnvironmentPostProcessor; -import org.springframework.core.env.ConfigurableEnvironment; - -import java.util.Set; - -import static com.iailab.framework.env.core.util.EnvUtils.HOST_NAME_VALUE; - -/** - * 多环境的 {@link EnvEnvironmentPostProcessor} 实现类 - * 将 iailab.env.tag 设置到 nacos 等组件对应的 tag 配置项,当且仅当它们不存在时 - * - * @author iailab - */ -public class EnvEnvironmentPostProcessor implements EnvironmentPostProcessor { - - private static final Set<String> TARGET_TAG_KEYS = SetUtils.asSet( - "spring.cloud.nacos.discovery.metadata.tag" // Nacos 注册中心 - // MQ TODO - ); - - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { - // 0. 设置 ${HOST_NAME} 兜底的环境变量 - String hostNameKey = StrUtil.subBetween(HOST_NAME_VALUE, "{", "}"); - if (!environment.containsProperty(hostNameKey)) { - environment.getSystemProperties().put(hostNameKey, EnvUtils.getHostName()); - } - - // 1.1 如果没有 iailab.env.tag 配置项,则不进行配置项的修改 - String tag = EnvUtils.getTag(environment); - if (StrUtil.isEmpty(tag)) { - return; - } - // 1.2 需要修改的配置项 - for (String targetTagKey : TARGET_TAG_KEYS) { - String targetTagValue = environment.getProperty(targetTagKey); - if (StrUtil.isNotEmpty(targetTagValue)) { - continue; - } - environment.getSystemProperties().put(targetTagKey, tag); - } - } - -} diff --git a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/config/EnvProperties.java b/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/config/EnvProperties.java deleted file mode 100644 index b17699f..0000000 --- a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/config/EnvProperties.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iailab.framework.env.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * 环境配置 - * - * @author iailab - */ -@ConfigurationProperties(prefix = "iailab.env") -@Data -public class EnvProperties { - - public static final String TAG_KEY = "iailab.env.tag"; - - /** - * 环境标签 - */ - private String tag; - -} diff --git a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/config/IailabEnvRpcAutoConfiguration.java b/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/config/IailabEnvRpcAutoConfiguration.java deleted file mode 100644 index ca16975..0000000 --- a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/config/IailabEnvRpcAutoConfiguration.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.iailab.framework.env.config; - -import com.iailab.framework.env.core.fegin.EnvLoadBalancerClientFactory; -import com.iailab.framework.env.core.fegin.EnvRequestInterceptor; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.client.loadbalancer.LoadBalancerClientsProperties; -import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientSpecification; -import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration; -import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; -import org.springframework.context.annotation.Bean; - -import java.util.Collections; -import java.util.List; - -/** - * 多环境的 RPC 组件的自动配置 - * - * @author iailab - */ -@AutoConfiguration -@EnableConfigurationProperties(EnvProperties.class) -public class IailabEnvRpcAutoConfiguration { - - // ========== Feign 相关 ========== - - /** - * 创建 {@link EnvLoadBalancerClientFactory} Bean - * - * 参考 {@link LoadBalancerAutoConfiguration#loadBalancerClientFactory(LoadBalancerClientsProperties)} 方法 - */ - @Bean - public LoadBalancerClientFactory loadBalancerClientFactory(LoadBalancerClientsProperties properties, - ObjectProvider<List<LoadBalancerClientSpecification>> configurations) { - EnvLoadBalancerClientFactory clientFactory = new EnvLoadBalancerClientFactory(properties); - clientFactory.setConfigurations(configurations.getIfAvailable(Collections::emptyList)); - return clientFactory; - } - - @Bean - public EnvRequestInterceptor envRequestInterceptor() { - return new EnvRequestInterceptor(); - } - -} diff --git a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/config/IailabEnvWebAutoConfiguration.java b/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/config/IailabEnvWebAutoConfiguration.java deleted file mode 100644 index 10b14f3..0000000 --- a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/config/IailabEnvWebAutoConfiguration.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.iailab.framework.env.config; - -import com.iailab.framework.common.enums.WebFilterOrderEnum; -import com.iailab.framework.env.core.web.EnvWebFilter; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; - -/** - * 多环境的 Web 组件的自动配置 - * - * @author iailab - */ -@AutoConfiguration -@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) -@EnableConfigurationProperties(EnvProperties.class) -public class IailabEnvWebAutoConfiguration { - - /** - * 创建 {@link EnvWebFilter} Bean - */ - @Bean - public FilterRegistrationBean<EnvWebFilter> envWebFilterFilter() { - EnvWebFilter filter = new EnvWebFilter(); - FilterRegistrationBean<EnvWebFilter> bean = new FilterRegistrationBean<>(filter); - bean.setOrder(WebFilterOrderEnum.ENV_TAG_FILTER); - return bean; - } - -} diff --git a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/context/EnvContextHolder.java b/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/context/EnvContextHolder.java deleted file mode 100644 index 551db9b..0000000 --- a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/context/EnvContextHolder.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.iailab.framework.env.core.context; - -import cn.hutool.core.collection.CollUtil; -import com.alibaba.ttl.TransmittableThreadLocal; - -import java.util.ArrayList; -import java.util.List; - -/** - * 开发环境上下文 - * - * @author iailab - */ -public class EnvContextHolder { - - /** - * 标签的上下文 - * - * 使用 {@link List} 的原因,可能存在多层设置或者清理 - */ - private static final ThreadLocal<List<String>> TAG_CONTEXT = TransmittableThreadLocal.withInitial(ArrayList::new); - - public static void setTag(String tag) { - TAG_CONTEXT.get().add(tag); - } - - public static String getTag() { - return CollUtil.getLast(TAG_CONTEXT.get()); - } - - public static void removeTag() { - List<String> tags = TAG_CONTEXT.get(); - if (CollUtil.isEmpty(tags)) { - return; - } - tags.remove(tags.size() - 1); - } - -} diff --git a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/fegin/EnvLoadBalancerClient.java b/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/fegin/EnvLoadBalancerClient.java deleted file mode 100644 index fb43806..0000000 --- a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/fegin/EnvLoadBalancerClient.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.iailab.framework.env.core.fegin; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.util.collection.CollectionUtils; -import com.iailab.framework.env.core.context.EnvContextHolder; -import com.iailab.framework.env.core.util.EnvUtils; -import com.alibaba.cloud.nacos.balancer.NacosBalancer; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.loadbalancer.DefaultResponse; -import org.springframework.cloud.client.loadbalancer.EmptyResponse; -import org.springframework.cloud.client.loadbalancer.Request; -import org.springframework.cloud.client.loadbalancer.Response; -import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; -import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier; -import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; -import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; -import reactor.core.publisher.Mono; - -import java.util.List; - -/** - * 多环境的 {@link org.springframework.cloud.client.loadbalancer.LoadBalancerClient} 实现类 - * 在从服务实例列表选择时,优先选择 tag 匹配的服务实例 - * - * @author iailab - */ -@RequiredArgsConstructor -@Slf4j -public class EnvLoadBalancerClient implements ReactorServiceInstanceLoadBalancer { - - /** - * 用于获取 serviceId 对应的服务实例的列表 - */ - private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider; - /** - * 需要获取的服务实例名 - * - * 暂时用于打印 logger 日志 - */ - private final String serviceId; - /** - * 被代理的 ReactiveLoadBalancer 对象 - */ - private final ReactiveLoadBalancer<ServiceInstance> reactiveLoadBalancer; - - @Override - public Mono<Response<ServiceInstance>> choose(Request request) { - // 情况一,没有 tag 时,使用默认的 reactiveLoadBalancer 实现负载均衡 - String tag = EnvContextHolder.getTag(); - if (StrUtil.isEmpty(tag)) { - return Mono.from(reactiveLoadBalancer.choose(request)); - } - - // 情况二,有 tag 时,使用 tag 匹配服务实例 - ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new); - return supplier.get(request).next().map(list -> getInstanceResponse(list, tag)); - } - - private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, String tag) { - // 如果服务实例为空,则直接返回 - if (CollUtil.isEmpty(instances)) { - log.warn("[getInstanceResponse][serviceId({}) 服务实例列表为空]", serviceId); - return new EmptyResponse(); - } - - // 筛选满足条件的实例列表 - List<ServiceInstance> chooseInstances = CollectionUtils.filterList(instances, instance -> tag.equals(EnvUtils.getTag(instance))); - if (CollUtil.isEmpty(chooseInstances)) { - log.warn("[getInstanceResponse][serviceId({}) 没有满足 tag({}) 的服务实例列表,直接使用所有服务实例列表]", serviceId, tag); - chooseInstances = instances; - } - - // TODO iailab:https://juejin.cn/post/7056770721858469896 想通网段 - - // 随机 + 权重获取实例列表 TODO iailab:目前直接使用 Nacos 提供的方法,如果替换注册中心,需要重新失败该方法 - return new DefaultResponse(NacosBalancer.getHostByRandomWeight3(chooseInstances)); - } - -} diff --git a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/fegin/EnvLoadBalancerClientFactory.java b/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/fegin/EnvLoadBalancerClientFactory.java deleted file mode 100644 index ba728f5..0000000 --- a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/fegin/EnvLoadBalancerClientFactory.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.iailab.framework.env.core.fegin; - - -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.loadbalancer.LoadBalancerClientsProperties; -import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; -import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; -import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; - -/** - * 多环境的 {@link LoadBalancerClientFactory} 实现类 - * 目的:在创建 {@link ReactiveLoadBalancer} 时,会额外增加 {@link EnvLoadBalancerClient} 代理,用于 tag 过滤服务实例 - * - * @author iailab - */ -public class EnvLoadBalancerClientFactory extends LoadBalancerClientFactory { - - public EnvLoadBalancerClientFactory(LoadBalancerClientsProperties properties) { - super(properties); - } - - @Override - public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) { - ReactiveLoadBalancer<ServiceInstance> reactiveLoadBalancer = super.getInstance(serviceId); - // 参考 {@link com.alibaba.cloud.nacos.loadbalancer.NacosLoadBalancerClientConfiguration#nacosLoadBalancer(Environment, LoadBalancerClientFactory, NacosDiscoveryProperties)} 方法 - return new EnvLoadBalancerClient(super.getLazyProvider(serviceId, ServiceInstanceListSupplier.class), - serviceId, reactiveLoadBalancer); - } - -} diff --git a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/fegin/EnvRequestInterceptor.java b/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/fegin/EnvRequestInterceptor.java deleted file mode 100644 index 6242683..0000000 --- a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/fegin/EnvRequestInterceptor.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.iailab.framework.env.core.fegin; - -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.env.core.context.EnvContextHolder; -import com.iailab.framework.env.core.util.EnvUtils; -import feign.RequestInterceptor; -import feign.RequestTemplate; - -/** - * 多环境的 {@link RequestInterceptor} 实现类:Feign 请求时,将 tag 设置到 header 中,继续透传给被调用的服务 - * - * @author iailab - */ -public class EnvRequestInterceptor implements RequestInterceptor { - - @Override - public void apply(RequestTemplate requestTemplate) { - String tag = EnvContextHolder.getTag(); - if (StrUtil.isNotEmpty(tag)) { - EnvUtils.setTag(requestTemplate, tag); - } - } - -} diff --git a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/package-info.java b/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/package-info.java deleted file mode 100644 index 304a953..0000000 --- a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.iailab.framework.env.core; diff --git a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/util/EnvUtils.java b/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/util/EnvUtils.java deleted file mode 100644 index c010e2e..0000000 --- a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/util/EnvUtils.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.iailab.framework.env.core.util; - -import com.iailab.framework.env.config.EnvProperties; -import feign.RequestTemplate; -import lombok.SneakyThrows; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.core.env.Environment; - -import javax.servlet.http.HttpServletRequest; -import java.net.InetAddress; -import java.util.Objects; - -/** - * 环境 Utils - * - * @author iailab - */ -public class EnvUtils { - - private static final String HEADER_TAG = "tag"; - - public static final String HOST_NAME_VALUE = "${HOSTNAME}"; - - public static String getTag(HttpServletRequest request) { - String tag = request.getHeader(HEADER_TAG); - // 如果请求的是 "${HOSTNAME}",则解析成对应的本地主机名 - // 目的:特殊逻辑,解决 IDEA Rest Client 不支持环境变量的读取,所以就服务器来做 - return Objects.equals(tag, HOST_NAME_VALUE) ? getHostName() : tag; - } - - public static String getTag(ServiceInstance instance) { - return instance.getMetadata().get(HEADER_TAG); - } - - public static String getTag(Environment environment) { - String tag = environment.getProperty(EnvProperties.TAG_KEY); - // 如果请求的是 "${HOSTNAME}",则解析成对应的本地主机名 - // 目的:特殊逻辑,解决 IDEA Rest Client 不支持环境变量的读取,所以就服务器来做 - return Objects.equals(tag, HOST_NAME_VALUE) ? getHostName() : tag; - } - - public static void setTag(RequestTemplate requestTemplate, String tag) { - requestTemplate.header(HEADER_TAG, tag); - } - - /** - * 获得 hostname 主机名 - * - * @return 主机名 - */ - @SneakyThrows - public static String getHostName() { - return InetAddress.getLocalHost().getHostName(); - } - -} diff --git a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/web/EnvWebFilter.java b/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/web/EnvWebFilter.java deleted file mode 100644 index 9d1912f..0000000 --- a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/core/web/EnvWebFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.iailab.framework.env.core.web; - -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.env.core.context.EnvContextHolder; -import com.iailab.framework.env.core.util.EnvUtils; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * 环境的 {@link javax.servlet.Filter} 实现类 - * 当有 tag 请求头时,设置到 {@link EnvContextHolder} 的标签上下文 - * - * @author iailab - */ -public class EnvWebFilter extends OncePerRequestFilter { - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) - throws ServletException, IOException { - // 如果没有 tag,则走默认的流程 - String tag = EnvUtils.getTag(request); - if (StrUtil.isEmpty(tag)) { - chain.doFilter(request, response); - return; - } - - // 如果有 tag,则设置到上下文 - EnvContextHolder.setTag(tag); - try { - chain.doFilter(request, response); - } finally { - EnvContextHolder.removeTag(); - } - } - -} diff --git a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/package-info.java b/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/package-info.java deleted file mode 100644 index cf90d7c..0000000 --- a/iailab-framework/iailab-common-env/src/main/java/com/iailab/framework/env/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * 开发环境拓展,实现类似阿里的特性环境的能力 - * 1. https://segmentfault.com/a/1190000018022987 - * - * @author iailab - */ -package com.iailab.framework.env; diff --git a/iailab-framework/iailab-common-env/src/main/resources/META-INF/spring.factories b/iailab-framework/iailab-common-env/src/main/resources/META-INF/spring.factories deleted file mode 100644 index e7a9ffe..0000000 --- a/iailab-framework/iailab-common-env/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.boot.env.EnvironmentPostProcessor=\ - com.iailab.framework.env.config.EnvEnvironmentPostProcessor diff --git a/iailab-framework/iailab-common-env/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/iailab-framework/iailab-common-env/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index f727275..0000000 --- a/iailab-framework/iailab-common-env/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,2 +0,0 @@ -com.iailab.framework.env.config.IailabEnvWebAutoConfiguration -com.iailab.framework.env.config.IailabEnvRpcAutoConfiguration diff --git a/iailab-framework/iailab-common-excel/pom.xml b/iailab-framework/iailab-common-excel/pom.xml deleted file mode 100644 index a069490..0000000 --- a/iailab-framework/iailab-common-excel/pom.xml +++ /dev/null @@ -1,82 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>com.iailab</groupId> - <artifactId>iailab-framework</artifactId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-excel</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description>Excel 拓展</description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common</artifactId> - </dependency> - - <!-- Spring 核心 --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter</artifactId> - </dependency> - - <!-- RPC 远程调用相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-rpc</artifactId> - <optional>true</optional> - </dependency> - - <!-- 业务组件 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-module-system-api</artifactId> <!-- 需要使用它,进行 Dict 的查询 --> - <version>${revision}</version> - </dependency> - - <!-- Web 相关 --> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-web</artifactId> - <scope>provided</scope> <!-- 设置为 provided,只有 ExcelUtils 使用 --> - </dependency> - - <dependency> - <groupId>jakarta.servlet</groupId> - <artifactId>jakarta.servlet-api</artifactId> - <scope>provided</scope> <!-- 设置为 provided,只有 ExcelUtils 使用 --> - </dependency> - - <!-- 工具类相关 --> - <dependency> - <groupId>com.alibaba</groupId> - <artifactId>easyexcel</artifactId> - </dependency> - - <dependency> - <groupId>com.google.guava</groupId> - <artifactId>guava</artifactId> - </dependency> - - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-biz-ip</artifactId> - <optional>true</optional> <!-- 设置为 optional,只有在 AreaConvert 的时候使用 --> - </dependency> - - <!-- Test 测试相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-test</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/dict/config/IailabDictAutoConfiguration.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/dict/config/IailabDictAutoConfiguration.java deleted file mode 100644 index 9f1ed42..0000000 --- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/dict/config/IailabDictAutoConfiguration.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.iailab.framework.dict.config; - -import com.iailab.framework.dict.core.DictFrameworkUtils; -import com.iailab.module.system.api.dict.DictDataApi; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Bean; - -@AutoConfiguration -public class IailabDictAutoConfiguration { - - @Bean - @SuppressWarnings("InstantiationOfUtilityClass") - public DictFrameworkUtils dictUtils(DictDataApi dictDataApi) { - DictFrameworkUtils.init(dictDataApi); - return new DictFrameworkUtils(); - } - -} diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/dict/config/IailabDictRpcAutoConfiguration.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/dict/config/IailabDictRpcAutoConfiguration.java deleted file mode 100644 index aff4b46..0000000 --- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/dict/config/IailabDictRpcAutoConfiguration.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.iailab.framework.dict.config; - -import com.iailab.module.system.api.dict.DictDataApi; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.cloud.openfeign.EnableFeignClients; - -/** - * 字典用到 Feign 的配置项 - * - * @author iailab - */ -@AutoConfiguration -@EnableFeignClients(clients = DictDataApi.class) // 主要是引入相关的 API 服务 -public class IailabDictRpcAutoConfiguration { -} diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/dict/core/DictFrameworkUtils.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/dict/core/DictFrameworkUtils.java deleted file mode 100644 index 307d8b6..0000000 --- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/dict/core/DictFrameworkUtils.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.iailab.framework.dict.core; - -import cn.hutool.core.util.ObjectUtil; -import com.iailab.framework.common.core.KeyValue; -import com.iailab.framework.common.util.cache.CacheUtils; -import com.iailab.module.system.api.dict.DictDataApi; -import com.iailab.module.system.api.dict.dto.DictDataRespDTO; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; - -import java.time.Duration; -import java.util.List; - -/** - * 字典工具类 - * - * @author iailab - */ -@Slf4j -public class DictFrameworkUtils { - - private static DictDataApi dictDataApi; - - private static final DictDataRespDTO DICT_DATA_NULL = new DictDataRespDTO(); - - // TODO @puhui999:GET_DICT_DATA_CACHE、GET_DICT_DATA_LIST_CACHE、PARSE_DICT_DATA_CACHE 这 3 个缓存是有点重叠,可以思考下,有没可能减少 1 个。微信讨论好私聊,再具体改哈 - /** - * 针对 {@link #getDictDataLabel(String, String)} 的缓存 - */ - private static final LoadingCache<KeyValue<String, String>, DictDataRespDTO> GET_DICT_DATA_CACHE = CacheUtils.buildAsyncReloadingCache( - Duration.ofMinutes(1L), // 过期时间 1 分钟 - new CacheLoader<KeyValue<String, String>, DictDataRespDTO>() { - - @Override - public DictDataRespDTO load(KeyValue<String, String> key) { - return ObjectUtil.defaultIfNull(dictDataApi.getDictData(key.getKey(), key.getValue()).getCheckedData(), DICT_DATA_NULL); - } - - }); - - /** - * 针对 {@link #getDictDataLabelList(String)} 的缓存 - */ - private static final LoadingCache<String, List<String>> GET_DICT_DATA_LIST_CACHE = CacheUtils.buildAsyncReloadingCache( - Duration.ofMinutes(1L), // 过期时间 1 分钟 - new CacheLoader<String, List<String>>() { - - @Override - public List<String> load(String dictType) { - return dictDataApi.getDictDataLabelList(dictType); - } - - }); - - /** - * 针对 {@link #parseDictDataValue(String, String)} 的缓存 - */ - private static final LoadingCache<KeyValue<String, String>, DictDataRespDTO> PARSE_DICT_DATA_CACHE = CacheUtils.buildAsyncReloadingCache( - Duration.ofMinutes(1L), // 过期时间 1 分钟 - new CacheLoader<KeyValue<String, String>, DictDataRespDTO>() { - - @Override - public DictDataRespDTO load(KeyValue<String, String> key) { - return ObjectUtil.defaultIfNull(dictDataApi.parseDictData(key.getKey(), key.getValue()).getCheckedData(), DICT_DATA_NULL); - } - - }); - - public static void init(DictDataApi dictDataApi) { - DictFrameworkUtils.dictDataApi = dictDataApi; - log.info("[init][初始化 DictFrameworkUtils 成功]"); - } - - @SneakyThrows - public static String getDictDataLabel(String dictType, Integer value) { - return GET_DICT_DATA_CACHE.get(new KeyValue<>(dictType, String.valueOf(value))).getLabel(); - } - - @SneakyThrows - public static String getDictDataLabel(String dictType, String value) { - return GET_DICT_DATA_CACHE.get(new KeyValue<>(dictType, value)).getLabel(); - } - - @SneakyThrows - public static List<String> getDictDataLabelList(String dictType) { - return GET_DICT_DATA_LIST_CACHE.get(dictType); - } - - @SneakyThrows - public static String parseDictDataValue(String dictType, String label) { - return PARSE_DICT_DATA_CACHE.get(new KeyValue<>(dictType, label)).getValue(); - } - -} diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/dict/package-info.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/dict/package-info.java deleted file mode 100644 index b7a0386..0000000 --- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/dict/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 字典数据模块,提供 {@link com.iailab.framework.dict.core.DictFrameworkUtils} 工具类 - * - * 通过将字典缓存在内存中,保证性能 - */ -package com.iailab.framework.dict; diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/annotations/DictFormat.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/annotations/DictFormat.java deleted file mode 100644 index 9cd3996..0000000 --- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/annotations/DictFormat.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iailab.framework.excel.core.annotations; - -import java.lang.annotation.*; - -/** - * 字典格式化 - * - * 实现将字典数据的值,格式化成字典数据的标签 - */ -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@Inherited -public @interface DictFormat { - - /** - * 例如说,SysDictTypeConstants、InfDictTypeConstants - * - * @return 字典类型 - */ - String value(); - -} diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/annotations/ExcelColumnSelect.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/annotations/ExcelColumnSelect.java deleted file mode 100644 index 7956094..0000000 --- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/annotations/ExcelColumnSelect.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.iailab.framework.excel.core.annotations; - -import java.lang.annotation.*; - -/** - * 给 Excel 列添加下拉选择数据 - * - * 其中 {@link #dictType()} 和 {@link #functionName()} 二选一 - * - * @author HUIHUI - */ -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@Inherited -public @interface ExcelColumnSelect { - - /** - * @return 字典类型 - */ - String dictType() default ""; - - /** - * @return 获取下拉数据源的方法名称 - */ - String functionName() default ""; - -} diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/AreaConvert.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/AreaConvert.java deleted file mode 100644 index 558c5f7..0000000 --- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/AreaConvert.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.iailab.framework.excel.core.convert; - -import cn.hutool.core.convert.Convert; -import com.iailab.framework.ip.core.Area; -import com.iailab.framework.ip.core.utils.AreaUtils; -import com.alibaba.excel.converters.Converter; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.GlobalConfiguration; -import com.alibaba.excel.metadata.data.ReadCellData; -import com.alibaba.excel.metadata.property.ExcelContentProperty; -import lombok.extern.slf4j.Slf4j; - -/** - * Excel 数据地区转换器 - * - * @author HUIHUI - */ -@Slf4j -public class AreaConvert implements Converter<Object> { - - @Override - public Class<?> supportJavaTypeKey() { - throw new UnsupportedOperationException("暂不支持,也不需要"); - } - - @Override - public CellDataTypeEnum supportExcelTypeKey() { - throw new UnsupportedOperationException("暂不支持,也不需要"); - } - - @Override - public Object convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty, - GlobalConfiguration globalConfiguration) { - // 解析地区编号 - String label = readCellData.getStringValue(); - Area area = AreaUtils.parseArea(label); - if (area == null) { - log.error("[convertToJavaData][label({}) 解析不掉]", label); - return null; - } - // 将 value 转换成对应的属性 - Class<?> fieldClazz = contentProperty.getField().getType(); - return Convert.convert(fieldClazz, area.getId()); - } - -} diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/DictConvert.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/DictConvert.java deleted file mode 100644 index 9b667f3..0000000 --- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/DictConvert.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.iailab.framework.excel.core.convert; - -import cn.hutool.core.convert.Convert; -import com.iailab.framework.dict.core.DictFrameworkUtils; -import com.iailab.framework.excel.core.annotations.DictFormat; -import com.alibaba.excel.converters.Converter; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.GlobalConfiguration; -import com.alibaba.excel.metadata.data.ReadCellData; -import com.alibaba.excel.metadata.data.WriteCellData; -import com.alibaba.excel.metadata.property.ExcelContentProperty; -import lombok.extern.slf4j.Slf4j; - -/** - * Excel 数据字典转换器 - * - * @author iailab - */ -@Slf4j -public class DictConvert implements Converter<Object> { - - @Override - public Class<?> supportJavaTypeKey() { - throw new UnsupportedOperationException("暂不支持,也不需要"); - } - - @Override - public CellDataTypeEnum supportExcelTypeKey() { - throw new UnsupportedOperationException("暂不支持,也不需要"); - } - - @Override - public Object convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty, - GlobalConfiguration globalConfiguration) { - // 使用字典解析 - String type = getType(contentProperty); - String label = readCellData.getStringValue(); - String value = DictFrameworkUtils.parseDictDataValue(type, label); - if (value == null) { - log.error("[convertToJavaData][type({}) 解析不掉 label({})]", type, label); - return null; - } - // 将 String 的 value 转换成对应的属性 - Class<?> fieldClazz = contentProperty.getField().getType(); - return Convert.convert(fieldClazz, value); - } - - @Override - public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, - GlobalConfiguration globalConfiguration) { - // 空时,返回空 - if (object == null) { - return new WriteCellData<>(""); - } - - // 使用字典格式化 - String type = getType(contentProperty); - String value = String.valueOf(object); - String label = DictFrameworkUtils.getDictDataLabel(type, value); - if (label == null) { - log.error("[convertToExcelData][type({}) 转换不了 label({})]", type, value); - return new WriteCellData<>(""); - } - // 生成 Excel 小表格 - return new WriteCellData<>(label); - } - - private static String getType(ExcelContentProperty contentProperty) { - return contentProperty.getField().getAnnotation(DictFormat.class).value(); - } - -} diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/JsonConvert.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/JsonConvert.java deleted file mode 100644 index 5a29d08..0000000 --- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/JsonConvert.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.iailab.framework.excel.core.convert; - -import com.iailab.framework.common.util.json.JsonUtils; -import com.alibaba.excel.converters.Converter; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.GlobalConfiguration; -import com.alibaba.excel.metadata.data.WriteCellData; -import com.alibaba.excel.metadata.property.ExcelContentProperty; - -/** - * Excel Json 转换器 - * - * @author iailab - */ -public class JsonConvert implements Converter<Object> { - - @Override - public Class<?> supportJavaTypeKey() { - throw new UnsupportedOperationException("暂不支持,也不需要"); - } - - @Override - public CellDataTypeEnum supportExcelTypeKey() { - throw new UnsupportedOperationException("暂不支持,也不需要"); - } - - @Override - public WriteCellData<String> convertToExcelData(Object value, ExcelContentProperty contentProperty, - GlobalConfiguration globalConfiguration) { - // 生成 Excel 小表格 - return new WriteCellData<>(JsonUtils.toJsonString(value)); - } - -} diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/MoneyConvert.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/MoneyConvert.java deleted file mode 100644 index fce1e64..0000000 --- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/MoneyConvert.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.iailab.framework.excel.core.convert; - -import com.alibaba.excel.converters.Converter; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.GlobalConfiguration; -import com.alibaba.excel.metadata.data.WriteCellData; -import com.alibaba.excel.metadata.property.ExcelContentProperty; - -import java.math.BigDecimal; -import java.math.RoundingMode; - -/** - * 金额转换器 - * - * 金额单位:分 - * - * @author iailab - */ -public class MoneyConvert implements Converter<Integer> { - - @Override - public Class<?> supportJavaTypeKey() { - throw new UnsupportedOperationException("暂不支持,也不需要"); - } - - @Override - public CellDataTypeEnum supportExcelTypeKey() { - throw new UnsupportedOperationException("暂不支持,也不需要"); - } - - @Override - public WriteCellData<String> convertToExcelData(Integer value, ExcelContentProperty contentProperty, - GlobalConfiguration globalConfiguration) { - BigDecimal result = BigDecimal.valueOf(value) - .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP); - return new WriteCellData<>(result.toString()); - } - -} diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/function/ExcelColumnSelectFunction.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/function/ExcelColumnSelectFunction.java deleted file mode 100644 index a817d4b..0000000 --- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/function/ExcelColumnSelectFunction.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.framework.excel.core.function; - -import java.util.List; - -/** - * Excel 列下拉数据源获取接口 - * - * 为什么不直接解析字典还搞个接口?考虑到有的下拉数据不是从字典中获取的所有需要做一个兼容 - - * @author HUIHUI - */ -public interface ExcelColumnSelectFunction { - - /** - * 获得方法名称 - * - * @return 方法名称 - */ - String getName(); - - /** - * 获得列下拉数据源 - * - * @return 下拉数据源 - */ - List<String> getOptions(); - -} diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/handler/SelectSheetWriteHandler.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/handler/SelectSheetWriteHandler.java deleted file mode 100644 index 9e5e3df..0000000 --- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/handler/SelectSheetWriteHandler.java +++ /dev/null @@ -1,186 +0,0 @@ -package com.iailab.framework.excel.core.handler; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.extra.spring.SpringUtil; -import cn.hutool.poi.excel.ExcelUtil; -import com.alibaba.excel.annotation.ExcelProperty; -import com.alibaba.excel.write.handler.SheetWriteHandler; -import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; -import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; -import com.iailab.framework.common.core.KeyValue; -import com.iailab.framework.dict.core.DictFrameworkUtils; -import com.iailab.framework.excel.core.annotations.ExcelColumnSelect; -import com.iailab.framework.excel.core.function.ExcelColumnSelectFunction; -import lombok.extern.slf4j.Slf4j; -import org.apache.poi.hssf.usermodel.HSSFDataValidation; -import org.apache.poi.ss.usermodel.*; -import org.apache.poi.ss.util.CellRangeAddressList; - -import java.lang.reflect.Field; -import java.util.*; - -import static com.iailab.framework.common.util.collection.CollectionUtils.convertList; - -/** - * 基于固定 sheet 实现下拉框 - * - * @author HUIHUI - */ -@Slf4j -public class SelectSheetWriteHandler implements SheetWriteHandler { - - /** - * 数据起始行从 0 开始 - * 约定:本项目第一行有标题所以从 1 开始如果您的 Excel 有多行标题请自行更改 - */ - public static final int FIRST_ROW = 1; - /** - * 下拉列需要创建下拉框的行数,默认两千行如需更多请自行调整 - */ - public static final int LAST_ROW = 2000; - - private static final String DICT_SHEET_NAME = "字典sheet"; - - /** - * key: 列 value: 下拉数据源 - */ - private final Map<Integer, List<String>> selectMap = new HashMap<>(); - - private static Boolean ifSetSelect; - - public SelectSheetWriteHandler(Class<?> head, Boolean selectFlag) { - ifSetSelect = selectFlag; - // 加载下拉数据获取接口 - Map<String, ExcelColumnSelectFunction> beansMap = SpringUtil.getBeanFactory().getBeansOfType(ExcelColumnSelectFunction.class); - if (MapUtil.isEmpty(beansMap)) { - return; - } - List<Field> fields = new ArrayList<>(); - for (Class<?> c = head; c != null; c = c.getSuperclass()) { - Collections.addAll(fields, c.getDeclaredFields()); - } - // 解析下拉数据 - int colIndex = 0; - for (Field field : fields) { - if (field.isAnnotationPresent(ExcelColumnSelect.class)) { - ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); - if (excelProperty != null && excelProperty.index() != -1) { - getSelectDataList(excelProperty.index(), field); - }else{ - getSelectDataList(colIndex, field); - } - } - colIndex++; - } - } - - /** - * 获得下拉数据,并添加到 {@link #selectMap} 中 - * - * @param colIndex 列索引 - * @param field 字段 - */ - private void getSelectDataList(int colIndex, Field field) { - ExcelColumnSelect columnSelect = field.getAnnotation(ExcelColumnSelect.class); - String dictType = columnSelect.dictType(); - String functionName = columnSelect.functionName(); - Assert.isTrue(ObjectUtil.isNotEmpty(dictType) || ObjectUtil.isNotEmpty(functionName), - "Field({}) 的 @ExcelColumnSelect 注解,dictType 和 functionName 不能同时为空", field.getName()); - - // 情况一:使用 dictType 获得下拉数据 - if (StrUtil.isNotEmpty(dictType)) { // 情况一: 字典数据 (默认) - selectMap.put(colIndex, DictFrameworkUtils.getDictDataLabelList(dictType)); - return; - } - - // 情况二:使用 functionName 获得下拉数据 - Map<String, ExcelColumnSelectFunction> functionMap = SpringUtil.getApplicationContext().getBeansOfType(ExcelColumnSelectFunction.class); - ExcelColumnSelectFunction function = CollUtil.findOne(functionMap.values(), item -> item.getName().equals(functionName)); - Assert.notNull(function, "未找到对应的 function({})", functionName); - selectMap.put(colIndex, function.getOptions()); - } - - @Override - public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { - if (CollUtil.isEmpty(selectMap)) { - return; - } - // 1. 获取相应操作对象 - DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper(); // 需要设置下拉框的 sheet 页的数据验证助手 - Workbook workbook = writeWorkbookHolder.getWorkbook(); // 获得工作簿 - List<KeyValue<Integer, List<String>>> keyValues = convertList(selectMap.entrySet(), entry -> new KeyValue<>(entry.getKey(), entry.getValue())); - keyValues.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错 - if (ifSetSelect){ - for (KeyValue<Integer, List<String>> keyValue : keyValues) { - /*起始行、终止行、起始列、终止列 起始行为1即表示表头不设置**/ - CellRangeAddressList addressList = new CellRangeAddressList(FIRST_ROW, LAST_ROW, keyValue.getKey(), keyValue.getKey()); - /*设置下拉框数据**/ - DataValidationConstraint constraint = helper.createExplicitListConstraint(keyValue.getValue().toArray(new String[0])); - DataValidation dataValidation = helper.createValidation(constraint, addressList); - if (dataValidation instanceof HSSFDataValidation) { - dataValidation.setSuppressDropDownArrow(false); - } else { - dataValidation.setSuppressDropDownArrow(true); - dataValidation.setShowErrorBox(true); - } - // 2.2 阻止输入非下拉框的值 - dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP); - dataValidation.createErrorBox("提示", "此值不存在于下拉选择中!"); - writeSheetHolder.getSheet().addValidationData(dataValidation); - } - }else{ - // 2. 创建数据字典的 sheet 页 - Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME); - for (KeyValue<Integer, List<String>> keyValue : keyValues) { - int rowLength = keyValue.getValue().size(); - // 2.1 设置字典 sheet 页的值,每一列一部字典项 - for (int i = 0; i < rowLength; i++) { - Row row = dictSheet.getRow(i); - if (row == null) { - row = dictSheet.createRow(i); - } - row.createCell(keyValue.getKey()).setCellValue(keyValue.getValue().get(i)); - } - // 2.2 设置单元格下拉选择 - setColumnSelect(writeSheetHolder, workbook, helper, keyValue); - } - } - } - - /** - * 设置单元格下拉选择 - */ - private static void setColumnSelect(WriteSheetHolder writeSheetHolder, Workbook workbook, DataValidationHelper helper, - KeyValue<Integer, List<String>> keyValue) { - // 1.1 创建可被其他单元格引用的名称 - Name name = workbook.createName(); - String excelColumn = ExcelUtil.indexToColName(keyValue.getKey()); - // 1.2 下拉框数据来源 eg:字典sheet!$B1:$B2 - String refers = DICT_SHEET_NAME + "!$" + excelColumn + "$1:$" + excelColumn + "$" + keyValue.getValue().size(); - name.setNameName("dict" + keyValue.getKey()); // 设置名称的名字 - name.setRefersToFormula(refers); // 设置公式 - - // 2.1 设置约束 - DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + keyValue.getKey()); // 设置引用约束 - // 设置下拉单元格的首行、末行、首列、末列 - CellRangeAddressList rangeAddressList = new CellRangeAddressList(FIRST_ROW, LAST_ROW, - keyValue.getKey(), keyValue.getKey()); - DataValidation validation = helper.createValidation(constraint, rangeAddressList); - if (validation instanceof HSSFDataValidation) { - validation.setSuppressDropDownArrow(false); - } else { - validation.setSuppressDropDownArrow(true); - validation.setShowErrorBox(true); - } - // 2.2 阻止输入非下拉框的值 - validation.setErrorStyle(DataValidation.ErrorStyle.STOP); - validation.createErrorBox("提示", "此值不存在于下拉选择中!"); - // 2.3 添加下拉框约束 - writeSheetHolder.getSheet().addValidationData(validation); - } - -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/util/ExcelUtils.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/util/ExcelUtils.java deleted file mode 100644 index f16434e..0000000 --- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/util/ExcelUtils.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.iailab.framework.excel.core.util; - -import com.iailab.framework.excel.core.handler.SelectSheetWriteHandler; -import com.alibaba.excel.EasyExcel; -import com.alibaba.excel.converters.longconverter.LongStringConverter; -import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; -import org.springframework.web.multipart.MultipartFile; - -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.List; - -/** - * Excel 工具类 - * - * @author iailab - */ -public class ExcelUtils { - - /** - * 将列表以 Excel 响应给前端 - * - * @param response 响应 - * @param filename 文件名 - * @param sheetName Excel sheet 名 - * @param head Excel head 头 - * @param data 数据列表哦 - * @param <T> 泛型,保证 head 和 data 类型的一致性 - * @throws IOException 写入失败的情况 - */ - public static <T> void write(HttpServletResponse response, String filename, String sheetName, - Class<T> head, List<T> data) throws IOException { - // 输出 Excel - EasyExcel.write(response.getOutputStream(), head) - .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 - .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度 - .registerWriteHandler(new SelectSheetWriteHandler(head,false)) // 基于固定 sheet 实现下拉框 - .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度 - .sheet(sheetName).doWrite(data); - // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了 - response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name())); - response.setContentType("application/vnd.ms-excel;charset=UTF-8"); - } - - public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException { - return EasyExcel.read(file.getInputStream(), head, null) - .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 - .doReadAllSync(); - } - - public static <T> void write(HttpServletResponse response, String filename, String sheetName, - Class<T> head, List<T> data, boolean selectFlag) throws IOException { - // 输出 Excel - EasyExcel.write(response.getOutputStream(), head) - .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 - .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度 - .registerWriteHandler(new SelectSheetWriteHandler(head,selectFlag)) // 基于固定 sheet 实现下拉框 - .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度 - .sheet(sheetName).doWrite(data); - // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了 - response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name())); - response.setContentType("application/vnd.ms-excel;charset=UTF-8"); - } -} diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/package-info.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/package-info.java deleted file mode 100644 index 21caa19..0000000 --- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 基于 EasyExcel 实现 Excel 相关的操作 - */ -package com.iailab.framework.excel; diff --git a/iailab-framework/iailab-common-excel/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/iailab-framework/iailab-common-excel/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 889b89f..0000000 --- a/iailab-framework/iailab-common-excel/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,2 +0,0 @@ -com.iailab.framework.dict.config.IailabDictRpcAutoConfiguration -com.iailab.framework.dict.config.IailabDictAutoConfiguration diff --git a/iailab-framework/iailab-common-excel/src/test/java/com/iailab/framework/dict/core/util/DictFrameworkUtilsTest.java b/iailab-framework/iailab-common-excel/src/test/java/com/iailab/framework/dict/core/util/DictFrameworkUtilsTest.java deleted file mode 100644 index 157eae1..0000000 --- a/iailab-framework/iailab-common-excel/src/test/java/com/iailab/framework/dict/core/util/DictFrameworkUtilsTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.iailab.framework.dict.core.util; - -import com.iailab.framework.common.enums.CommonStatusEnum; -import com.iailab.framework.dict.core.DictFrameworkUtils; -import com.iailab.framework.test.core.ut.BaseMockitoUnitTest; -import com.iailab.module.system.api.dict.DictDataApi; -import com.iailab.module.system.api.dict.dto.DictDataRespDTO; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; - -import static com.iailab.framework.common.pojo.CommonResult.success; -import static com.iailab.framework.test.core.util.RandomUtils.randomPojo; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.when; - -/** - * {@link DictFrameworkUtils} 的单元测试 - */ -public class DictFrameworkUtilsTest extends BaseMockitoUnitTest { - - @Mock - private DictDataApi dictDataApi; - - @BeforeEach - public void setUp() { - DictFrameworkUtils.init(dictDataApi); - } - - @Test - public void testGetDictDataLabel() { - // mock 数据 - DictDataRespDTO dataRespDTO = randomPojo(DictDataRespDTO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - // mock 方法 - when(dictDataApi.getDictData(dataRespDTO.getDictType(), dataRespDTO.getValue())).thenReturn(success(dataRespDTO)); - - // 断言返回值 - assertEquals(dataRespDTO.getLabel(), DictFrameworkUtils.getDictDataLabel(dataRespDTO.getDictType(), dataRespDTO.getValue())); - } - - @Test - public void testParseDictDataValue() { - // mock 数据 - DictDataRespDTO resp = randomPojo(DictDataRespDTO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - // mock 方法 - when(dictDataApi.parseDictData(resp.getDictType(), resp.getLabel())).thenReturn(success(resp)); - // 断言返回值 - assertEquals(resp.getValue(), DictFrameworkUtils.parseDictDataValue(resp.getDictType(), resp.getLabel())); - } - -} diff --git a/iailab-framework/iailab-common-job/pom.xml b/iailab-framework/iailab-common-job/pom.xml deleted file mode 100644 index 38dd07a..0000000 --- a/iailab-framework/iailab-common-job/pom.xml +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>com.iailab</groupId> - <artifactId>iailab-framework</artifactId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-job</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description>任务拓展,基于 XXL-Job 实现</description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common</artifactId> - </dependency> - - <!-- Spring 核心 --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-configuration-processor</artifactId> - <optional>true</optional> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter</artifactId> - <optional>true</optional> - </dependency> - - <!-- Job 相关 --> - <dependency> - <groupId>com.xuxueli</groupId> - <artifactId>xxl-job-core</artifactId> - </dependency> - - <!-- 工具类相关 --> - <dependency> - <groupId>jakarta.validation</groupId> - <artifactId>jakarta.validation-api</artifactId> - </dependency> - - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common-job/src/main/java/com/iailab/framework/quartz/config/IailabAsyncAutoConfiguration.java b/iailab-framework/iailab-common-job/src/main/java/com/iailab/framework/quartz/config/IailabAsyncAutoConfiguration.java deleted file mode 100644 index 58c2947..0000000 --- a/iailab-framework/iailab-common-job/src/main/java/com/iailab/framework/quartz/config/IailabAsyncAutoConfiguration.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.iailab.framework.quartz.config; - -import com.alibaba.ttl.TtlRunnable; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; - -/** - * 异步任务 Configuration - */ -@AutoConfiguration -@EnableAsync -public class IailabAsyncAutoConfiguration { - - @Bean - public BeanPostProcessor threadPoolTaskExecutorBeanPostProcessor() { - return new BeanPostProcessor() { - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (!(bean instanceof ThreadPoolTaskExecutor)) { - return bean; - } - // 修改提交的任务,接入 TransmittableThreadLocal - ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) bean; - executor.setTaskDecorator(TtlRunnable::get); - return executor; - } - - }; - } - -} diff --git a/iailab-framework/iailab-common-job/src/main/java/com/iailab/framework/quartz/config/IailabXxlJobAutoConfiguration.java b/iailab-framework/iailab-common-job/src/main/java/com/iailab/framework/quartz/config/IailabXxlJobAutoConfiguration.java deleted file mode 100644 index 297ce1f..0000000 --- a/iailab-framework/iailab-common-job/src/main/java/com/iailab/framework/quartz/config/IailabXxlJobAutoConfiguration.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.iailab.framework.quartz.config; - -import com.xxl.job.core.executor.XxlJobExecutor; -import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; -import lombok.extern.slf4j.Slf4j; -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.Configuration; -import org.springframework.scheduling.annotation.EnableScheduling; - -/** - * XXL-Job 自动配置类 - * - * @author iailab - */ -@AutoConfiguration -@ConditionalOnClass(XxlJobSpringExecutor.class) -@ConditionalOnProperty(prefix = "xxl.job", name = "enabled", havingValue = "true", matchIfMissing = true) -@EnableConfigurationProperties({XxlJobProperties.class}) -@EnableScheduling // 开启 Spring 自带的定时任务 -@Slf4j -public class IailabXxlJobAutoConfiguration { - - @Bean - @ConditionalOnMissingBean - public XxlJobExecutor xxlJobExecutor(XxlJobProperties properties) { - log.info("[xxlJobExecutor][初始化 XXL-Job 执行器的配置]"); - XxlJobProperties.AdminProperties admin = properties.getAdmin(); - XxlJobProperties.ExecutorProperties executor = properties.getExecutor(); - - // 初始化执行器 - XxlJobExecutor xxlJobExecutor = new XxlJobSpringExecutor(); - xxlJobExecutor.setIp(executor.getIp()); - xxlJobExecutor.setPort(executor.getPort()); - xxlJobExecutor.setAppname(executor.getAppName()); - xxlJobExecutor.setLogPath(executor.getLogPath()); - xxlJobExecutor.setLogRetentionDays(executor.getLogRetentionDays()); - xxlJobExecutor.setAdminAddresses(admin.getAddresses()); - xxlJobExecutor.setAccessToken(properties.getAccessToken()); - return xxlJobExecutor; - } - -} diff --git a/iailab-framework/iailab-common-job/src/main/java/com/iailab/framework/quartz/config/XxlJobProperties.java b/iailab-framework/iailab-common-job/src/main/java/com/iailab/framework/quartz/config/XxlJobProperties.java deleted file mode 100644 index 5d09c7b..0000000 --- a/iailab-framework/iailab-common-job/src/main/java/com/iailab/framework/quartz/config/XxlJobProperties.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.iailab.framework.quartz.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.validation.annotation.Validated; - -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -/** - * XXL-Job 配置类 - */ -@ConfigurationProperties("xxl.job") -@Validated -@Data -public class XxlJobProperties { - - /** - * 是否开启,默认为 true 关闭 - */ - private Boolean enabled = true; - /** - * 访问令牌 - */ - private String accessToken; - /** - * 控制器配置 - */ - @NotNull(message = "控制器配置不能为空") - private AdminProperties admin; - /** - * 执行器配置 - */ - @NotNull(message = "执行器配置不能为空") - private ExecutorProperties executor; - - /** - * XXL-Job 调度器配置类 - */ - @Data - @Valid - public static class AdminProperties { - - /** - * 调度器地址 - */ - @NotEmpty(message = "调度器地址不能为空") - private String addresses; - - } - - /** - * XXL-Job 执行器配置类 - */ - @Data - @Valid - public static class ExecutorProperties { - - /** - * 默认端口 - * - * 这里使用 -1 表示随机 - */ - private static final Integer PORT_DEFAULT = -1; - - /** - * 默认日志保留天数 - * - * 如果想永久保留,则设置为 -1 - */ - private static final Integer LOG_RETENTION_DAYS_DEFAULT = 30; - - /** - * 应用名 - */ - @NotEmpty(message = "应用名不能为空") - private String appName; - /** - * 执行器的 IP - */ - private String ip; - /** - * 执行器的 Port - */ - private Integer port = PORT_DEFAULT; - /** - * 日志地址 - */ - @NotEmpty(message = "日志地址不能为空") - private String logPath; - /** - * 日志保留天数 - */ - private Integer logRetentionDays = LOG_RETENTION_DAYS_DEFAULT; - - } - -} diff --git a/iailab-framework/iailab-common-job/src/main/java/com/iailab/framework/quartz/package-info.java b/iailab-framework/iailab-common-job/src/main/java/com/iailab/framework/quartz/package-info.java deleted file mode 100644 index 38ff695..0000000 --- a/iailab-framework/iailab-common-job/src/main/java/com/iailab/framework/quartz/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * 1. 定时任务,基于 XXL-Job 实现。 - * 2. 异步任务,采用 Spring Async 异步执行。 - */ -package com.iailab.framework.quartz; diff --git a/iailab-framework/iailab-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/iailab-framework/iailab-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index f76be21..0000000 --- a/iailab-framework/iailab-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,2 +0,0 @@ -com.iailab.framework.quartz.config.IailabXxlJobAutoConfiguration -com.iailab.framework.quartz.config.IailabAsyncAutoConfiguration diff --git a/iailab-framework/iailab-common-monitor/pom.xml b/iailab-framework/iailab-common-monitor/pom.xml deleted file mode 100644 index 2746113..0000000 --- a/iailab-framework/iailab-common-monitor/pom.xml +++ /dev/null @@ -1,73 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>com.iailab</groupId> - <artifactId>iailab-framework</artifactId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-monitor</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description>服务监控,提供链路追踪、日志服务、指标收集等等功能</description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common</artifactId> - </dependency> - - <!-- Spring 核心 --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-aop</artifactId> - </dependency> - - <!-- Web 相关 --> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-web</artifactId> - <scope>provided</scope> <!-- 设置为 provided,只有 TraceFilter 使用 --> - </dependency> - - <dependency> - <groupId>jakarta.servlet</groupId> - <artifactId>jakarta.servlet-api</artifactId> - <scope>provided</scope> <!-- 设置为 provided,只有 TraceFilter 使用 --> - </dependency> - - <!-- 监控相关 --> - <dependency> - <groupId>io.opentracing</groupId> - <artifactId>opentracing-util</artifactId> - </dependency> - <dependency> - <groupId>org.apache.skywalking</groupId> - <artifactId>apm-toolkit-trace</artifactId> - </dependency> - <dependency> - <groupId>org.apache.skywalking</groupId> - <artifactId>apm-toolkit-logback-1.x</artifactId> - </dependency> - <dependency> - <groupId>org.apache.skywalking</groupId> - <artifactId>apm-toolkit-opentracing</artifactId> - </dependency> - - <!-- Micrometer 对 Prometheus 的支持 --> - <dependency> - <groupId>io.micrometer</groupId> - <artifactId>micrometer-registry-prometheus</artifactId> - </dependency> - - <dependency> - <groupId>de.codecentric</groupId> - <artifactId>spring-boot-admin-starter-client</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 --> - </dependency> - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/config/IailabMetricsAutoConfiguration.java b/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/config/IailabMetricsAutoConfiguration.java deleted file mode 100644 index 9d46110..0000000 --- a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/config/IailabMetricsAutoConfiguration.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.framework.tracer.config; - -import io.micrometer.core.instrument.MeterRegistry; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * Metrics 配置类 - * - * @author iailab - */ -@AutoConfiguration -@ConditionalOnClass({MeterRegistryCustomizer.class}) -@ConditionalOnProperty(prefix = "iailab.metrics", value = "enable", matchIfMissing = true) // 允许使用 iailab.metrics.enable=false 禁用 Metrics -public class IailabMetricsAutoConfiguration { - - @Bean - public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags( - @Value("${spring.application.name}") String applicationName) { - return registry -> registry.config().commonTags("application", applicationName); - } - -} diff --git a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/config/IailabTracerAutoConfiguration.java b/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/config/IailabTracerAutoConfiguration.java deleted file mode 100644 index 48072c5..0000000 --- a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/config/IailabTracerAutoConfiguration.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.iailab.framework.tracer.config; - -import com.iailab.framework.common.enums.WebFilterOrderEnum; -import com.iailab.framework.tracer.core.aop.BizTraceAspect; -import com.iailab.framework.tracer.core.filter.TraceFilter; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; - -/** - * Tracer 配置类 - * - * @author mashu - */ -@AutoConfiguration -@ConditionalOnClass({BizTraceAspect.class}) -@EnableConfigurationProperties(TracerProperties.class) -@ConditionalOnProperty(prefix = "iailab.tracer", value = "enable", matchIfMissing = true) -public class IailabTracerAutoConfiguration { - - // TODO @iailab:重要。目前 opentracing 版本存在冲突,要么保证 skywalking,要么保证阿里云短信 sdk -// @Bean -// public TracerProperties bizTracerProperties() { -// return new TracerProperties(); -// } -// -// @Bean -// public BizTraceAspect bizTracingAop() { -// return new BizTraceAspect(tracer()); -// } -// -// @Bean -// public Tracer tracer() { -// // 创建 SkywalkingTracer 对象 -// SkywalkingTracer tracer = new SkywalkingTracer(); -// // 设置为 GlobalTracer 的追踪器 -// GlobalTracer.register(tracer); -// return tracer; -// } - - /** - * 创建 TraceFilter 过滤器,响应 header 设置 traceId - */ - @Bean - public FilterRegistrationBean<TraceFilter> traceFilter() { - FilterRegistrationBean<TraceFilter> registrationBean = new FilterRegistrationBean<>(); - registrationBean.setFilter(new TraceFilter()); - registrationBean.setOrder(WebFilterOrderEnum.TRACE_FILTER); - return registrationBean; - } - -} diff --git a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/config/TracerProperties.java b/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/config/TracerProperties.java deleted file mode 100644 index a779e16..0000000 --- a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/config/TracerProperties.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.iailab.framework.tracer.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * BizTracer配置类 - * - * @author 麻薯 - */ -@ConfigurationProperties("iailab.tracer") -@Data -public class TracerProperties { -} diff --git a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/core/annotation/BizTrace.java b/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/core/annotation/BizTrace.java deleted file mode 100644 index 074bb18..0000000 --- a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/core/annotation/BizTrace.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.iailab.framework.tracer.core.annotation; - -import java.lang.annotation.*; - -/** - * 打印业务编号 / 业务类型注解 - * - * 使用时,需要设置 SkyWalking OAP Server 的 application.yaml 配置文件,修改 SW_SEARCHABLE_TAG_KEYS 配置项, - * 增加 biz.type 和 biz.id 两值,然后重启 SkyWalking OAP Server 服务器。 - * - * @author 麻薯 - */ -@Target({ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -@Inherited -public @interface BizTrace { - - /** - * 业务编号 tag 名 - */ - String ID_TAG = "biz.id"; - /** - * 业务类型 tag 名 - */ - String TYPE_TAG = "biz.type"; - - /** - * @return 操作名 - */ - String operationName() default ""; - - /** - * @return 业务编号 - */ - String id(); - - /** - * @return 业务类型 - */ - String type(); - -} diff --git a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/core/aop/BizTraceAspect.java b/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/core/aop/BizTraceAspect.java deleted file mode 100644 index 954e933..0000000 --- a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/core/aop/BizTraceAspect.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.iailab.framework.tracer.core.aop; - -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.tracer.core.annotation.BizTrace; -import com.iailab.framework.common.util.spring.SpringExpressionUtils; -import com.iailab.framework.tracer.core.util.TracerFrameworkUtils; -import io.opentracing.Span; -import io.opentracing.Tracer; -import io.opentracing.tag.Tags; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; - -import java.util.Map; - -import static java.util.Arrays.asList; - -/** - * {@link BizTrace} 切面,记录业务链路 - * - * @author mashu - */ -@Aspect -@AllArgsConstructor -@Slf4j -public class BizTraceAspect { - - private static final String BIZ_OPERATION_NAME_PREFIX = "Biz/"; - - private final Tracer tracer; - - @Around(value = "@annotation(trace)") - public Object around(ProceedingJoinPoint joinPoint, BizTrace trace) throws Throwable { - // 创建 span - String operationName = getOperationName(joinPoint, trace); - Span span = tracer.buildSpan(operationName) - .withTag(Tags.COMPONENT.getKey(), "biz") - .start(); - try { - // 执行原有方法 - return joinPoint.proceed(); - } catch (Throwable throwable) { - TracerFrameworkUtils.onError(throwable, span); - throw throwable; - } finally { - // 设置 Span 的 biz 属性 - setBizTag(span, joinPoint, trace); - // 完成 Span - span.finish(); - } - } - - private String getOperationName(ProceedingJoinPoint joinPoint, BizTrace trace) { - // 自定义操作名 - if (StrUtil.isNotEmpty(trace.operationName())) { - return BIZ_OPERATION_NAME_PREFIX + trace.operationName(); - } - // 默认操作名,使用方法名 - return BIZ_OPERATION_NAME_PREFIX - + joinPoint.getSignature().getDeclaringType().getSimpleName() - + "/" + joinPoint.getSignature().getName(); - } - - private void setBizTag(Span span, ProceedingJoinPoint joinPoint, BizTrace trace) { - try { - Map<String, Object> result = SpringExpressionUtils.parseExpressions(joinPoint, asList(trace.type(), trace.id())); - span.setTag(BizTrace.TYPE_TAG, MapUtil.getStr(result, trace.type())); - span.setTag(BizTrace.ID_TAG, MapUtil.getStr(result, trace.id())); - } catch (Exception ex) { - log.error("[setBizTag][解析 bizType 与 bizId 发生异常]", ex); - } - } - -} diff --git a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/core/filter/TraceFilter.java b/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/core/filter/TraceFilter.java deleted file mode 100644 index 75f943c..0000000 --- a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/core/filter/TraceFilter.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.iailab.framework.tracer.core.filter; - -import com.iailab.framework.common.util.monitor.TracerUtils; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * Trace 过滤器,打印 traceId 到 header 中返回 - * - * @author iailab - */ -public class TraceFilter extends OncePerRequestFilter { - - /** - * Header 名 - 链路追踪编号 - */ - private static final String HEADER_NAME_TRACE_ID = "trace-id"; - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) - throws IOException, ServletException { - // 设置响应 traceId - response.addHeader(HEADER_NAME_TRACE_ID, TracerUtils.getTraceId()); - // 继续过滤 - chain.doFilter(request, response); - } - -} diff --git a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/core/util/TracerFrameworkUtils.java b/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/core/util/TracerFrameworkUtils.java deleted file mode 100644 index 0e08f91..0000000 --- a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/core/util/TracerFrameworkUtils.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.iailab.framework.tracer.core.util; - -import io.opentracing.Span; -import io.opentracing.tag.Tags; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.HashMap; -import java.util.Map; - -/** - * 链路追踪 Util - * - * @author iailab - */ -public class TracerFrameworkUtils { - - /** - * 将异常记录到 Span 中,参考自 com.aliyuncs.utils.TraceUtils - * - * @param throwable 异常 - * @param span Span - */ - public static void onError(Throwable throwable, Span span) { - Tags.ERROR.set(span, Boolean.TRUE); - if (throwable != null) { - span.log(errorLogs(throwable)); - } - } - - private static Map<String, Object> errorLogs(Throwable throwable) { - Map<String, Object> errorLogs = new HashMap<String, Object>(10); - errorLogs.put("event", Tags.ERROR.getKey()); - errorLogs.put("error.object", throwable); - errorLogs.put("error.kind", throwable.getClass().getName()); - String message = throwable.getCause() != null ? throwable.getCause().getMessage() : throwable.getMessage(); - if (message != null) { - errorLogs.put("message", message); - } - StringWriter sw = new StringWriter(); - throwable.printStackTrace(new PrintWriter(sw)); - errorLogs.put("stack", sw.toString()); - return errorLogs; - } - -} diff --git a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/package-info.java b/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/package-info.java deleted file mode 100644 index 681acc6..0000000 --- a/iailab-framework/iailab-common-monitor/src/main/java/com/iailab/framework/tracer/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 使用 SkyWalking 组件,作为链路追踪、日志中心。 - * - * @author iailab - */ -package com.iailab.framework.tracer; diff --git a/iailab-framework/iailab-common-monitor/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/iailab-framework/iailab-common-monitor/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index d5e538e..0000000 --- a/iailab-framework/iailab-common-monitor/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,2 +0,0 @@ -com.iailab.framework.tracer.config.IailabTracerAutoConfiguration -com.iailab.framework.tracer.config.IailabMetricsAutoConfiguration diff --git a/iailab-framework/iailab-common-mq/pom.xml b/iailab-framework/iailab-common-mq/pom.xml deleted file mode 100644 index 29b9b2b..0000000 --- a/iailab-framework/iailab-common-mq/pom.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>com.iailab</groupId> - <artifactId>iailab-framework</artifactId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-mq</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description>消息队列,支持 Redis、RocketMQ、RabbitMQ、Kafka 四种</description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <!-- DB 相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-redis</artifactId> - </dependency> - - <!-- 消息队列相关 --> - <dependency> - <groupId>org.springframework.kafka</groupId> - <artifactId>spring-kafka</artifactId> -<!-- <optional>true</optional>--> - </dependency> - <dependency> - <groupId>org.springframework.amqp</groupId> - <artifactId>spring-rabbit</artifactId> -<!-- <optional>true</optional>--> - </dependency> - <dependency> - <groupId>org.apache.rocketmq</groupId> - <artifactId>rocketmq-spring-boot-starter</artifactId> -<!-- <optional>true</optional>--> - </dependency> - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/common/RoutingConstant.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/common/RoutingConstant.java deleted file mode 100644 index 47d1c14..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/common/RoutingConstant.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iailab.framework.mq.common; - -/** - * - * - * @author PanZhibao - * @Description - * @createTime 2024年11月05日 - */ -public interface RoutingConstant { - - String Iailab_Data_PointCollectFinish = "Iailab.Data.PointCollectFinish"; - - // 摄像头通配路由 - String Iailab_Data_Image = "Iailab.Data.Image.*"; - - // 大华摄像头路由 - String Iailab_Data_Image_Dahua = "Iailab.Data.Image.Dahua"; - - // 海康摄像头路由 - String Iailab_Data_Image_Hikvision = "Iailab.Data.Image.Hikvision"; -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/package-info.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/package-info.java deleted file mode 100644 index 74fea2a..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 消息队列,支持 Redis、RocketMQ、RabbitMQ、Kafka 四种 - */ -package com.iailab.framework.mq; diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/rabbitmq/config/IailabRabbitMQAutoConfiguration.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/rabbitmq/config/IailabRabbitMQAutoConfiguration.java deleted file mode 100644 index 2efeb3b..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/rabbitmq/config/IailabRabbitMQAutoConfiguration.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.framework.mq.rabbitmq.config; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; -import org.springframework.amqp.support.converter.MessageConverter; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.context.annotation.Bean; - -/** - * RabbitMQ 消息队列配置类 - * - * @author iailab - */ -@AutoConfiguration -@Slf4j -@ConditionalOnClass(name = "org.springframework.amqp.rabbit.core.RabbitTemplate") -public class IailabRabbitMQAutoConfiguration { - - /** - * Jackson2JsonMessageConverter Bean:使用 jackson 序列化消息 - */ - @Bean - public MessageConverter createMessageConverter() { - return new Jackson2JsonMessageConverter(); - } - -} diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/rabbitmq/core/package-info.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/rabbitmq/core/package-info.java deleted file mode 100644 index 1340868..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/rabbitmq/core/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占位符,无特殊逻辑 - */ -package com.iailab.framework.mq.rabbitmq.core; \ No newline at end of file diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/rabbitmq/package-info.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/rabbitmq/package-info.java deleted file mode 100644 index 1582bcb..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/rabbitmq/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 消息队列,基于 RabbitMQ 提供 - */ -package com.iailab.framework.mq.rabbitmq; diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/config/IailabRedisMQConsumerAutoConfiguration.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/config/IailabRedisMQConsumerAutoConfiguration.java deleted file mode 100644 index 187f863..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/config/IailabRedisMQConsumerAutoConfiguration.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.iailab.framework.mq.redis.config; - -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.system.SystemUtil; -import com.iailab.framework.common.enums.DocumentEnum; -import com.iailab.framework.mq.redis.core.RedisMQTemplate; -import com.iailab.framework.mq.redis.core.job.RedisPendingMessageResendJob; -import com.iailab.framework.mq.redis.core.pubsub.AbstractRedisChannelMessageListener; -import com.iailab.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener; -import com.iailab.framework.redis.config.IailabRedisAutoConfiguration; -import lombok.extern.slf4j.Slf4j; -import org.redisson.api.RedissonClient; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.context.annotation.Bean; -import org.springframework.data.redis.connection.RedisServerCommands; -import org.springframework.data.redis.connection.stream.Consumer; -import org.springframework.data.redis.connection.stream.ObjectRecord; -import org.springframework.data.redis.connection.stream.ReadOffset; -import org.springframework.data.redis.connection.stream.StreamOffset; -import org.springframework.data.redis.core.RedisCallback; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.listener.ChannelTopic; -import org.springframework.data.redis.listener.RedisMessageListenerContainer; -import org.springframework.data.redis.stream.StreamMessageListenerContainer; -import org.springframework.scheduling.annotation.EnableScheduling; - -import java.util.List; -import java.util.Properties; - -/** - * Redis 消息队列 Consumer 配置类 - * - * @author iailab - */ -@Slf4j -@EnableScheduling // 启用定时任务,用于 RedisPendingMessageResendJob 重发消息 -@AutoConfiguration(after = IailabRedisAutoConfiguration.class) -public class IailabRedisMQConsumerAutoConfiguration { - - /** - * 创建 Redis Pub/Sub 广播消费的容器 - */ - @Bean - @ConditionalOnBean(AbstractRedisChannelMessageListener.class) // 只有 AbstractChannelMessageListener 存在的时候,才需要注册 Redis pubsub 监听 - public RedisMessageListenerContainer redisMessageListenerContainer( - RedisMQTemplate redisMQTemplate, List<AbstractRedisChannelMessageListener<?>> listeners) { - // 创建 RedisMessageListenerContainer 对象 - RedisMessageListenerContainer container = new RedisMessageListenerContainer(); - // 设置 RedisConnection 工厂。 - container.setConnectionFactory(redisMQTemplate.getRedisTemplate().getRequiredConnectionFactory()); - // 添加监听器 - listeners.forEach(listener -> { - listener.setRedisMQTemplate(redisMQTemplate); - container.addMessageListener(listener, new ChannelTopic(listener.getChannel())); - log.info("[redisMessageListenerContainer][注册 Channel({}) 对应的监听器({})]", - listener.getChannel(), listener.getClass().getName()); - }); - return container; - } - - /** - * 创建 Redis Stream 重新消费的任务 - */ - @Bean - @ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 - public RedisPendingMessageResendJob redisPendingMessageResendJob(List<AbstractRedisStreamMessageListener<?>> listeners, - RedisMQTemplate redisTemplate, - @Value("${spring.application.name}") String groupName, - RedissonClient redissonClient) { - return new RedisPendingMessageResendJob(listeners, redisTemplate, groupName, redissonClient); - } - - /** - * 创建 Redis Stream 集群消费的容器 - * - * 基础知识:<a href="https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html">Redis Stream 的 xreadgroup 命令</a> - */ - @Bean(initMethod = "start", destroyMethod = "stop") - @ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 - public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer( - RedisMQTemplate redisMQTemplate, List<AbstractRedisStreamMessageListener<?>> listeners) { - RedisTemplate<String, ?> redisTemplate = redisMQTemplate.getRedisTemplate(); - checkRedisVersion(redisTemplate); - // 第一步,创建 StreamMessageListenerContainer 容器 - // 创建 options 配置 - StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> containerOptions = - StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder() - .batchSize(10) // 一次性最多拉取多少条消息 - .targetType(String.class) // 目标类型。统一使用 String,通过自己封装的 AbstractStreamMessageListener 去反序列化 - .build(); - // 创建 container 对象 - StreamMessageListenerContainer<String, ObjectRecord<String, String>> container = - StreamMessageListenerContainer.create(redisMQTemplate.getRedisTemplate().getRequiredConnectionFactory(), containerOptions); - - // 第二步,注册监听器,消费对应的 Stream 主题 - String consumerName = buildConsumerName(); - listeners.parallelStream().forEach(listener -> { - log.info("[redisStreamMessageListenerContainer][开始注册 StreamKey({}) 对应的监听器({})]", - listener.getStreamKey(), listener.getClass().getName()); - // 创建 listener 对应的消费者分组 - try { - redisTemplate.opsForStream().createGroup(listener.getStreamKey(), listener.getGroup()); - } catch (Exception ignore) { - } - // 设置 listener 对应的 redisTemplate - listener.setRedisMQTemplate(redisMQTemplate); - // 创建 Consumer 对象 - Consumer consumer = Consumer.from(listener.getGroup(), consumerName); - // 设置 Consumer 消费进度,以最小消费进度为准 - StreamOffset<String> streamOffset = StreamOffset.create(listener.getStreamKey(), ReadOffset.lastConsumed()); - // 设置 Consumer 监听 - StreamMessageListenerContainer.StreamReadRequestBuilder<String> builder = StreamMessageListenerContainer.StreamReadRequest - .builder(streamOffset).consumer(consumer) - .autoAcknowledge(false) // 不自动 ack - .cancelOnError(throwable -> false); // 默认配置,发生异常就取消消费,显然不符合预期;因此,我们设置为 false - container.register(builder.build(), listener); - log.info("[redisStreamMessageListenerContainer][完成注册 StreamKey({}) 对应的监听器({})]", - listener.getStreamKey(), listener.getClass().getName()); - }); - return container; - } - - /** - * 构建消费者名字,使用本地 IP + 进程编号的方式。 - * 参考自 RocketMQ clientId 的实现 - * - * @return 消费者名字 - */ - private static String buildConsumerName() { - return String.format("%s@%d", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID()); - } - - /** - * 校验 Redis 版本号,是否满足最低的版本号要求! - */ - private static void checkRedisVersion(RedisTemplate<String, ?> redisTemplate) { - // 获得 Redis 版本 - Properties info = redisTemplate.execute((RedisCallback<Properties>) RedisServerCommands::info); - String version = MapUtil.getStr(info, "redis_version"); - // 校验最低版本必须大于等于 5.0.0 - int majorVersion = Integer.parseInt(StrUtil.subBefore(version, '.', false)); - if (majorVersion < 5) { - throw new IllegalStateException(StrUtil.format("您当前的 Redis 版本为 {},小于最低要求的 5.0.0 版本!" + - "请参考 {} 文档进行安装。", version, DocumentEnum.REDIS_INSTALL.getUrl())); - } - } - -} diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/config/IailabRedisMQProducerAutoConfiguration.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/config/IailabRedisMQProducerAutoConfiguration.java deleted file mode 100644 index 249f7f6..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/config/IailabRedisMQProducerAutoConfiguration.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.iailab.framework.mq.redis.config; - -import com.iailab.framework.mq.redis.core.RedisMQTemplate; -import com.iailab.framework.mq.redis.core.interceptor.RedisMessageInterceptor; -import com.iailab.framework.redis.config.IailabRedisAutoConfiguration; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.data.redis.core.StringRedisTemplate; - -import java.util.List; - -/** - * Redis 消息队列 Producer 配置类 - * - * @author iailab - */ -@Slf4j -@AutoConfiguration(after = IailabRedisAutoConfiguration.class) -public class IailabRedisMQProducerAutoConfiguration { - - @Bean - public RedisMQTemplate redisMQTemplate(StringRedisTemplate redisTemplate, - List<RedisMessageInterceptor> interceptors) { - RedisMQTemplate redisMQTemplate = new RedisMQTemplate(redisTemplate); - // 添加拦截器 - interceptors.forEach(redisMQTemplate::addInterceptor); - return redisMQTemplate; - } - -} diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/RedisMQTemplate.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/RedisMQTemplate.java deleted file mode 100644 index 76009f8..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/RedisMQTemplate.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.iailab.framework.mq.redis.core; - -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.framework.mq.redis.core.interceptor.RedisMessageInterceptor; -import com.iailab.framework.mq.redis.core.message.AbstractRedisMessage; -import com.iailab.framework.mq.redis.core.pubsub.AbstractRedisChannelMessage; -import com.iailab.framework.mq.redis.core.stream.AbstractRedisStreamMessage; -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.data.redis.connection.stream.RecordId; -import org.springframework.data.redis.connection.stream.StreamRecords; -import org.springframework.data.redis.core.RedisTemplate; - -import java.util.ArrayList; -import java.util.List; - -/** - * Redis MQ 操作模板类 - * - * @author iailab - */ -@AllArgsConstructor -public class RedisMQTemplate { - - @Getter - private final RedisTemplate<String, ?> redisTemplate; - /** - * 拦截器数组 - */ - @Getter - private final List<RedisMessageInterceptor> interceptors = new ArrayList<>(); - - /** - * 发送 Redis 消息,基于 Redis pub/sub 实现 - * - * @param message 消息 - */ - public <T extends AbstractRedisChannelMessage> void send(T message) { - try { - sendMessageBefore(message); - // 发送消息 - redisTemplate.convertAndSend(message.getChannel(), JsonUtils.toJsonString(message)); - } finally { - sendMessageAfter(message); - } - } - - /** - * 发送 Redis 消息,基于 Redis Stream 实现 - * - * @param message 消息 - * @return 消息记录的编号对象 - */ - public <T extends AbstractRedisStreamMessage> RecordId send(T message) { - try { - sendMessageBefore(message); - // 发送消息 - return redisTemplate.opsForStream().add(StreamRecords.newRecord() - .ofObject(JsonUtils.toJsonString(message)) // 设置内容 - .withStreamKey(message.getStreamKey())); // 设置 stream key - } finally { - sendMessageAfter(message); - } - } - - /** - * 添加拦截器 - * - * @param interceptor 拦截器 - */ - public void addInterceptor(RedisMessageInterceptor interceptor) { - interceptors.add(interceptor); - } - - private void sendMessageBefore(AbstractRedisMessage message) { - // 正序 - interceptors.forEach(interceptor -> interceptor.sendMessageBefore(message)); - } - - private void sendMessageAfter(AbstractRedisMessage message) { - // 倒序 - for (int i = interceptors.size() - 1; i >= 0; i--) { - interceptors.get(i).sendMessageAfter(message); - } - } - -} diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/interceptor/RedisMessageInterceptor.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/interceptor/RedisMessageInterceptor.java deleted file mode 100644 index b3f1eae..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/interceptor/RedisMessageInterceptor.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.iailab.framework.mq.redis.core.interceptor; - -import com.iailab.framework.mq.redis.core.message.AbstractRedisMessage; - -/** - * {@link AbstractRedisMessage} 消息拦截器 - * 通过拦截器,作为插件机制,实现拓展。 - * 例如说,多租户场景下的 MQ 消息处理 - * - * @author iailab - */ -public interface RedisMessageInterceptor { - - default void sendMessageBefore(AbstractRedisMessage message) { - } - - default void sendMessageAfter(AbstractRedisMessage message) { - } - - default void consumeMessageBefore(AbstractRedisMessage message) { - } - - default void consumeMessageAfter(AbstractRedisMessage message) { - } - -} diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/job/RedisPendingMessageResendJob.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/job/RedisPendingMessageResendJob.java deleted file mode 100644 index 448c63a..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/job/RedisPendingMessageResendJob.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.iailab.framework.mq.redis.core.job; - -import cn.hutool.core.collection.CollUtil; -import com.iailab.framework.mq.redis.core.RedisMQTemplate; -import com.iailab.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.redisson.api.RLock; -import org.redisson.api.RedissonClient; -import org.springframework.data.domain.Range; -import org.springframework.data.redis.connection.stream.*; -import org.springframework.data.redis.core.StreamOperations; -import org.springframework.scheduling.annotation.Scheduled; - -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * 这个任务用于处理,crash 之后的消费者未消费完的消息 - */ -@Slf4j -@AllArgsConstructor -public class RedisPendingMessageResendJob { - - private static final String LOCK_KEY = "redis:pending:msg:lock"; - - /** - * 消息超时时间,默认 5 分钟 - * - * 1. 超时的消息才会被重新投递 - * 2. 由于定时任务 1 分钟一次,消息超时后不会被立即重投,极端情况下消息5分钟过期后,再等 1 分钟才会被扫瞄到 - */ - private static final int EXPIRE_TIME = 5 * 60; - - private final List<AbstractRedisStreamMessageListener<?>> listeners; - private final RedisMQTemplate redisTemplate; - private final String groupName; - private final RedissonClient redissonClient; - - /** - * 一分钟执行一次,这里选择每分钟的35秒执行,是为了避免整点任务过多的问题 - */ - @Scheduled(cron = "35 * * * * ?") - public void messageResend() { - RLock lock = redissonClient.getLock(LOCK_KEY); - // 尝试加锁 - if (lock.tryLock()) { - try { - execute(); - } catch (Exception ex) { - log.error("[messageResend][执行异常]", ex); - } finally { - lock.unlock(); - } - } - } - - /** - * 执行清理逻辑 - * - * @see <a href="https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/480/files">讨论</a> - */ - private void execute() { - StreamOperations<String, Object, Object> ops = redisTemplate.getRedisTemplate().opsForStream(); - listeners.forEach(listener -> { - PendingMessagesSummary pendingMessagesSummary = Objects.requireNonNull(ops.pending(listener.getStreamKey(), groupName)); - // 每个消费者的 pending 队列消息数量 - Map<String, Long> pendingMessagesPerConsumer = pendingMessagesSummary.getPendingMessagesPerConsumer(); - pendingMessagesPerConsumer.forEach((consumerName, pendingMessageCount) -> { - log.info("[processPendingMessage][消费者({}) 消息数量({})]", consumerName, pendingMessageCount); - // 每个消费者的 pending消息的详情信息 - PendingMessages pendingMessages = ops.pending(listener.getStreamKey(), Consumer.from(groupName, consumerName), Range.unbounded(), pendingMessageCount); - if (pendingMessages.isEmpty()) { - return; - } - pendingMessages.forEach(pendingMessage -> { - // 获取消息上一次传递到 consumer 的时间, - long lastDelivery = pendingMessage.getElapsedTimeSinceLastDelivery().getSeconds(); - if (lastDelivery < EXPIRE_TIME){ - return; - } - // 获取指定 id 的消息体 - List<MapRecord<String, Object, Object>> records = ops.range(listener.getStreamKey(), - Range.of(Range.Bound.inclusive(pendingMessage.getIdAsString()), Range.Bound.inclusive(pendingMessage.getIdAsString()))); - if (CollUtil.isEmpty(records)) { - return; - } - // 重新投递消息 - redisTemplate.getRedisTemplate().opsForStream().add(StreamRecords.newRecord() - .ofObject(records.get(0).getValue()) // 设置内容 - .withStreamKey(listener.getStreamKey())); - // ack 消息消费完成 - redisTemplate.getRedisTemplate().opsForStream().acknowledge(groupName, records.get(0)); - log.info("[processPendingMessage][消息({})重新投递成功]", records.get(0).getId()); - }); - }); - }); - } -} diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/message/AbstractRedisMessage.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/message/AbstractRedisMessage.java deleted file mode 100644 index 46b3f88..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/message/AbstractRedisMessage.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.iailab.framework.mq.redis.core.message; - -import lombok.Data; - -import java.util.HashMap; -import java.util.Map; - -/** - * Redis 消息抽象基类 - * - * @author iailab - */ -@Data -public abstract class AbstractRedisMessage { - - /** - * 头 - */ - private Map<String, String> headers = new HashMap<>(); - - public String getHeader(String key) { - return headers.get(key); - } - - public void addHeader(String key, String value) { - headers.put(key, value); - } - -} diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/pubsub/AbstractRedisChannelMessage.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/pubsub/AbstractRedisChannelMessage.java deleted file mode 100644 index e95d622..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/pubsub/AbstractRedisChannelMessage.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.iailab.framework.mq.redis.core.pubsub; - -import com.iailab.framework.mq.redis.core.message.AbstractRedisMessage; -import com.fasterxml.jackson.annotation.JsonIgnore; - -/** - * Redis Channel Message 抽象类 - * - * @author iailab - */ -public abstract class AbstractRedisChannelMessage extends AbstractRedisMessage { - - /** - * 获得 Redis Channel,默认使用类名 - * - * @return Channel - */ - @JsonIgnore // 避免序列化。原因是,Redis 发布 Channel 消息的时候,已经会指定。 - public String getChannel() { - return getClass().getSimpleName(); - } - -} diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/pubsub/AbstractRedisChannelMessageListener.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/pubsub/AbstractRedisChannelMessageListener.java deleted file mode 100644 index f0f2578..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/pubsub/AbstractRedisChannelMessageListener.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.iailab.framework.mq.redis.core.pubsub; - -import cn.hutool.core.util.TypeUtil; -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.framework.mq.redis.core.RedisMQTemplate; -import com.iailab.framework.mq.redis.core.interceptor.RedisMessageInterceptor; -import com.iailab.framework.mq.redis.core.message.AbstractRedisMessage; -import lombok.Setter; -import lombok.SneakyThrows; -import org.springframework.data.redis.connection.Message; -import org.springframework.data.redis.connection.MessageListener; - -import java.lang.reflect.Type; -import java.util.List; - -/** - * Redis Pub/Sub 监听器抽象类,用于实现广播消费 - * - * @param <T> 消息类型。一定要填写噢,不然会报错 - * - * @author iailab - */ -public abstract class AbstractRedisChannelMessageListener<T extends AbstractRedisChannelMessage> implements MessageListener { - - /** - * 消息类型 - */ - private final Class<T> messageType; - /** - * Redis Channel - */ - private final String channel; - /** - * RedisMQTemplate - */ - @Setter - private RedisMQTemplate redisMQTemplate; - - @SneakyThrows - protected AbstractRedisChannelMessageListener() { - this.messageType = getMessageClass(); - this.channel = messageType.getDeclaredConstructor().newInstance().getChannel(); - } - - /** - * 获得 Sub 订阅的 Redis Channel 通道 - * - * @return channel - */ - public final String getChannel() { - return channel; - } - - @Override - public final void onMessage(Message message, byte[] bytes) { - T messageObj = JsonUtils.parseObject(message.getBody(), messageType); - try { - consumeMessageBefore(messageObj); - // 消费消息 - this.onMessage(messageObj); - } finally { - consumeMessageAfter(messageObj); - } - } - - /** - * 处理消息 - * - * @param message 消息 - */ - public abstract void onMessage(T message); - - /** - * 通过解析类上的泛型,获得消息类型 - * - * @return 消息类型 - */ - @SuppressWarnings("unchecked") - private Class<T> getMessageClass() { - Type type = TypeUtil.getTypeArgument(getClass(), 0); - if (type == null) { - throw new IllegalStateException(String.format("类型(%s) 需要设置消息类型", getClass().getName())); - } - return (Class<T>) type; - } - - private void consumeMessageBefore(AbstractRedisMessage message) { - assert redisMQTemplate != null; - List<RedisMessageInterceptor> interceptors = redisMQTemplate.getInterceptors(); - // 正序 - interceptors.forEach(interceptor -> interceptor.consumeMessageBefore(message)); - } - - private void consumeMessageAfter(AbstractRedisMessage message) { - assert redisMQTemplate != null; - List<RedisMessageInterceptor> interceptors = redisMQTemplate.getInterceptors(); - // 倒序 - for (int i = interceptors.size() - 1; i >= 0; i--) { - interceptors.get(i).consumeMessageAfter(message); - } - } - -} diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/stream/AbstractRedisStreamMessage.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/stream/AbstractRedisStreamMessage.java deleted file mode 100644 index 05743c8..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/stream/AbstractRedisStreamMessage.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.iailab.framework.mq.redis.core.stream; - -import com.iailab.framework.mq.redis.core.message.AbstractRedisMessage; -import com.fasterxml.jackson.annotation.JsonIgnore; - -/** - * Redis Stream Message 抽象类 - * - * @author iailab - */ -public abstract class AbstractRedisStreamMessage extends AbstractRedisMessage { - - /** - * 获得 Redis Stream Key,默认使用类名 - * - * @return Channel - */ - @JsonIgnore // 避免序列化 - public String getStreamKey() { - return getClass().getSimpleName(); - } - -} diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/stream/AbstractRedisStreamMessageListener.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/stream/AbstractRedisStreamMessageListener.java deleted file mode 100644 index 6628611..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/core/stream/AbstractRedisStreamMessageListener.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.iailab.framework.mq.redis.core.stream; - -import cn.hutool.core.util.TypeUtil; -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.framework.mq.redis.core.RedisMQTemplate; -import com.iailab.framework.mq.redis.core.interceptor.RedisMessageInterceptor; -import com.iailab.framework.mq.redis.core.message.AbstractRedisMessage; -import lombok.Getter; -import lombok.Setter; -import lombok.SneakyThrows; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.redis.connection.stream.ObjectRecord; -import org.springframework.data.redis.stream.StreamListener; - -import java.lang.reflect.Type; -import java.util.List; - -/** - * Redis Stream 监听器抽象类,用于实现集群消费 - * - * @param <T> 消息类型。一定要填写噢,不然会报错 - * - * @author iailab - */ -public abstract class AbstractRedisStreamMessageListener<T extends AbstractRedisStreamMessage> - implements StreamListener<String, ObjectRecord<String, String>> { - - /** - * 消息类型 - */ - private final Class<T> messageType; - /** - * Redis Channel - */ - @Getter - private final String streamKey; - - /** - * Redis 消费者分组,默认使用 spring.application.name 名字 - */ - @Value("${spring.application.name}") - @Getter - private String group; - /** - * RedisMQTemplate - */ - @Setter - private RedisMQTemplate redisMQTemplate; - - @SneakyThrows - protected AbstractRedisStreamMessageListener() { - this.messageType = getMessageClass(); - this.streamKey = messageType.getDeclaredConstructor().newInstance().getStreamKey(); - } - - @Override - public void onMessage(ObjectRecord<String, String> message) { - // 消费消息 - T messageObj = JsonUtils.parseObject(message.getValue(), messageType); - try { - consumeMessageBefore(messageObj); - // 消费消息 - this.onMessage(messageObj); - // ack 消息消费完成 - redisMQTemplate.getRedisTemplate().opsForStream().acknowledge(group, message); - // TODO iailab:需要额外考虑以下几个点: - // 1. 处理异常的情况 - // 2. 发送日志;以及事务的结合 - // 3. 消费日志;以及通用的幂等性 - // 4. 消费失败的重试,https://zhuanlan.zhihu.com/p/60501638 - } finally { - consumeMessageAfter(messageObj); - } - } - - /** - * 处理消息 - * - * @param message 消息 - */ - public abstract void onMessage(T message); - - /** - * 通过解析类上的泛型,获得消息类型 - * - * @return 消息类型 - */ - @SuppressWarnings("unchecked") - private Class<T> getMessageClass() { - Type type = TypeUtil.getTypeArgument(getClass(), 0); - if (type == null) { - throw new IllegalStateException(String.format("类型(%s) 需要设置消息类型", getClass().getName())); - } - return (Class<T>) type; - } - - private void consumeMessageBefore(AbstractRedisMessage message) { - assert redisMQTemplate != null; - List<RedisMessageInterceptor> interceptors = redisMQTemplate.getInterceptors(); - // 正序 - interceptors.forEach(interceptor -> interceptor.consumeMessageBefore(message)); - } - - private void consumeMessageAfter(AbstractRedisMessage message) { - assert redisMQTemplate != null; - List<RedisMessageInterceptor> interceptors = redisMQTemplate.getInterceptors(); - // 倒序 - for (int i = interceptors.size() - 1; i >= 0; i--) { - interceptors.get(i).consumeMessageAfter(message); - } - } - -} diff --git a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/package-info.java b/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/package-info.java deleted file mode 100644 index 7a386a9..0000000 --- a/iailab-framework/iailab-common-mq/src/main/java/com/iailab/framework/mq/redis/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 消息队列,基于 Redis 提供: - * 1. 基于 Pub/Sub 实现广播消费 - * 2. 基于 Stream 实现集群消费 - */ -package com.iailab.framework.mq.redis; diff --git a/iailab-framework/iailab-common-mq/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/iailab-framework/iailab-common-mq/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index c38ac54..0000000 --- a/iailab-framework/iailab-common-mq/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,3 +0,0 @@ -com.iailab.framework.mq.redis.config.IailabRedisMQProducerAutoConfiguration -com.iailab.framework.mq.redis.config.IailabRedisMQConsumerAutoConfiguration -com.iailab.framework.mq.rabbitmq.config.IailabRabbitMQAutoConfiguration diff --git a/iailab-framework/iailab-common-mybatis/pom.xml b/iailab-framework/iailab-common-mybatis/pom.xml deleted file mode 100644 index 9aa8d58..0000000 --- a/iailab-framework/iailab-common-mybatis/pom.xml +++ /dev/null @@ -1,95 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>com.iailab</groupId> - <artifactId>iailab-framework</artifactId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-mybatis</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description>数据库连接池、多数据源、事务、MyBatis 拓展</description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common</artifactId> - </dependency> - - <!-- Web 相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-web</artifactId> - <scope>provided</scope> <!-- 设置为 provided,只有 OncePerRequestFilter 使用到 --> - </dependency> - - <!-- DB 相关 --> - <dependency> - <groupId>com.mysql</groupId> - <artifactId>mysql-connector-j</artifactId> - </dependency> - <dependency> - <groupId>com.oracle.database.jdbc</groupId> - <artifactId>ojdbc8</artifactId> -<!-- <optional>true</optional>--> - </dependency> - <dependency> - <groupId>org.postgresql</groupId> - <artifactId>postgresql</artifactId> - <optional>true</optional> - </dependency> - <dependency> - <groupId>com.microsoft.sqlserver</groupId> - <artifactId>mssql-jdbc</artifactId> - <optional>true</optional> - </dependency> - <dependency> - <groupId>com.dameng</groupId> - <artifactId>DmJdbcDriver18</artifactId> - <optional>true</optional> - </dependency> - - <dependency> - <groupId>com.alibaba</groupId> - <artifactId>druid-spring-boot-starter</artifactId> - </dependency> - <dependency> - <groupId>com.baomidou</groupId> - <artifactId>mybatis-plus-boot-starter</artifactId> - </dependency> - <dependency> - <groupId>com.baomidou</groupId> - <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 --> - </dependency> - - <dependency> - <groupId>com.github.yulichang</groupId> - <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 --> - </dependency> - - <dependency> - <groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 --> - <artifactId>easy-trans-spring-boot-starter</artifactId> - </dependency> - <dependency> - <groupId>com.fhs-opensource</groupId> - <artifactId>easy-trans-mybatis-plus-extend</artifactId> - </dependency> - <dependency> - <groupId>org.testng</groupId> - <artifactId>testng</artifactId> - <version>RELEASE</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>com.alibaba.nacos</groupId> - <artifactId>nacos-client</artifactId> - </dependency> - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/dao/BaseDao.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/dao/BaseDao.java deleted file mode 100644 index 0cdb369..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/dao/BaseDao.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.dao; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; - -/** - * 基础Dao - * - * @author Mark sunlightcs@gmail.com - * @since 1.0.0 - */ -public interface BaseDao<T> extends BaseMapper<T> { - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/entity/BaseEntity.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/entity/BaseEntity.java deleted file mode 100644 index 1a740ce..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/entity/BaseEntity.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.entity; - -import com.baomidou.mybatisplus.annotation.FieldFill; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import lombok.Data; - -import java.io.Serializable; -import java.util.Date; - -/** - * 基础实体类,所有实体都需要继承 - * - * @author Mark sunlightcs@gmail.com - */ -@Data -public abstract class BaseEntity implements Serializable { - /** - * id - */ - @TableId - private Long id; - /** - * 创建者 - */ - @TableField(fill = FieldFill.INSERT) - private Long creator; - /** - * 创建时间 - */ - @TableField(fill = FieldFill.INSERT) - private Date createDate; -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/package-info.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/package-info.java deleted file mode 100644 index 44e33fb..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.iailab.framework.common; \ No newline at end of file diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/page/PageData.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/page/PageData.java deleted file mode 100644 index 64c0822..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/page/PageData.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.page; - -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.Data; - -import java.io.Serializable; -import java.util.List; - -/** - * 分页工具类 - * - * @author Mark sunlightcs@gmail.com - */ -@Data -@Tag(name = "分页数据") -public class PageData<T> implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "总记录数") - private int total; - - @Schema(description = "列表数据") - private List<T> list; - - /** - * 分页 - * @param list 列表数据 - * @param total 总记录数 - */ - public PageData(List<T> list, long total) { - this.list = list; - this.total = (int)total; - } -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/service/BaseService.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/service/BaseService.java deleted file mode 100644 index 686441d..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/service/BaseService.java +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.service; - -import com.baomidou.mybatisplus.core.conditions.Wrapper; - -import java.io.Serializable; -import java.util.Collection; - -/** - * 基础服务接口,所有Service接口都要继承 - * - * @author Mark sunlightcs@gmail.com - */ -public interface BaseService<T> { - Class<T> currentModelClass(); - - /** - * <p> - * 插入一条记录(选择字段,策略插入) - * </p> - * - * @param entity 实体对象 - */ - boolean insert(T entity); - - /** - * <p> - * 插入(批量),该方法不支持 Oracle、SQL Server - * </p> - * - * @param entityList 实体对象集合 - */ - boolean insertBatch(Collection<T> entityList); - - /** - * <p> - * 插入(批量),该方法不支持 Oracle、SQL Server - * </p> - * - * @param entityList 实体对象集合 - * @param batchSize 插入批次数量 - */ - boolean insertBatch(Collection<T> entityList, int batchSize); - - /** - * <p> - * 根据 ID 选择修改 - * </p> - * - * @param entity 实体对象 - */ - boolean updateById(T entity); - - /** - * <p> - * 根据 whereEntity 条件,更新记录 - * </p> - * - * @param entity 实体对象 - * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper} - */ - boolean update(T entity, Wrapper<T> updateWrapper); - - /** - * <p> - * 根据ID 批量更新 - * </p> - * - * @param entityList 实体对象集合 - */ - boolean updateBatchById(Collection<T> entityList); - - /** - * <p> - * 根据ID 批量更新 - * </p> - * - * @param entityList 实体对象集合 - * @param batchSize 更新批次数量 - */ - boolean updateBatchById(Collection<T> entityList, int batchSize); - - /** - * <p> - * 根据 ID 查询 - * </p> - * - * @param id 主键ID - */ - T selectById(Serializable id); - - /** - * <p> - * 根据 ID 删除 - * </p> - * - * @param id 主键ID - */ - boolean deleteById(Serializable id); - - /** - * <p> - * 删除(根据ID 批量删除) - * </p> - * - * @param idList 主键ID列表 - */ - boolean deleteBatchIds(Collection<? extends Serializable> idList); -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/service/CrudService.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/service/CrudService.java deleted file mode 100644 index c8c78cb..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/service/CrudService.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * <p> - * https://www.renren.io - * <p> - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.service; - -import com.iailab.framework.common.page.PageData; - -import java.util.List; -import java.util.Map; - -/** - * CRUD基础服务接口 - * - * @author Mark sunlightcs@gmail.com - */ -public interface CrudService<T, D> extends BaseService<T> { - - PageData<D> page(Map<String, Object> params); - - List<D> list(Map<String, Object> params); - - D get(Long id); - - void save(D dto); - - void update(D dto); - - void delete(Long[] ids); - -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/service/impl/BaseServiceImpl.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/service/impl/BaseServiceImpl.java deleted file mode 100644 index 51254d7..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/service/impl/BaseServiceImpl.java +++ /dev/null @@ -1,219 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.service.impl; - -import com.baomidou.mybatisplus.core.conditions.Wrapper; -import com.baomidou.mybatisplus.core.enums.SqlMethod; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.core.metadata.OrderItem; -import com.baomidou.mybatisplus.core.toolkit.Constants; -import com.baomidou.mybatisplus.core.toolkit.ReflectionKit; -import com.baomidou.mybatisplus.core.toolkit.StringUtils; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; -import com.iailab.framework.common.constant.Constant; -import com.iailab.framework.common.page.PageData; -import com.iailab.framework.common.service.BaseService; -import com.iailab.framework.common.util.object.BeanUtils; -import org.apache.ibatis.binding.MapperMethod; -import org.apache.ibatis.logging.Log; -import org.apache.ibatis.logging.LogFactory; -import org.apache.ibatis.session.SqlSession; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.annotation.Transactional; - -import java.io.Serializable; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.function.BiConsumer; - -/** - * 基础服务类,所有Service都要继承 - * - * @author Mark sunlightcs@gmail.com - */ -public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements BaseService<T> { - @Autowired - protected M baseDao; - protected Log log = LogFactory.getLog(getClass()); - - /** - * 获取分页对象 - * @param params 分页查询参数 - * @param defaultOrderField 默认排序字段 - * @param isAsc 排序方式 - */ - protected IPage<T> getPage(Map<String, Object> params, String defaultOrderField, boolean isAsc) { - //分页参数 - long curPage = 1; - long limit = 10; - - if(params.get(Constant.PAGE) != null){ - curPage = Long.parseLong((String)params.get(Constant.PAGE)); - } - if(params.get(Constant.LIMIT) != null){ - limit = Long.parseLong((String)params.get(Constant.LIMIT)); - } - - //分页对象 - Page<T> page = new Page<>(curPage, limit); - - //分页参数 - params.put(Constant.PAGE, page); - - //排序字段 - String orderField = (String)params.get(Constant.ORDER_FIELD); - String order = (String)params.get(Constant.ORDER); - - //前端字段排序 - if(StringUtils.isNotBlank(orderField) && StringUtils.isNotBlank(order)){ - if(Constant.ASC.equalsIgnoreCase(order)) { - return page.addOrder(OrderItem.asc(orderField)); - }else { - return page.addOrder(OrderItem.desc(orderField)); - } - } - - //没有排序字段,则不排序 - if(StringUtils.isBlank(defaultOrderField)){ - return page; - } - - //默认排序 - if(isAsc) { - page.addOrder(OrderItem.asc(defaultOrderField)); - }else { - page.addOrder(OrderItem.desc(defaultOrderField)); - } - - return page; - } - - protected <T> PageData<T> getPageData(List<?> list, long total, Class<T> target){ - List<T> targetList = BeanUtils.toBean(list, target); - - return new PageData<>(targetList, total); - } - - protected <T> PageData<T> getPageData(IPage page, Class<T> target){ - return getPageData(page.getRecords(), page.getTotal(), target); - } - - protected void paramsToLike(Map<String, Object> params, String... likes){ - for (String like : likes){ - String val = (String)params.get(like); - if (StringUtils.isNotBlank(val)){ - params.put(like, "%" + val + "%"); - }else { - params.put(like, null); - } - } - } - - /** - * <p> - * 判断数据库操作是否成功 - * </p> - * <p> - * 注意!! 该方法为 Integer 判断,不可传入 int 基本类型 - * </p> - * - * @param result 数据库操作返回影响条数 - * @return boolean - */ - protected static boolean retBool(Integer result) { - return SqlHelper.retBool(result); - } - - protected Class<M> currentMapperClass() { - return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 0); - } - - @Override - public Class<T> currentModelClass() { - return (Class<T>)ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 1); - } - - protected String getSqlStatement(SqlMethod sqlMethod) { - return SqlHelper.getSqlStatement(this.currentMapperClass(), sqlMethod); - } - - @Override - public boolean insert(T entity) { - return BaseServiceImpl.retBool(baseDao.insert(entity)); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public boolean insertBatch(Collection<T> entityList) { - return insertBatch(entityList, 100); - } - - /** - * 批量插入 - */ - @Override - @Transactional(rollbackFor = Exception.class) - public boolean insertBatch(Collection<T> entityList, int batchSize) { - String sqlStatement = getSqlStatement(SqlMethod.INSERT_ONE); - return executeBatch(entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity)); - } - - /** - * 执行批量操作 - */ - protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) { - return SqlHelper.executeBatch(this.currentModelClass(), this.log, list, batchSize, consumer); - } - - - @Override - public boolean updateById(T entity) { - return BaseServiceImpl.retBool(baseDao.updateById(entity)); - } - - @Override - public boolean update(T entity, Wrapper<T> updateWrapper) { - return BaseServiceImpl.retBool(baseDao.update(entity, updateWrapper)); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public boolean updateBatchById(Collection<T> entityList) { - return updateBatchById(entityList, 30); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public boolean updateBatchById(Collection<T> entityList, int batchSize) { - String sqlStatement = getSqlStatement(SqlMethod.UPDATE_BY_ID); - return executeBatch(entityList, batchSize, (sqlSession, entity) -> { - MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>(); - param.put(Constants.ENTITY, entity); - sqlSession.update(sqlStatement, param); - }); - } - - @Override - public T selectById(Serializable id) { - return baseDao.selectById(id); - } - - @Override - public boolean deleteById(Serializable id) { - return SqlHelper.retBool(baseDao.deleteById(id)); - } - - @Override - public boolean deleteBatchIds(Collection<? extends Serializable> idList) { - return SqlHelper.retBool(baseDao.deleteBatchIds(idList)); - } -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/service/impl/CrudServiceImpl.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/service/impl/CrudServiceImpl.java deleted file mode 100644 index 6b12a43..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/common/service/impl/CrudServiceImpl.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * <p> - * https://www.renren.io - * <p> - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.service.impl; - -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.core.toolkit.ReflectionKit; -import com.iailab.framework.common.page.PageData; -import com.iailab.framework.common.service.CrudService; -import com.iailab.framework.common.util.object.ConvertUtils; -import org.springframework.beans.BeanUtils; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -/** - * CRUD基础服务类 - * - * @author Mark sunlightcs@gmail.com - */ -public abstract class CrudServiceImpl<M extends BaseMapper<T>, T, D> extends BaseServiceImpl<M, T> implements CrudService<T, D> { - - protected Class<D> currentDtoClass() { - return (Class<D>)ReflectionKit.getSuperClassGenericType(getClass(), CrudServiceImpl.class, 2); - } - - @Override - public PageData<D> page(Map<String, Object> params) { - IPage<T> page = baseDao.selectPage( - getPage(params, null, false), - getWrapper(params) - ); - - return getPageData(page, currentDtoClass()); - } - - @Override - public List<D> list(Map<String, Object> params) { - List<T> entityList = baseDao.selectList(getWrapper(params)); - - return ConvertUtils.sourceToTarget(entityList, currentDtoClass()); - } - - public abstract QueryWrapper<T> getWrapper(Map<String, Object> params); - - @Override - public D get(Long id) { - T entity = baseDao.selectById(id); - - return ConvertUtils.sourceToTarget(entity, currentDtoClass()); - } - - @Override - public void save(D dto) { - T entity = ConvertUtils.sourceToTarget(dto, currentModelClass()); - insert(entity); - - //copy主键值到dto - BeanUtils.copyProperties(entity, dto); - } - - @Override - public void update(D dto) { - T entity = ConvertUtils.sourceToTarget(dto, currentModelClass()); - updateById(entity); - } - - @Override - public void delete(Long[] ids) { - baseDao.deleteBatchIds(Arrays.asList(ids)); - } -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/datasource/config/IailabDataSourceAutoConfiguration.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/datasource/config/IailabDataSourceAutoConfiguration.java deleted file mode 100644 index 27d252d..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/datasource/config/IailabDataSourceAutoConfiguration.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.iailab.framework.datasource.config; - -import com.iailab.framework.datasource.core.filter.DruidAdRemoveFilter; -import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.transaction.annotation.EnableTransactionManagement; - -/** - * 数据库配置类 - * - * @author iailab - */ -@AutoConfiguration -@EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理 -@EnableConfigurationProperties(DruidStatProperties.class) -public class IailabDataSourceAutoConfiguration { - - /** - * 创建 DruidAdRemoveFilter 过滤器,过滤 common.js 的广告 - */ - @Bean - @ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true") - public FilterRegistrationBean<DruidAdRemoveFilter> druidAdRemoveFilterFilter(DruidStatProperties properties) { - // 获取 druid web 监控页面的参数 - DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); - // 提取 common.js 的配置路径 - String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; - String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); - // 创建 DruidAdRemoveFilter Bean - FilterRegistrationBean<DruidAdRemoveFilter> registrationBean = new FilterRegistrationBean<>(); - registrationBean.setFilter(new DruidAdRemoveFilter()); - registrationBean.addUrlPatterns(commonJsPattern); - return registrationBean; - } - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/datasource/core/enums/DataSourceEnum.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/datasource/core/enums/DataSourceEnum.java deleted file mode 100644 index 0617e5e..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/datasource/core/enums/DataSourceEnum.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iailab.framework.datasource.core.enums; - -/** - * 对应于多数据源中不同数据源配置 - * - * 通过在方法上,使用 {@link com.baomidou.dynamic.datasource.annotation.DS} 注解,设置使用的数据源。 - * 注意,默认是 {@link #MASTER} 数据源 - * - * 对应官方文档为 http://dynamic-datasource.com/guide/customize/Annotation.html - */ -public interface DataSourceEnum { - - /** - * 主库,推荐使用 {@link com.baomidou.dynamic.datasource.annotation.Master} 注解 - */ - String MASTER = "master"; - /** - * 从库,推荐使用 {@link com.baomidou.dynamic.datasource.annotation.Slave} 注解 - */ - String SLAVE = "slave"; - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/datasource/core/filter/DruidAdRemoveFilter.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/datasource/core/filter/DruidAdRemoveFilter.java deleted file mode 100644 index cd54d0c..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/datasource/core/filter/DruidAdRemoveFilter.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.iailab.framework.datasource.core.filter; - -import com.alibaba.druid.util.Utils; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * Druid 底部广告过滤器 - * - * @author iailab - */ -public class DruidAdRemoveFilter extends OncePerRequestFilter { - - /** - * common.js 的路径 - */ - private static final String COMMON_JS_ILE_PATH = "support/http/resources/js/common.js"; - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) - throws ServletException, IOException { - chain.doFilter(request, response); - // 重置缓冲区,响应头不会被重置 - response.resetBuffer(); - // 获取 common.js - String text = Utils.readFromResource(COMMON_JS_ILE_PATH); - // 正则替换 banner, 除去底部的广告信息 - text = text.replaceAll("<a.*?banner\"></a><br/>", ""); - text = text.replaceAll("powered.*?shrek.wang</a>", ""); - response.getWriter().write(text); - } - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/datasource/package-info.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/datasource/package-info.java deleted file mode 100644 index fa02238..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/datasource/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * 数据库连接池,采用 Druid - * 多数据源,采用爆米花 - */ -package com.iailab.framework.datasource; diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/config/IailabMybatisAutoConfiguration.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/config/IailabMybatisAutoConfiguration.java deleted file mode 100644 index 5ee2990..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/config/IailabMybatisAutoConfiguration.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.iailab.framework.mybatis.config; - -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.mybatis.core.enums.SqlConstants; -import com.iailab.framework.mybatis.core.handler.DefaultDBFieldHandler; -import com.baomidou.mybatisplus.annotation.DbType; -import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; -import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; -import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator; -import com.baomidou.mybatisplus.extension.incrementer.*; -import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; -import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; -import org.apache.ibatis.annotations.Mapper; -import org.mybatis.spring.annotation.MapperScan; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.core.env.ConfigurableEnvironment; - -/** - * MyBaits 配置类 - * - * @author iailab - */ -@AutoConfiguration(before = MybatisPlusAutoConfiguration.class) // 目的:先于 MyBatis Plus 自动配置,避免 @MapperScan 可能扫描不到 Mapper 打印 warn 日志 -@MapperScan(value = "${iailab.info.base-package}", annotationClass = Mapper.class, - lazyInitialization = "${mybatis.lazy-initialization:false}") // Mapper 懒加载,目前仅用于单元测试 -public class IailabMybatisAutoConfiguration { - - @Bean - public MybatisPlusInterceptor mybatisPlusInterceptor() { - MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); - mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); // 分页插件 - return mybatisPlusInterceptor; - } - - @Bean - public MetaObjectHandler defaultMetaObjectHandler(){ - return new DefaultDBFieldHandler(); // 自动填充参数类 - } - - @Bean - @ConditionalOnProperty(prefix = "mybatis-plus.global-config.db-config", name = "id-type", havingValue = "INPUT") - public IKeyGenerator keyGenerator(ConfigurableEnvironment environment) { - DbType dbType = IdTypeEnvironmentPostProcessor.getDbType(environment); - if (dbType != null) { - switch (dbType) { - case POSTGRE_SQL: - return new PostgreKeyGenerator(); - case ORACLE: - case ORACLE_12C: - return new OracleKeyGenerator(); - case H2: - return new H2KeyGenerator(); - case KINGBASE_ES: - return new KingbaseKeyGenerator(); - case DM: - return new DmKeyGenerator(); - } - } - // 找不到合适的 IKeyGenerator 实现类 - throw new IllegalArgumentException(StrUtil.format("DbType{} 找不到合适的 IKeyGenerator 实现类", dbType)); - } - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/config/IdTypeEnvironmentPostProcessor.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/config/IdTypeEnvironmentPostProcessor.java deleted file mode 100644 index 16ae754..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/config/IdTypeEnvironmentPostProcessor.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.iailab.framework.mybatis.config; - -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.util.collection.SetUtils; -import com.iailab.framework.mybatis.core.enums.SqlConstants; -import com.iailab.framework.mybatis.core.util.JdbcUtils; -import com.baomidou.mybatisplus.annotation.DbType; -import com.baomidou.mybatisplus.annotation.IdType; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.env.EnvironmentPostProcessor; -import org.springframework.core.env.ConfigurableEnvironment; - -import java.util.Set; - -/** - * 当 IdType 为 {@link IdType#NONE} 时,根据 PRIMARY 数据源所使用的数据库,自动设置 - * - * @author iailab - */ -@Slf4j -public class IdTypeEnvironmentPostProcessor implements EnvironmentPostProcessor { - - private static final String ID_TYPE_KEY = "mybatis-plus.global-config.db-config.id-type"; - - private static final String DATASOURCE_DYNAMIC_KEY = "spring.datasource.dynamic"; - - private static final String QUARTZ_JOB_STORE_DRIVER_KEY = "spring.quartz.properties.org.quartz.jobStore.driverDelegateClass"; - - private static final Set<DbType> INPUT_ID_TYPES = SetUtils.asSet(DbType.ORACLE, DbType.ORACLE_12C, - DbType.POSTGRE_SQL, DbType.KINGBASE_ES, DbType.DB2, DbType.H2); - - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { - // 如果获取不到 DbType,则不进行处理 - DbType dbType = getDbType(environment); - if (dbType == null) { - return; - } - - // 设置 Quartz JobStore 对应的 Driver - // TODO iailab:暂时没有找到特别合适的地方,先放在这里 - setJobStoreDriverIfPresent(environment, dbType); - // 初始化 SQL 静态变量 - SqlConstants.init(dbType); - // 如果非 NONE,则不进行处理 - IdType idType = getIdType(environment); - if (idType != IdType.NONE) { - return; - } - // 情况一,用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库 - if (INPUT_ID_TYPES.contains(dbType)) { - setIdType(environment, IdType.INPUT); - return; - } - // 情况二,自增 ID,适合 MySQL 等直接自增的数据库 - setIdType(environment, IdType.AUTO); - } - - public IdType getIdType(ConfigurableEnvironment environment) { - return environment.getProperty(ID_TYPE_KEY, IdType.class); - } - - public void setIdType(ConfigurableEnvironment environment, IdType idType) { - environment.getSystemProperties().put(ID_TYPE_KEY, idType); - log.info("[setIdType][修改 MyBatis Plus 的 idType 为({})]", idType); - } - - public void setJobStoreDriverIfPresent(ConfigurableEnvironment environment, DbType dbType) { - String driverClass = environment.getProperty(QUARTZ_JOB_STORE_DRIVER_KEY); - if (StrUtil.isNotEmpty(driverClass)) { - return; - } - // 根据 dbType 类型,获取对应的 driverClass - switch (dbType) { - case POSTGRE_SQL: - driverClass = "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate"; - break; - case ORACLE: - case ORACLE_12C: - driverClass = "org.quartz.impl.jdbcjobstore.oracle.OracleDelegate"; - break; - case SQL_SERVER: - case SQL_SERVER2005: - driverClass = "org.quartz.impl.jdbcjobstore.MSSQLDelegate"; - break; - } - // 设置 driverClass 变量 - if (StrUtil.isNotEmpty(driverClass)) { - environment.getSystemProperties().put(QUARTZ_JOB_STORE_DRIVER_KEY, driverClass); - } - } - - public static DbType getDbType(ConfigurableEnvironment environment) { - String primary = environment.getProperty(DATASOURCE_DYNAMIC_KEY + "." + "primary"); - if (StrUtil.isEmpty(primary)) { - return null; - } - String url = environment.getProperty(DATASOURCE_DYNAMIC_KEY + ".datasource." + primary + ".url"); - if (StrUtil.isEmpty(url)) { - return null; - } - return JdbcUtils.getDbType(url); - } - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/config/MyBatisConfiguration.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/config/MyBatisConfiguration.java deleted file mode 100644 index e841574..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/config/MyBatisConfiguration.java +++ /dev/null @@ -1,84 +0,0 @@ -//package com.iailab.framework.mybatis.config; -// -//import com.alibaba.fastjson.JSONArray; -//import com.alibaba.fastjson.JSONObject; -//import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; -//import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; -//import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; -//import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; -//import com.iailab.framework.mybatis.core.handler.MybatisHandler; -//import com.iailab.framework.mybatis.interceptor.DataFilterInterceptor; -//import org.apache.ibatis.session.SqlSessionFactory; -//import org.apache.ibatis.type.JdbcType; -//import org.mybatis.spring.SqlSessionFactoryBean; -//import org.mybatis.spring.SqlSessionTemplate; -//import org.springframework.beans.factory.annotation.Value; -//import org.springframework.boot.jdbc.DataSourceBuilder; -//import org.springframework.context.annotation.Bean; -//import org.springframework.context.annotation.Configuration; -//import org.springframework.context.annotation.Primary; -//import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -//import org.springframework.jdbc.datasource.DataSourceTransactionManager; -// -// -///** -// * @author houzhongjian -// * @Title: MyBatisConfiguration -// * @ProjectName design-parent -// * @Description: 解决独立启动某个服务时报错问题 No typehandler found for property sqlSessionTemplate -// * @date 2024/7/2 16:35 -// */ -//@Configuration -//public class MyBatisConfiguration { -// -// // 配置mapper的扫描,找到所有的mapper.xml映射文件 -//// @Value("${iailab.info.base-package}") -// @Value("${mybatis-plus.mapper-locations}") -// private String mapperLocations; -// -// @Bean -// @Primary -// public SqlSessionFactory sqlSessionFactory() throws Exception { -// SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); -// sqlSessionFactoryBean.setDataSource(DataSourceBuilder.create().build()); -// sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations)); -// sqlSessionFactoryBean.setConfiguration(buildConfiguration()); -// return sqlSessionFactoryBean.getObject(); -// } -// -// @Bean -// @Primary -// public SqlSessionTemplate sqlSessionTemplate() throws Exception { -// return new SqlSessionTemplate(sqlSessionFactory()); -// } -// -// @Bean -// @Primary -// public DataSourceTransactionManager transactionManager() { -// return new DataSourceTransactionManager(DataSourceBuilder.create().build()); -// } -// -// private org.apache.ibatis.session.Configuration buildConfiguration() { -// org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); -// configuration.getTypeHandlerRegistry().register(JSONObject.class, JdbcType.VARCHAR, MybatisHandler.class); -// configuration.getTypeHandlerRegistry().register(JSONArray.class, JdbcType.VARCHAR, MybatisHandler.class); -// return configuration; -// } -// -// @Bean -// public MybatisPlusInterceptor mybatisPlusInterceptor() { -// MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); -// // 数据权限 -// mybatisPlusInterceptor.addInnerInterceptor(new DataFilterInterceptor()); -// // 分页插件 -// mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); -// // 乐观锁 -// mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); -// // 防止全表更新与删除 -// mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); -// -// return mybatisPlusInterceptor; -// } -// -// -//} \ No newline at end of file diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/dataobject/BaseDO.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/dataobject/BaseDO.java deleted file mode 100644 index 8c97c70..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/dataobject/BaseDO.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.iailab.framework.mybatis.core.dataobject; - -import com.baomidou.mybatisplus.annotation.FieldFill; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableLogic; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fhs.core.trans.vo.TransPojo; -import lombok.Data; -import org.apache.ibatis.type.JdbcType; - -import java.io.Serializable; -import java.time.LocalDateTime; - -/** - * 基础实体对象 - * - * 为什么实现 {@link TransPojo} 接口? - * 因为使用 Easy-Trans TransType.SIMPLE 模式,集成 MyBatis Plus 查询 - * - * @author iailab - */ -@Data -@JsonIgnoreProperties(value = "transMap") // 由于 Easy-Trans 会添加 transMap 属性,避免 Jackson 在 Spring Cache 反序列化报错 -public abstract class BaseDO implements Serializable, TransPojo { - - /** - * 创建时间 - */ - @TableField(fill = FieldFill.INSERT) - private LocalDateTime createTime; - /** - * 最后更新时间 - */ - @TableField(fill = FieldFill.INSERT_UPDATE) - private LocalDateTime updateTime; - /** - * 创建者,目前使用 SysUser 的 id 编号 - * - * 使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。 - */ - @TableField(fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR) - private String creator; - /** - * 更新者,目前使用 SysUser 的 id 编号 - * - * 使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。 - */ - @TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.VARCHAR) - private String updater; - /** - * 是否删除 - */ - @TableLogic - private Boolean deleted; - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/enums/DbTypeEnum.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/enums/DbTypeEnum.java deleted file mode 100644 index 644d1e3..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/enums/DbTypeEnum.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.iailab.framework.mybatis.core.enums; - -import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.annotation.DbType; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; - -/** - * 针对 MyBatis Plus 的 {@link DbType} 增强,补充更多信息 - */ -@Getter -@AllArgsConstructor -public enum DbTypeEnum { - - /** - * MySQL - */ - MY_SQL( DbType.MYSQL, "MySQL", "FIND_IN_SET('#{value}', #{column}) <> 0"), - - /** - * Oracle - */ - ORACLE(DbType.ORACLE, "Oracle", "FIND_IN_SET('#{value}', #{column}) <> 0"), - - /** - * PostgreSQL - * - * 华为 openGauss 使用 ProductName 与 PostgreSQL 相同 - */ - POSTGRE_SQL(DbType.POSTGRE_SQL,"PostgreSQL", "POSITION('#{value}' IN #{column}) <> 0"), - - /** - * SQL Server - */ - SQL_SERVER(DbType.SQL_SERVER, "Microsoft SQL Server", "CHARINDEX(',' + #{value} + ',', ',' + #{column} + ',') <> 0"), - - /** - * 达梦 - */ - DM(DbType.DM, "DM DBMS", "FIND_IN_SET('#{value}', #{column}) <> 0"), - - /** - * 人大金仓 - */ - KINGBASE_ES(DbType.KINGBASE_ES, "KingbaseES", "POSITION('#{value}' IN #{column}) <> 0"), - ; - - public static final Map<String, DbTypeEnum> MAP_BY_NAME = Arrays.stream(values()) - .collect(Collectors.toMap(DbTypeEnum::getProductName, Function.identity())); - - public static final Map<DbType, DbTypeEnum> MAP_BY_MP = Arrays.stream(values()) - .collect(Collectors.toMap(DbTypeEnum::getMpDbType, Function.identity())); - - /** - * MyBatis Plus 类型 - */ - private final DbType mpDbType; - /** - * 数据库产品名 - */ - private final String productName; - /** - * SQL FIND_IN_SET 模板 - */ - private final String findInSetTemplate; - - public static DbType find(String databaseProductName) { - if (StrUtil.isBlank(databaseProductName)) { - return null; - } - return MAP_BY_NAME.get(databaseProductName).getMpDbType(); - } - - public static String getFindInSetTemplate(DbType dbType) { - return Optional.of(MAP_BY_MP.get(dbType).getFindInSetTemplate()) - .orElseThrow(() -> new IllegalArgumentException("FIND_IN_SET not supported")); - } -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/enums/SqlConstants.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/enums/SqlConstants.java deleted file mode 100644 index 5ba0046..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/enums/SqlConstants.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iailab.framework.mybatis.core.enums; - -import com.baomidou.mybatisplus.annotation.DbType; - -/** - * SQL相关常量类 - * - * @author iailab - */ -public class SqlConstants { - - /** - * 数据库的类型 - */ - // TODO 此处使用nacos配置文件的话,先初始化nacos配置才初始化dbType,导致读不到值。暂时先固定Mysql - public static DbType DB_TYPE = DbType.MYSQL; - - public static void init(DbType dbType) { - DB_TYPE = dbType; - } - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/handler/DefaultDBFieldHandler.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/handler/DefaultDBFieldHandler.java deleted file mode 100644 index f047f87..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/handler/DefaultDBFieldHandler.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.iailab.framework.mybatis.core.handler; - -import com.iailab.framework.mybatis.core.dataobject.BaseDO; -import com.iailab.framework.web.core.util.WebFrameworkUtils; -import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; -import org.apache.ibatis.reflection.MetaObject; - -import java.time.LocalDateTime; -import java.util.Objects; - -/** - * 通用参数填充实现类 - * - * 如果没有显式的对通用参数进行赋值,这里会对通用参数进行填充、赋值 - * - * @author hexiaowu - */ -public class DefaultDBFieldHandler implements MetaObjectHandler { - - @Override - public void insertFill(MetaObject metaObject) { - if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) { - BaseDO baseDO = (BaseDO) metaObject.getOriginalObject(); - - LocalDateTime current = LocalDateTime.now(); - // 创建时间为空,则以当前时间为插入时间 - if (Objects.isNull(baseDO.getCreateTime())) { - baseDO.setCreateTime(current); - } - // 更新时间为空,则以当前时间为更新时间 - if (Objects.isNull(baseDO.getUpdateTime())) { - baseDO.setUpdateTime(current); - } - - Long userId = WebFrameworkUtils.getLoginUserId(); - // 当前登录用户不为空,创建人为空,则当前登录用户为创建人 - if (Objects.nonNull(userId) && Objects.isNull(baseDO.getCreator())) { - baseDO.setCreator(userId.toString()); - } - // 当前登录用户不为空,更新人为空,则当前登录用户为更新人 - if (Objects.nonNull(userId) && Objects.isNull(baseDO.getUpdater())) { - baseDO.setUpdater(userId.toString()); - } - } - } - - @Override - public void updateFill(MetaObject metaObject) { - // 更新时间为空,则以当前时间为更新时间 - Object modifyTime = getFieldValByName("updateTime", metaObject); - if (Objects.isNull(modifyTime)) { - setFieldValByName("updateTime", LocalDateTime.now(), metaObject); - } - - // 当前登录用户不为空,更新人为空,则当前登录用户为更新人 - Object modifier = getFieldValByName("updater", metaObject); - Long userId = WebFrameworkUtils.getLoginUserId(); - if (Objects.nonNull(userId) && Objects.isNull(modifier)) { - setFieldValByName("updater", userId.toString(), metaObject); - } - } -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/handler/MybatisHandler.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/handler/MybatisHandler.java deleted file mode 100644 index c7653fe..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/handler/MybatisHandler.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.iailab.framework.mybatis.core.handler; - -import com.alibaba.fastjson.JSON; -import org.apache.ibatis.type.BaseTypeHandler; -import org.apache.ibatis.type.JdbcType; - -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; - -public class MybatisHandler extends BaseTypeHandler<Object> { - - @Override - public void setNonNullParameter(PreparedStatement preparedStatement, int i, Object o, JdbcType jdbcType) throws SQLException { - preparedStatement.setString(i, JSON.toJSONString(o)); - } - - @Override - public Object getNullableResult(ResultSet resultSet, String s) throws SQLException { - String sqlJson = resultSet.getString(s); - if (null != sqlJson) { - return JSON.parse(sqlJson); - } - return null; - } - - @Override - public Object getNullableResult(ResultSet resultSet, int i) throws SQLException { - String sqlJson = resultSet.getString(i); - if (null != sqlJson) { - return JSON.parse(sqlJson); - } - return null; - } - - @Override - public Object getNullableResult(CallableStatement callableStatement, int i) throws SQLException { - String sqlJson = callableStatement.getString(i); - if (null != sqlJson) { - return JSON.parse(sqlJson); - } - return null; - } -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/mapper/BaseMapperX.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/mapper/BaseMapperX.java deleted file mode 100644 index ccf2c77..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/mapper/BaseMapperX.java +++ /dev/null @@ -1,225 +0,0 @@ -package com.iailab.framework.mybatis.core.mapper; - -import cn.hutool.core.collection.CollUtil; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.iailab.framework.common.pojo.PageParam; -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.common.pojo.SortablePageParam; -import com.iailab.framework.common.pojo.SortingField; -import com.iailab.framework.mybatis.core.enums.SqlConstants; -import com.iailab.framework.mybatis.core.util.MyBatisUtils; -import com.baomidou.mybatisplus.annotation.DbType; -import com.baomidou.mybatisplus.core.conditions.Wrapper; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.core.toolkit.support.SFunction; -import com.baomidou.mybatisplus.extension.toolkit.Db; -import com.github.yulichang.base.MPJBaseMapper; -import com.github.yulichang.interfaces.MPJBaseJoin; -import com.github.yulichang.wrapper.MPJLambdaWrapper; -import org.apache.ibatis.annotations.Param; - -import java.util.Collection; -import java.util.List; -import java.util.Objects; - -/** - * 在 MyBatis Plus 的 BaseMapper 的基础上拓展,提供更多的能力 - * - * 1. {@link BaseMapper} 为 MyBatis Plus 的基础接口,提供基础的 CRUD 能力 - * 2. {@link MPJBaseMapper} 为 MyBatis Plus Join 的基础接口,提供连表 Join 能力 - */ -public interface BaseMapperX<T> extends MPJBaseMapper<T> { - - /** - * 获取分页对象 - * @param params 分页查询参数 - */ - default IPage<T> getPage(PageParam params) { - //分页参数 - long curPage = 1; - long limit = 10; - - if(params.getPageNo() != null){ - curPage = params.getPageNo(); - } - if(params.getPageSize() != null){ - limit = params.getPageSize(); - } - - //分页对象 - return new Page<>(curPage, limit); - } - - default PageResult<T> selectPage(SortablePageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) { - return selectPage(pageParam, pageParam.getSortingFields(), queryWrapper); - } - - default PageResult<T> selectPage(PageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) { - return selectPage(pageParam, null, queryWrapper); - } - - default PageResult<T> selectPage(PageParam pageParam, Collection<SortingField> sortingFields, @Param("ew") Wrapper<T> queryWrapper) { - // 特殊:不分页,直接查询全部 - if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) { - List<T> list = selectList(queryWrapper); - return new PageResult<>(list, (long) list.size()); - } - - // MyBatis Plus 查询 - IPage<T> mpPage = MyBatisUtils.buildPage(pageParam, sortingFields); - selectPage(mpPage, queryWrapper); - // 转换返回 - return new PageResult<>(mpPage.getRecords(), mpPage.getTotal()); - } - - default <D> PageResult<D> selectJoinPage(PageParam pageParam, Class<D> clazz, MPJLambdaWrapper<T> lambdaWrapper) { - // 特殊:不分页,直接查询全部 - if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) { - List<D> list = selectJoinList(clazz, lambdaWrapper); - return new PageResult<>(list, (long) list.size()); - } - - // MyBatis Plus Join 查询 - IPage<D> mpPage = MyBatisUtils.buildPage(pageParam); - mpPage = selectJoinPage(mpPage, clazz, lambdaWrapper); - // 转换返回 - return new PageResult<>(mpPage.getRecords(), mpPage.getTotal()); - } - - default <DTO> PageResult<DTO> selectJoinPage(PageParam pageParam, Class<DTO> resultTypeClass, MPJBaseJoin<T> joinQueryWrapper) { - IPage<DTO> mpPage = MyBatisUtils.buildPage(pageParam); - selectJoinPage(mpPage, resultTypeClass, joinQueryWrapper); - // 转换返回 - return new PageResult<>(mpPage.getRecords(), mpPage.getTotal()); - } - - default T selectOne(String field, Object value) { - return selectOne(new QueryWrapper<T>().eq(field, value)); - } - - default T selectOne(SFunction<T, ?> field, Object value) { - return selectOne(new LambdaQueryWrapper<T>().eq(field, value)); - } - - default T selectOne(String field1, Object value1, String field2, Object value2) { - return selectOne(new QueryWrapper<T>().eq(field1, value1).eq(field2, value2)); - } - - default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) { - return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)); - } - - default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2, - SFunction<T, ?> field3, Object value3) { - return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2) - .eq(field3, value3)); - } - - default Long selectCount() { - return selectCount(new QueryWrapper<>()); - } - - default Long selectCount(String field, Object value) { - return selectCount(new QueryWrapper<T>().eq(field, value)); - } - - default Long selectCount(SFunction<T, ?> field, Object value) { - return selectCount(new LambdaQueryWrapper<T>().eq(field, value)); - } - - default List<T> selectList() { - return selectList(new QueryWrapper<>()); - } - - default List<T> selectList(String field, Object value) { - return selectList(new QueryWrapper<T>().eq(field, value)); - } - - default List<T> selectList(SFunction<T, ?> field, Object value) { - return selectList(new LambdaQueryWrapper<T>().eq(field, value)); - } - - default List<T> selectList(String field, Collection<?> values) { - if (CollUtil.isEmpty(values)) { - return CollUtil.newArrayList(); - } - return selectList(new QueryWrapper<T>().in(field, values)); - } - - default List<T> selectList(SFunction<T, ?> field, Collection<?> values) { - if (CollUtil.isEmpty(values)) { - return CollUtil.newArrayList(); - } - return selectList(new LambdaQueryWrapper<T>().in(field, values)); - } - - @Deprecated - default List<T> selectList(SFunction<T, ?> leField, SFunction<T, ?> geField, Object value) { - return selectList(new LambdaQueryWrapper<T>().le(leField, value).ge(geField, value)); - } - - default List<T> selectList(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) { - return selectList(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)); - } - - /** - * 批量插入,适合大量数据插入 - * - * @param entities 实体们 - */ - default Boolean insertBatch(Collection<T> entities) { - // 特殊:SQL Server 批量插入后,获取 id 会报错,因此通过循环处理 - if (Objects.equals(SqlConstants.DB_TYPE, DbType.SQL_SERVER)) { - entities.forEach(this::insert); - return CollUtil.isNotEmpty(entities); - } - return Db.saveBatch(entities); - } - - /** - * 批量插入,适合大量数据插入 - * - * @param entities 实体们 - * @param size 插入数量 Db.saveBatch 默认为 1000 - */ - default Boolean insertBatch(Collection<T> entities, int size) { - // 特殊:SQL Server 批量插入后,获取 id 会报错,因此通过循环处理 - if (Objects.equals(SqlConstants.DB_TYPE, DbType.SQL_SERVER)) { - entities.forEach(this::insert); - return CollUtil.isNotEmpty(entities); - } - return Db.saveBatch(entities, size); - } - - default int updateBatch(T update) { - return update(update, new QueryWrapper<>()); - } - - default Boolean updateBatch(Collection<T> entities) { - return Db.updateBatchById(entities); - } - - default Boolean updateBatch(Collection<T> entities, int size) { - return Db.updateBatchById(entities, size); - } - - default boolean insertOrUpdate(T entity) { - return Db.saveOrUpdate(entity); - } - - default Boolean insertOrUpdateBatch(Collection<T> collection) { - return Db.saveOrUpdateBatch(collection); - } - - default int delete(String field, String value) { - return delete(new QueryWrapper<T>().eq(field, value)); - } - - default int delete(SFunction<T, ?> field, Object value) { - return delete(new LambdaQueryWrapper<T>().eq(field, value)); - } - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/query/LambdaQueryWrapperX.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/query/LambdaQueryWrapperX.java deleted file mode 100644 index cfca5c9..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/query/LambdaQueryWrapperX.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.iailab.framework.mybatis.core.query; - -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.ObjectUtil; -import com.iailab.framework.common.util.collection.ArrayUtils; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.toolkit.support.SFunction; -import org.springframework.util.StringUtils; - -import java.util.Collection; - -/** - * 拓展 MyBatis Plus QueryWrapper 类,主要增加如下功能: - * <p> - * 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。 - * - * @param <T> 数据类型 - */ -public class LambdaQueryWrapperX<T> extends LambdaQueryWrapper<T> { - - public LambdaQueryWrapperX<T> likeIfPresent(SFunction<T, ?> column, String val) { - if (StringUtils.hasText(val)) { - return (LambdaQueryWrapperX<T>) super.like(column, val); - } - return this; - } - - public LambdaQueryWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) { - if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) { - return (LambdaQueryWrapperX<T>) super.in(column, values); - } - return this; - } - - public LambdaQueryWrapperX<T> inIfPresent(SFunction<T, ?> column, Object... values) { - if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) { - return (LambdaQueryWrapperX<T>) super.in(column, values); - } - return this; - } - - public LambdaQueryWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) { - if (ObjectUtil.isNotEmpty(val)) { - return (LambdaQueryWrapperX<T>) super.eq(column, val); - } - return this; - } - - public LambdaQueryWrapperX<T> neIfPresent(SFunction<T, ?> column, Object val) { - if (ObjectUtil.isNotEmpty(val)) { - return (LambdaQueryWrapperX<T>) super.ne(column, val); - } - return this; - } - - public LambdaQueryWrapperX<T> gtIfPresent(SFunction<T, ?> column, Object val) { - if (val != null) { - return (LambdaQueryWrapperX<T>) super.gt(column, val); - } - return this; - } - - public LambdaQueryWrapperX<T> geIfPresent(SFunction<T, ?> column, Object val) { - if (val != null) { - return (LambdaQueryWrapperX<T>) super.ge(column, val); - } - return this; - } - - public LambdaQueryWrapperX<T> ltIfPresent(SFunction<T, ?> column, Object val) { - if (val != null) { - return (LambdaQueryWrapperX<T>) super.lt(column, val); - } - return this; - } - - public LambdaQueryWrapperX<T> leIfPresent(SFunction<T, ?> column, Object val) { - if (val != null) { - return (LambdaQueryWrapperX<T>) super.le(column, val); - } - return this; - } - - public LambdaQueryWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object val1, Object val2) { - if (val1 != null && val2 != null) { - return (LambdaQueryWrapperX<T>) super.between(column, val1, val2); - } - if (val1 != null) { - return (LambdaQueryWrapperX<T>) ge(column, val1); - } - if (val2 != null) { - return (LambdaQueryWrapperX<T>) le(column, val2); - } - return this; - } - - public LambdaQueryWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object[] values) { - Object val1 = ArrayUtils.get(values, 0); - Object val2 = ArrayUtils.get(values, 1); - return betweenIfPresent(column, val1, val2); - } - - // ========== 重写父类方法,方便链式调用 ========== - - @Override - public LambdaQueryWrapperX<T> eq(boolean condition, SFunction<T, ?> column, Object val) { - super.eq(condition, column, val); - return this; - } - - @Override - public LambdaQueryWrapperX<T> eq(SFunction<T, ?> column, Object val) { - super.eq(column, val); - return this; - } - - @Override - public LambdaQueryWrapperX<T> orderByDesc(SFunction<T, ?> column) { - super.orderByDesc(true, column); - return this; - } - - @Override - public LambdaQueryWrapperX<T> last(String lastSql) { - super.last(lastSql); - return this; - } - - @Override - public LambdaQueryWrapperX<T> in(SFunction<T, ?> column, Collection<?> coll) { - super.in(column, coll); - return this; - } - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/query/MPJLambdaWrapperX.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/query/MPJLambdaWrapperX.java deleted file mode 100644 index fa8ebc7..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/query/MPJLambdaWrapperX.java +++ /dev/null @@ -1,313 +0,0 @@ -package com.iailab.framework.mybatis.core.query; - -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.ObjectUtil; -import com.iailab.framework.common.util.collection.ArrayUtils; -import com.baomidou.mybatisplus.core.toolkit.support.SFunction; -import com.github.yulichang.toolkit.MPJWrappers; -import com.github.yulichang.wrapper.MPJLambdaWrapper; -import org.springframework.util.StringUtils; - -import java.util.Collection; -import java.util.function.Consumer; - -/** - * 拓展 MyBatis Plus Join QueryWrapper 类,主要增加如下功能: - * <p> - * 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。 - * - * @param <T> 数据类型 - */ -public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { - - public MPJLambdaWrapperX<T> likeIfPresent(SFunction<T, ?> column, String val) { - MPJWrappers.lambdaJoin().like(column, val); - if (StringUtils.hasText(val)) { - return (MPJLambdaWrapperX<T>) super.like(column, val); - } - return this; - } - - public MPJLambdaWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) { - if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) { - return (MPJLambdaWrapperX<T>) super.in(column, values); - } - return this; - } - - public MPJLambdaWrapperX<T> inIfPresent(SFunction<T, ?> column, Object... values) { - if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) { - return (MPJLambdaWrapperX<T>) super.in(column, values); - } - return this; - } - - public MPJLambdaWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) { - if (ObjectUtil.isNotEmpty(val)) { - return (MPJLambdaWrapperX<T>) super.eq(column, val); - } - return this; - } - - public MPJLambdaWrapperX<T> neIfPresent(SFunction<T, ?> column, Object val) { - if (ObjectUtil.isNotEmpty(val)) { - return (MPJLambdaWrapperX<T>) super.ne(column, val); - } - return this; - } - - public MPJLambdaWrapperX<T> gtIfPresent(SFunction<T, ?> column, Object val) { - if (val != null) { - return (MPJLambdaWrapperX<T>) super.gt(column, val); - } - return this; - } - - public MPJLambdaWrapperX<T> geIfPresent(SFunction<T, ?> column, Object val) { - if (val != null) { - return (MPJLambdaWrapperX<T>) super.ge(column, val); - } - return this; - } - - public MPJLambdaWrapperX<T> ltIfPresent(SFunction<T, ?> column, Object val) { - if (val != null) { - return (MPJLambdaWrapperX<T>) super.lt(column, val); - } - return this; - } - - public MPJLambdaWrapperX<T> leIfPresent(SFunction<T, ?> column, Object val) { - if (val != null) { - return (MPJLambdaWrapperX<T>) super.le(column, val); - } - return this; - } - - public MPJLambdaWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object val1, Object val2) { - if (val1 != null && val2 != null) { - return (MPJLambdaWrapperX<T>) super.between(column, val1, val2); - } - if (val1 != null) { - return (MPJLambdaWrapperX<T>) ge(column, val1); - } - if (val2 != null) { - return (MPJLambdaWrapperX<T>) le(column, val2); - } - return this; - } - - public MPJLambdaWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object[] values) { - Object val1 = ArrayUtils.get(values, 0); - Object val2 = ArrayUtils.get(values, 1); - return betweenIfPresent(column, val1, val2); - } - - // ========== 重写父类方法,方便链式调用 ========== - - @Override - public <X> MPJLambdaWrapperX<T> eq(boolean condition, SFunction<X, ?> column, Object val) { - super.eq(condition, column, val); - return this; - } - - @Override - public <X> MPJLambdaWrapperX<T> eq(SFunction<X, ?> column, Object val) { - super.eq(column, val); - return this; - } - - @Override - public <X> MPJLambdaWrapperX<T> orderByDesc(SFunction<X, ?> column) { - //noinspection unchecked - super.orderByDesc(true, column); - return this; - } - - @Override - public MPJLambdaWrapperX<T> last(String lastSql) { - super.last(lastSql); - return this; - } - - @Override - public <X> MPJLambdaWrapperX<T> in(SFunction<X, ?> column, Collection<?> coll) { - super.in(column, coll); - return this; - } - - @Override - public MPJLambdaWrapperX<T> selectAll(Class<?> clazz) { - super.selectAll(clazz); - return this; - } - - @Override - public MPJLambdaWrapperX<T> selectAll(Class<?> clazz, String prefix) { - super.selectAll(clazz, prefix); - return this; - } - - @Override - public <S> MPJLambdaWrapperX<T> selectAs(SFunction<S, ?> column, String alias) { - super.selectAs(column, alias); - return this; - } - - @Override - public <E> MPJLambdaWrapperX<T> selectAs(String column, SFunction<E, ?> alias) { - super.selectAs(column, alias); - return this; - } - - @Override - public <S, X> MPJLambdaWrapperX<T> selectAs(SFunction<S, ?> column, SFunction<X, ?> alias) { - super.selectAs(column, alias); - return this; - } - - @Override - public <E, X> MPJLambdaWrapperX<T> selectAs(String index, SFunction<E, ?> column, SFunction<X, ?> alias) { - super.selectAs(index, column, alias); - return this; - } - - @Override - public <E> MPJLambdaWrapperX<T> selectAsClass(Class<E> source, Class<?> tag) { - super.selectAsClass(source, tag); - return this; - } - - @Override - public <E, F> MPJLambdaWrapperX<T> selectSub(Class<E> clazz, Consumer<MPJLambdaWrapper<E>> consumer, SFunction<F, ?> alias) { - super.selectSub(clazz, consumer, alias); - return this; - } - - @Override - public <E, F> MPJLambdaWrapperX<T> selectSub(Class<E> clazz, String st, Consumer<MPJLambdaWrapper<E>> consumer, SFunction<F, ?> alias) { - super.selectSub(clazz, st, consumer, alias); - return this; - } - - @Override - public <S> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column) { - super.selectCount(column); - return this; - } - - @Override - public MPJLambdaWrapperX<T> selectCount(Object column, String alias) { - super.selectCount(column, alias); - return this; - } - - @Override - public <X> MPJLambdaWrapperX<T> selectCount(Object column, SFunction<X, ?> alias) { - super.selectCount(column, alias); - return this; - } - - @Override - public <S, X> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column, String alias) { - super.selectCount(column, alias); - return this; - } - - @Override - public <S, X> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column, SFunction<X, ?> alias) { - super.selectCount(column, alias); - return this; - } - - @Override - public <S> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column) { - super.selectSum(column); - return this; - } - - @Override - public <S, X> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column, String alias) { - super.selectSum(column, alias); - return this; - } - - @Override - public <S, X> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column, SFunction<X, ?> alias) { - super.selectSum(column, alias); - return this; - } - - @Override - public <S> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column) { - super.selectMax(column); - return this; - } - - @Override - public <S, X> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column, String alias) { - super.selectMax(column, alias); - return this; - } - - @Override - public <S, X> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column, SFunction<X, ?> alias) { - super.selectMax(column, alias); - return this; - } - - @Override - public <S> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column) { - super.selectMin(column); - return this; - } - - @Override - public <S, X> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column, String alias) { - super.selectMin(column, alias); - return this; - } - - @Override - public <S, X> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column, SFunction<X, ?> alias) { - super.selectMin(column, alias); - return this; - } - - @Override - public <S> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column) { - super.selectAvg(column); - return this; - } - - @Override - public <S, X> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column, String alias) { - super.selectAvg(column, alias); - return this; - } - - @Override - public <S, X> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column, SFunction<X, ?> alias) { - super.selectAvg(column, alias); - return this; - } - - @Override - public <S> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column) { - super.selectLen(column); - return this; - } - - @Override - public <S, X> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column, String alias) { - super.selectLen(column, alias); - return this; - } - - @Override - public <S, X> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column, SFunction<X, ?> alias) { - super.selectLen(column, alias); - return this; - } - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/query/QueryWrapperX.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/query/QueryWrapperX.java deleted file mode 100644 index bf79bf5..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/query/QueryWrapperX.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.iailab.framework.mybatis.core.query; - -import cn.hutool.core.lang.Assert; -import com.iailab.framework.mybatis.core.enums.SqlConstants; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.toolkit.ArrayUtils; -import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; -import org.springframework.util.StringUtils; - -import java.util.Collection; - -/** - * 拓展 MyBatis Plus QueryWrapper 类,主要增加如下功能: - * - * 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。 - * - * @param <T> 数据类型 - */ -public class QueryWrapperX<T> extends QueryWrapper<T> { - - public QueryWrapperX<T> likeIfPresent(String column, String val) { - if (StringUtils.hasText(val)) { - return (QueryWrapperX<T>) super.like(column, val); - } - return this; - } - - public QueryWrapperX<T> inIfPresent(String column, Collection<?> values) { - if (!CollectionUtils.isEmpty(values)) { - return (QueryWrapperX<T>) super.in(column, values); - } - return this; - } - - public QueryWrapperX<T> inIfPresent(String column, Object... values) { - if (!ArrayUtils.isEmpty(values)) { - return (QueryWrapperX<T>) super.in(column, values); - } - return this; - } - - public QueryWrapperX<T> eqIfPresent(String column, Object val) { - if (val != null) { - return (QueryWrapperX<T>) super.eq(column, val); - } - return this; - } - - public QueryWrapperX<T> neIfPresent(String column, Object val) { - if (val != null) { - return (QueryWrapperX<T>) super.ne(column, val); - } - return this; - } - - public QueryWrapperX<T> gtIfPresent(String column, Object val) { - if (val != null) { - return (QueryWrapperX<T>) super.gt(column, val); - } - return this; - } - - public QueryWrapperX<T> geIfPresent(String column, Object val) { - if (val != null) { - return (QueryWrapperX<T>) super.ge(column, val); - } - return this; - } - - public QueryWrapperX<T> ltIfPresent(String column, Object val) { - if (val != null) { - return (QueryWrapperX<T>) super.lt(column, val); - } - return this; - } - - public QueryWrapperX<T> leIfPresent(String column, Object val) { - if (val != null) { - return (QueryWrapperX<T>) super.le(column, val); - } - return this; - } - - public QueryWrapperX<T> betweenIfPresent(String column, Object val1, Object val2) { - if (val1 != null && val2 != null) { - return (QueryWrapperX<T>) super.between(column, val1, val2); - } - if (val1 != null) { - return (QueryWrapperX<T>) ge(column, val1); - } - if (val2 != null) { - return (QueryWrapperX<T>) le(column, val2); - } - return this; - } - - public QueryWrapperX<T> betweenIfPresent(String column, Object[] values) { - if (values!= null && values.length != 0 && values[0] != null && values[1] != null) { - return (QueryWrapperX<T>) super.between(column, values[0], values[1]); - } - if (values!= null && values.length != 0 && values[0] != null) { - return (QueryWrapperX<T>) ge(column, values[0]); - } - if (values!= null && values.length != 0 && values[1] != null) { - return (QueryWrapperX<T>) le(column, values[1]); - } - return this; - } - - // ========== 重写父类方法,方便链式调用 ========== - - @Override - public QueryWrapperX<T> eq(boolean condition, String column, Object val) { - super.eq(condition, column, val); - return this; - } - - @Override - public QueryWrapperX<T> eq(String column, Object val) { - super.eq(column, val); - return this; - } - - @Override - public QueryWrapperX<T> orderByDesc(String column) { - super.orderByDesc(true, column); - return this; - } - - @Override - public QueryWrapperX<T> last(String lastSql) { - super.last(lastSql); - return this; - } - - @Override - public QueryWrapperX<T> in(String column, Collection<?> coll) { - super.in(column, coll); - return this; - } - - /** - * 设置只返回最后一条 - * - * TODO iailab:不是完美解,需要在思考下。如果使用多数据源,并且数据源是多种类型时,可能会存在问题:实现之返回一条的语法不同 - * - * @return this - */ - public QueryWrapperX<T> limitN(int n) { - Assert.notNull(SqlConstants.DB_TYPE, "获取不到数据库的类型"); - switch (SqlConstants.DB_TYPE) { - case ORACLE: - case ORACLE_12C: - super.le("ROWNUM", n); - break; - case SQL_SERVER: - case SQL_SERVER2005: - super.select("TOP " + n + " *"); // 由于 SQL Server 是通过 SELECT TOP 1 实现限制一条,所以只好使用 * 查询剩余字段 - break; - default: - super.last("LIMIT " + n); - } - return this; - } - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/EncryptTypeHandler.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/EncryptTypeHandler.java deleted file mode 100644 index 57f92e2..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/EncryptTypeHandler.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.iailab.framework.mybatis.core.type; - -import cn.hutool.core.lang.Assert; -import cn.hutool.crypto.SecureUtil; -import cn.hutool.crypto.symmetric.AES; -import cn.hutool.extra.spring.SpringUtil; -import org.apache.ibatis.type.BaseTypeHandler; -import org.apache.ibatis.type.JdbcType; - -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; - -/** - * 字段字段的 TypeHandler 实现类,基于 {@link cn.hutool.crypto.symmetric.AES} 实现 - * 可通过 jasypt.encryptor.password 配置项,设置密钥 - * - * @author iailab - */ -public class EncryptTypeHandler extends BaseTypeHandler<String> { - - private static final String ENCRYPTOR_PROPERTY_NAME = "mybatis-plus.encryptor.password"; - - private static AES aes; - - @Override - public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { - ps.setString(i, encrypt(parameter)); - } - - @Override - public String getNullableResult(ResultSet rs, String columnName) throws SQLException { - String value = rs.getString(columnName); - return decrypt(value); - } - - @Override - public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { - String value = rs.getString(columnIndex); - return decrypt(value); - } - - @Override - public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { - String value = cs.getString(columnIndex); - return decrypt(value); - } - - private static String decrypt(String value) { - if (value == null) { - return null; - } - return getEncryptor().decryptStr(value); - } - - public static String encrypt(String rawValue) { - if (rawValue == null) { - return null; - } - return getEncryptor().encryptBase64(rawValue); - } - - private static AES getEncryptor() { - if (aes != null) { - return aes; - } - // 构建 AES - String password = SpringUtil.getProperty(ENCRYPTOR_PROPERTY_NAME); - Assert.notEmpty(password, "配置项({}) 不能为空", ENCRYPTOR_PROPERTY_NAME); - aes = SecureUtil.aes(password.getBytes()); - return aes; - } - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/IntegerListTypeHandler.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/IntegerListTypeHandler.java deleted file mode 100644 index 11041bc..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/IntegerListTypeHandler.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.iailab.framework.mybatis.core.type; - -import cn.hutool.core.collection.CollUtil; -import com.iailab.framework.common.util.string.StrUtils; -import org.apache.ibatis.type.JdbcType; -import org.apache.ibatis.type.MappedJdbcTypes; -import org.apache.ibatis.type.MappedTypes; -import org.apache.ibatis.type.TypeHandler; - -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; - -/** - * List<Integer> 的类型转换器实现类,对应数据库的 varchar 类型 - * - * @author jason - */ -@MappedJdbcTypes(JdbcType.VARCHAR) -@MappedTypes(List.class) -public class IntegerListTypeHandler implements TypeHandler<List<Integer>> { - - private static final String COMMA = ","; - - @Override - public void setParameter(PreparedStatement ps, int i, List<Integer> strings, JdbcType jdbcType) throws SQLException { - ps.setString(i, CollUtil.join(strings, COMMA)); - } - - @Override - public List<Integer> getResult(ResultSet rs, String columnName) throws SQLException { - String value = rs.getString(columnName); - return getResult(value); - } - - @Override - public List<Integer> getResult(ResultSet rs, int columnIndex) throws SQLException { - String value = rs.getString(columnIndex); - return getResult(value); - } - - @Override - public List<Integer> getResult(CallableStatement cs, int columnIndex) throws SQLException { - String value = cs.getString(columnIndex); - return getResult(value); - } - - private List<Integer> getResult(String value) { - if (value == null) { - return null; - } - return StrUtils.splitToInteger(value, COMMA); - } -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/JsonLongSetTypeHandler.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/JsonLongSetTypeHandler.java deleted file mode 100644 index cce6ac8..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/JsonLongSetTypeHandler.java +++ /dev/null @@ -1,31 +0,0 @@ -//package com.iailab.framework.mybatis.core.type; -// -//import com.iailab.framework.common.util.json.JsonUtils; -//import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; -//import com.fasterxml.jackson.core.type.TypeReference; -// -//import java.util.Set; -// -///** -// * 参考 {@link com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler} 实现 -// * 在我们将字符串反序列化为 Set 并且泛型为 Long 时,如果每个元素的数值太小,会被处理成 Integer 类型,导致可能存在隐性的 BUG。 -// * -// * 例如说哦,SysUserDO 的 postIds 属性 -// * -// * @author iailab -// */ -//public class JsonLongSetTypeHandler extends AbstractJsonTypeHandler<Object> { -// -// private static final TypeReference<Set<Long>> TYPE_REFERENCE = new TypeReference<Set<Long>>(){}; -// -// @Override -// protected Object parse(String json) { -// return JsonUtils.parseObject(json, TYPE_REFERENCE); -// } -// -// @Override -// protected String toJson(Object obj) { -// return JsonUtils.toJsonString(obj); -// } -// -//} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/LongListTypeHandler.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/LongListTypeHandler.java deleted file mode 100644 index 4bbd179..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/LongListTypeHandler.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.iailab.framework.mybatis.core.type; - -import cn.hutool.core.collection.CollUtil; -import com.iailab.framework.common.util.string.StrUtils; -import org.apache.ibatis.type.JdbcType; -import org.apache.ibatis.type.MappedJdbcTypes; -import org.apache.ibatis.type.MappedTypes; -import org.apache.ibatis.type.TypeHandler; - -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; - -/** - * List<Long> 的类型转换器实现类,对应数据库的 varchar 类型 - * - * @author iailab - */ -@MappedJdbcTypes(JdbcType.VARCHAR) -@MappedTypes(List.class) -public class LongListTypeHandler implements TypeHandler<List<Long>> { - - private static final String COMMA = ","; - - @Override - public void setParameter(PreparedStatement ps, int i, List<Long> strings, JdbcType jdbcType) throws SQLException { - // 设置占位符 - ps.setString(i, CollUtil.join(strings, COMMA)); - } - - @Override - public List<Long> getResult(ResultSet rs, String columnName) throws SQLException { - String value = rs.getString(columnName); - return getResult(value); - } - - @Override - public List<Long> getResult(ResultSet rs, int columnIndex) throws SQLException { - String value = rs.getString(columnIndex); - return getResult(value); - } - - @Override - public List<Long> getResult(CallableStatement cs, int columnIndex) throws SQLException { - String value = cs.getString(columnIndex); - return getResult(value); - } - - private List<Long> getResult(String value) { - if (value == null) { - return null; - } - return StrUtils.splitToLong(value, COMMA); - } -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/StringListTypeHandler.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/StringListTypeHandler.java deleted file mode 100644 index 024ffc0..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/type/StringListTypeHandler.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.iailab.framework.mybatis.core.type; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import org.apache.ibatis.type.JdbcType; -import org.apache.ibatis.type.MappedJdbcTypes; -import org.apache.ibatis.type.MappedTypes; -import org.apache.ibatis.type.TypeHandler; - -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; - -/** - * List<String> 的类型转换器实现类,对应数据库的 varchar 类型 - * - * @author 永不言败 - * @since 2022 3/23 12:50:15 - */ -@MappedJdbcTypes(JdbcType.VARCHAR) -@MappedTypes(List.class) -public class StringListTypeHandler implements TypeHandler<List<String>> { - - private static final String COMMA = ","; - - @Override - public void setParameter(PreparedStatement ps, int i, List<String> strings, JdbcType jdbcType) throws SQLException { - // 设置占位符 - ps.setString(i, CollUtil.join(strings, COMMA)); - } - - @Override - public List<String> getResult(ResultSet rs, String columnName) throws SQLException { - String value = rs.getString(columnName); - return getResult(value); - } - - @Override - public List<String> getResult(ResultSet rs, int columnIndex) throws SQLException { - String value = rs.getString(columnIndex); - return getResult(value); - } - - @Override - public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException { - String value = cs.getString(columnIndex); - return getResult(value); - } - - private List<String> getResult(String value) { - if (value == null) { - return null; - } - return StrUtil.splitTrim(value, COMMA); - } -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/util/JdbcUtils.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/util/JdbcUtils.java deleted file mode 100644 index 0275079..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/util/JdbcUtils.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.iailab.framework.mybatis.core.util; - -import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; -import com.baomidou.mybatisplus.annotation.DbType; -import com.iailab.framework.common.util.spring.SpringUtils; -import com.iailab.framework.mybatis.core.enums.DbTypeEnum; - -import javax.sql.DataSource; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; - -/** - * JDBC 工具类 - * - * @author iailab - */ -public class JdbcUtils { - - /** - * 判断连接是否正确 - * - * @param url 数据源连接 - * @param username 账号 - * @param password 密码 - * @return 是否正确 - */ - public static boolean isConnectionOK(String url, String username, String password) { - try (Connection ignored = DriverManager.getConnection(url, username, password)) { - return true; - } catch (Exception ex) { - return false; - } - } - - /** - * 获得 URL 对应的 DB 类型 - * - * @param url URL - * @return DB 类型 - */ - public static DbType getDbType(String url) { - return com.baomidou.mybatisplus.extension.toolkit.JdbcUtils.getDbType(url); - } - - /** - * 通过当前数据库连接获得对应的 DB 类型 - * - * @return DB 类型 - */ - public static DbType getDbType() { - DynamicRoutingDataSource dynamicRoutingDataSource = SpringUtils.getBean(DynamicRoutingDataSource.class); - DataSource dataSource = dynamicRoutingDataSource.determineDataSource(); - try (Connection conn = dataSource.getConnection()) { - return DbTypeEnum.find(conn.getMetaData().getDatabaseProductName()); - } catch (SQLException e) { - throw new IllegalArgumentException(e.getMessage()); - } - } - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/util/MyBatisUtils.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/util/MyBatisUtils.java deleted file mode 100644 index a8d8411..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/core/util/MyBatisUtils.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.iailab.framework.mybatis.core.util; - -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.annotation.DbType; -import com.baomidou.mybatisplus.core.metadata.OrderItem; -import com.baomidou.mybatisplus.core.toolkit.StringPool; -import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; -import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.iailab.framework.common.pojo.PageParam; -import com.iailab.framework.common.pojo.SortingField; -import com.iailab.framework.mybatis.core.enums.DbTypeEnum; -import net.sf.jsqlparser.expression.Alias; -import net.sf.jsqlparser.schema.Column; -import net.sf.jsqlparser.schema.Table; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -/** - * MyBatis 工具类 - */ -public class MyBatisUtils { - - private static final String MYSQL_ESCAPE_CHARACTER = "`"; - - public static <T> Page<T> buildPage(PageParam pageParam) { - return buildPage(pageParam, null); - } - - public static <T> Page<T> buildPage(PageParam pageParam, Collection<SortingField> sortingFields) { - // 页码 + 数量 - Page<T> page = new Page<>(pageParam.getPageNo(), pageParam.getPageSize()); - // 排序字段 - if (!CollectionUtil.isEmpty(sortingFields)) { - page.addOrder(sortingFields.stream().map(sortingField -> SortingField.ORDER_ASC.equals(sortingField.getOrder()) ? - OrderItem.asc(sortingField.getField()) : OrderItem.desc(sortingField.getField())) - .collect(Collectors.toList())); - } - return page; - } - - /** - * 将拦截器添加到链中 - * 由于 MybatisPlusInterceptor 不支持添加拦截器,所以只能全量设置 - * - * @param interceptor 链 - * @param inner 拦截器 - * @param index 位置 - */ - public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner, int index) { - List<InnerInterceptor> inners = new ArrayList<>(interceptor.getInterceptors()); - inners.add(index, inner); - interceptor.setInterceptors(inners); - } - - /** - * 获得 Table 对应的表名 - * <p> - * 兼容 MySQL 转义表名 `t_xxx` - * - * @param table 表 - * @return 去除转移字符后的表名 - */ - public static String getTableName(Table table) { - String tableName = table.getName(); - if (tableName.startsWith(MYSQL_ESCAPE_CHARACTER) && tableName.endsWith(MYSQL_ESCAPE_CHARACTER)) { - tableName = tableName.substring(1, tableName.length() - 1); - } - return tableName; - } - - /** - * 构建 Column 对象 - * - * @param tableName 表名 - * @param tableAlias 别名 - * @param column 字段名 - * @return Column 对象 - */ - public static Column buildColumn(String tableName, Alias tableAlias, String column) { - if (tableAlias != null) { - tableName = tableAlias.getName(); - } - return new Column(tableName + StringPool.DOT + column); - } - - /** - * 跨数据库的 find_in_set 实现 - * - * @param column 字段名称 - * @param value 查询值(不带单引号) - * @return sql - */ - public static String findInSet(String column, Object value) { - // 这里不用SqlConstants.DB_TYPE,因为它是使用 primary 数据源的 url 推断出来的类型 - DbType dbType = JdbcUtils.getDbType(); - return DbTypeEnum.getFindInSetTemplate(dbType) - .replace("#{column}", column) - .replace("#{value}", StrUtil.toString(value)); - } - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/interceptor/DataFilterInterceptor.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/interceptor/DataFilterInterceptor.java deleted file mode 100644 index 8cf8a98..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/interceptor/DataFilterInterceptor.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.mybatis.interceptor; - -import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.core.toolkit.PluginUtils; -import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; -import net.sf.jsqlparser.JSQLParserException; -import net.sf.jsqlparser.expression.Expression; -import net.sf.jsqlparser.expression.StringValue; -import net.sf.jsqlparser.expression.operators.conditional.AndExpression; -import net.sf.jsqlparser.parser.CCJSqlParserUtil; -import net.sf.jsqlparser.statement.select.PlainSelect; -import net.sf.jsqlparser.statement.select.Select; -import org.apache.ibatis.executor.Executor; -import org.apache.ibatis.mapping.BoundSql; -import org.apache.ibatis.mapping.MappedStatement; -import org.apache.ibatis.session.ResultHandler; -import org.apache.ibatis.session.RowBounds; - -import java.util.Map; - -/** - * 数据过滤 - * - * @author Mark sunlightcs@gmail.com - */ -public class DataFilterInterceptor implements InnerInterceptor { - - @Override - public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { - DataScope scope = getDataScope(parameter); - // 不进行数据过滤 - if(scope == null || StrUtil.isBlank(scope.getSqlFilter())){ - return; - } - - // 拼接新SQL - String buildSql = getSelect(boundSql.getSql(), scope); - - // 重写SQL - PluginUtils.mpBoundSql(boundSql).sql(buildSql); - } - - private DataScope getDataScope(Object parameter){ - if (parameter == null){ - return null; - } - - // 判断参数里是否有DataScope对象 - if (parameter instanceof Map) { - Map<?, ?> parameterMap = (Map<?, ?>) parameter; - for (Map.Entry entry : parameterMap.entrySet()) { - if (entry.getValue() != null && entry.getValue() instanceof DataScope) { - return (DataScope) entry.getValue(); - } - } - } else if (parameter instanceof DataScope) { - return (DataScope) parameter; - } - - return null; - } - - private String getSelect(String buildSql, DataScope scope){ - try { - Select select = (Select) CCJSqlParserUtil.parse(buildSql); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - - Expression expression = plainSelect.getWhere(); - if(expression == null){ - plainSelect.setWhere(new StringValue(scope.getSqlFilter())); - }else{ - AndExpression andExpression = new AndExpression(expression, new StringValue(scope.getSqlFilter())); - plainSelect.setWhere(andExpression); - } - - return select.toString().replaceAll("'", ""); - }catch (JSQLParserException e){ - return buildSql; - } - } -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/interceptor/DataScope.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/interceptor/DataScope.java deleted file mode 100644 index e9f2dcc..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/interceptor/DataScope.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.mybatis.interceptor; - -/** - * 数据范围 - * - * @author Mark sunlightcs@gmail.com - * @since 1.0.0 - */ -public class DataScope { - private String sqlFilter; - - public DataScope(String sqlFilter) { - this.sqlFilter = sqlFilter; - } - - public String getSqlFilter() { - return sqlFilter; - } - - public void setSqlFilter(String sqlFilter) { - this.sqlFilter = sqlFilter; - } - - @Override - public String toString() { - return this.sqlFilter; - } -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/package-info.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/package-info.java deleted file mode 100644 index bf8d7ef..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 使用 MyBatis Plus 提升使用 MyBatis 的开发效率 - */ -package com.iailab.framework.mybatis; diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/translate/config/IailabTranslateAutoConfiguration.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/translate/config/IailabTranslateAutoConfiguration.java deleted file mode 100644 index 55cfff1..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/translate/config/IailabTranslateAutoConfiguration.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.iailab.framework.translate.config; - -import com.iailab.framework.translate.core.TranslateUtils; -import com.fhs.trans.service.impl.TransService; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Bean; - -@AutoConfiguration -public class IailabTranslateAutoConfiguration { - - @Bean - @SuppressWarnings({"InstantiationOfUtilityClass", "SpringJavaInjectionPointsAutowiringInspection"}) - public TranslateUtils translateUtils(TransService transService) { - TranslateUtils.init(transService); - return new TranslateUtils(); - } - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/translate/core/TranslateUtils.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/translate/core/TranslateUtils.java deleted file mode 100644 index 94f8b8b..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/translate/core/TranslateUtils.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.iailab.framework.translate.core; - -import cn.hutool.core.collection.CollUtil; -import com.fhs.core.trans.vo.VO; -import com.fhs.trans.service.impl.TransService; - -import java.util.List; - -/** - * VO 数据翻译 Utils - * - * @author iailab - */ -public class TranslateUtils { - - private static TransService transService; - - public static void init(TransService transService) { - TranslateUtils.transService = transService; - } - - /** - * 数据翻译 - * - * 使用场景:无法使用 @TransMethodResult 注解的场景,只能通过手动触发翻译 - * - * @param data 数据 - * @return 翻译结果 - */ - public static <T extends VO> List<T> translate(List<T> data) { - if (CollUtil.isNotEmpty((data))) { - transService.transBatch(data); - } - return data; - } - -} diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/translate/package-info.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/translate/package-info.java deleted file mode 100644 index 7378e0f..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/translate/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 使用 Easy-Trans 提升使用 VO 数据翻译的开发效率 - */ -package com.iailab.framework.translate; diff --git a/iailab-framework/iailab-common-mybatis/src/main/resources/META-INF/spring.factories b/iailab-framework/iailab-common-mybatis/src/main/resources/META-INF/spring.factories deleted file mode 100644 index a0149ea..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.boot.env.EnvironmentPostProcessor=\ - com.iailab.framework.mybatis.config.IdTypeEnvironmentPostProcessor diff --git a/iailab-framework/iailab-common-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/iailab-framework/iailab-common-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index c3f21ff..0000000 --- a/iailab-framework/iailab-common-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,3 +0,0 @@ -com.iailab.framework.datasource.config.IailabDataSourceAutoConfiguration -com.iailab.framework.mybatis.config.IailabMybatisAutoConfiguration -com.iailab.framework.translate.config.IailabTranslateAutoConfiguration \ No newline at end of file diff --git a/iailab-framework/iailab-common-protection/pom.xml b/iailab-framework/iailab-common-protection/pom.xml deleted file mode 100644 index 85ca482..0000000 --- a/iailab-framework/iailab-common-protection/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>com.iailab</groupId> - <artifactId>iailab-framework</artifactId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-protection</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description>服务保证,提供分布式锁、幂等、限流、熔断等等功能</description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <!-- Web 相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-web</artifactId> - <scope>provided</scope> <!-- 设置为 provided,只有限流、幂等使用到 --> - </dependency> - - <!-- DB 相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-redis</artifactId> - </dependency> - - <!-- 服务保障相关 --> - <dependency> - <groupId>com.baomidou</groupId> - <artifactId>lock4j-redisson-spring-boot-starter</artifactId> - <optional>true</optional> - </dependency> - - <!-- Test 测试相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-test</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/config/IailabIdempotentConfiguration.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/config/IailabIdempotentConfiguration.java deleted file mode 100644 index 226645b..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/config/IailabIdempotentConfiguration.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.iailab.framework.idempotent.config; - -import com.iailab.framework.idempotent.core.aop.IdempotentAspect; -import com.iailab.framework.idempotent.core.keyresolver.impl.DefaultIdempotentKeyResolver; -import com.iailab.framework.idempotent.core.keyresolver.impl.ExpressionIdempotentKeyResolver; -import com.iailab.framework.idempotent.core.keyresolver.IdempotentKeyResolver; -import com.iailab.framework.idempotent.core.keyresolver.impl.UserIdempotentKeyResolver; -import com.iailab.framework.idempotent.core.redis.IdempotentRedisDAO; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import com.iailab.framework.redis.config.IailabRedisAutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.data.redis.core.StringRedisTemplate; - -import java.util.List; - -@AutoConfiguration(after = IailabRedisAutoConfiguration.class) -public class IailabIdempotentConfiguration { - - @Bean - public IdempotentAspect idempotentAspect(List<IdempotentKeyResolver> keyResolvers, IdempotentRedisDAO idempotentRedisDAO) { - return new IdempotentAspect(keyResolvers, idempotentRedisDAO); - } - - @Bean - public IdempotentRedisDAO idempotentRedisDAO(StringRedisTemplate stringRedisTemplate) { - return new IdempotentRedisDAO(stringRedisTemplate); - } - - // ========== 各种 IdempotentKeyResolver Bean ========== - - @Bean - public DefaultIdempotentKeyResolver defaultIdempotentKeyResolver() { - return new DefaultIdempotentKeyResolver(); - } - - @Bean - public UserIdempotentKeyResolver userIdempotentKeyResolver() { - return new UserIdempotentKeyResolver(); - } - - @Bean - public ExpressionIdempotentKeyResolver expressionIdempotentKeyResolver() { - return new ExpressionIdempotentKeyResolver(); - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/annotation/Idempotent.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/annotation/Idempotent.java deleted file mode 100644 index 8686c0a..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/annotation/Idempotent.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.iailab.framework.idempotent.core.annotation; - -import com.iailab.framework.idempotent.core.keyresolver.impl.DefaultIdempotentKeyResolver; -import com.iailab.framework.idempotent.core.keyresolver.IdempotentKeyResolver; -import com.iailab.framework.idempotent.core.keyresolver.impl.ExpressionIdempotentKeyResolver; -import com.iailab.framework.idempotent.core.keyresolver.impl.UserIdempotentKeyResolver; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.concurrent.TimeUnit; - -/** - * 幂等注解 - * - * @author iailab - */ -@Target({ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface Idempotent { - - /** - * 幂等的超时时间,默认为 1 秒 - * - * 注意,如果执行时间超过它,请求还是会进来 - */ - int timeout() default 1; - /** - * 时间单位,默认为 SECONDS 秒 - */ - TimeUnit timeUnit() default TimeUnit.SECONDS; - - /** - * 提示信息,正在执行中的提示 - */ - String message() default "重复请求,请稍后重试"; - - /** - * 使用的 Key 解析器 - * - * @see DefaultIdempotentKeyResolver 全局级别 - * @see UserIdempotentKeyResolver 用户级别 - * @see ExpressionIdempotentKeyResolver 自定义表达式,通过 {@link #keyArg()} 计算 - */ - Class<? extends IdempotentKeyResolver> keyResolver() default DefaultIdempotentKeyResolver.class; - /** - * 使用的 Key 参数 - */ - String keyArg() default ""; - - /** - * 删除 Key,当发生异常时候 - * - * 问题:为什么发生异常时,需要删除 Key 呢? - * 回答:发生异常时,说明业务发生错误,此时需要删除 Key,避免下次请求无法正常执行。 - * - * 问题:为什么不搞 deleteWhenSuccess 执行成功时,需要删除 Key 呢? - * 回答:这种情况下,本质上是分布式锁,推荐使用 @Lock4j 注解 - */ - boolean deleteKeyWhenException() default true; - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/aop/IdempotentAspect.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/aop/IdempotentAspect.java deleted file mode 100644 index 603f270..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/aop/IdempotentAspect.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.iailab.framework.idempotent.core.aop; - -import com.iailab.framework.common.exception.ServiceException; -import com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants; -import com.iailab.framework.common.util.collection.CollectionUtils; -import com.iailab.framework.idempotent.core.annotation.Idempotent; -import com.iailab.framework.idempotent.core.keyresolver.IdempotentKeyResolver; -import com.iailab.framework.idempotent.core.redis.IdempotentRedisDAO; -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.springframework.util.Assert; - -import java.util.List; -import java.util.Map; - -/** - * 拦截声明了 {@link Idempotent} 注解的方法,实现幂等操作 - * - * @author iailab - */ -@Aspect -@Slf4j -public class IdempotentAspect { - - /** - * IdempotentKeyResolver 集合 - */ - private final Map<Class<? extends IdempotentKeyResolver>, IdempotentKeyResolver> keyResolvers; - - private final IdempotentRedisDAO idempotentRedisDAO; - - public IdempotentAspect(List<IdempotentKeyResolver> keyResolvers, IdempotentRedisDAO idempotentRedisDAO) { - this.keyResolvers = CollectionUtils.convertMap(keyResolvers, IdempotentKeyResolver::getClass); - this.idempotentRedisDAO = idempotentRedisDAO; - } - - @Around(value = "@annotation(idempotent)") - public Object aroundPointCut(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable { - // 获得 IdempotentKeyResolver - IdempotentKeyResolver keyResolver = keyResolvers.get(idempotent.keyResolver()); - Assert.notNull(keyResolver, "找不到对应的 IdempotentKeyResolver"); - // 解析 Key - String key = keyResolver.resolver(joinPoint, idempotent); - - // 1. 锁定 Key - boolean success = idempotentRedisDAO.setIfAbsent(key, idempotent.timeout(), idempotent.timeUnit()); - // 锁定失败,抛出异常 - if (!success) { - log.info("[aroundPointCut][方法({}) 参数({}) 存在重复请求]", joinPoint.getSignature().toString(), joinPoint.getArgs()); - throw new ServiceException(GlobalErrorCodeConstants.REPEATED_REQUESTS.getCode(), idempotent.message()); - } - - // 2. 执行逻辑 - try { - return joinPoint.proceed(); - } catch (Throwable throwable) { - // 3. 异常时,删除 Key - // 参考美团 GTIS 思路:https://tech.meituan.com/2016/09/29/distributed-system-mutually-exclusive-idempotence-cerberus-gtis.html - if (idempotent.deleteKeyWhenException()) { - idempotentRedisDAO.delete(key); - } - throw throwable; - } - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/keyresolver/IdempotentKeyResolver.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/keyresolver/IdempotentKeyResolver.java deleted file mode 100644 index 503957a..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/keyresolver/IdempotentKeyResolver.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iailab.framework.idempotent.core.keyresolver; - -import com.iailab.framework.idempotent.core.annotation.Idempotent; -import org.aspectj.lang.JoinPoint; - -/** - * 幂等 Key 解析器接口 - * - * @author iailab - */ -public interface IdempotentKeyResolver { - - /** - * 解析一个 Key - * - * @param idempotent 幂等注解 - * @param joinPoint AOP 切面 - * @return Key - */ - String resolver(JoinPoint joinPoint, Idempotent idempotent); - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/keyresolver/impl/DefaultIdempotentKeyResolver.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/keyresolver/impl/DefaultIdempotentKeyResolver.java deleted file mode 100644 index f9c5847..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/keyresolver/impl/DefaultIdempotentKeyResolver.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.idempotent.core.keyresolver.impl; - -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.SecureUtil; -import com.iailab.framework.idempotent.core.annotation.Idempotent; -import com.iailab.framework.idempotent.core.keyresolver.IdempotentKeyResolver; -import org.aspectj.lang.JoinPoint; - -/** - * 默认(全局级别)幂等 Key 解析器,使用方法名 + 方法参数,组装成一个 Key - * - * 为了避免 Key 过长,使用 MD5 进行“压缩” - * - * @author iailab - */ -public class DefaultIdempotentKeyResolver implements IdempotentKeyResolver { - - @Override - public String resolver(JoinPoint joinPoint, Idempotent idempotent) { - String methodName = joinPoint.getSignature().toString(); - String argsStr = StrUtil.join(",", joinPoint.getArgs()); - return SecureUtil.md5(methodName + argsStr); - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/keyresolver/impl/ExpressionIdempotentKeyResolver.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/keyresolver/impl/ExpressionIdempotentKeyResolver.java deleted file mode 100644 index b1fe1b5..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/keyresolver/impl/ExpressionIdempotentKeyResolver.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.iailab.framework.idempotent.core.keyresolver.impl; - -import cn.hutool.core.util.ArrayUtil; -import com.iailab.framework.idempotent.core.annotation.Idempotent; -import com.iailab.framework.idempotent.core.keyresolver.IdempotentKeyResolver; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.core.LocalVariableTableParameterNameDiscoverer; -import org.springframework.core.ParameterNameDiscoverer; -import org.springframework.expression.Expression; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; - -import java.lang.reflect.Method; - -/** - * 基于 Spring EL 表达式, - * - * @author iailab - */ -public class ExpressionIdempotentKeyResolver implements IdempotentKeyResolver { - - private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); - private final ExpressionParser expressionParser = new SpelExpressionParser(); - - @Override - public String resolver(JoinPoint joinPoint, Idempotent idempotent) { - // 获得被拦截方法参数名列表 - Method method = getMethod(joinPoint); - Object[] args = joinPoint.getArgs(); - String[] parameterNames = this.parameterNameDiscoverer.getParameterNames(method); - // 准备 Spring EL 表达式解析的上下文 - StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); - if (ArrayUtil.isNotEmpty(parameterNames)) { - for (int i = 0; i < parameterNames.length; i++) { - evaluationContext.setVariable(parameterNames[i], args[i]); - } - } - - // 解析参数 - Expression expression = expressionParser.parseExpression(idempotent.keyArg()); - return expression.getValue(evaluationContext, String.class); - } - - private static Method getMethod(JoinPoint point) { - // 处理,声明在类上的情况 - MethodSignature signature = (MethodSignature) point.getSignature(); - Method method = signature.getMethod(); - if (!method.getDeclaringClass().isInterface()) { - return method; - } - - // 处理,声明在接口上的情况 - try { - return point.getTarget().getClass().getDeclaredMethod( - point.getSignature().getName(), method.getParameterTypes()); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/keyresolver/impl/UserIdempotentKeyResolver.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/keyresolver/impl/UserIdempotentKeyResolver.java deleted file mode 100644 index 22e29bc..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/keyresolver/impl/UserIdempotentKeyResolver.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.framework.idempotent.core.keyresolver.impl; - -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.SecureUtil; -import com.iailab.framework.idempotent.core.annotation.Idempotent; -import com.iailab.framework.idempotent.core.keyresolver.IdempotentKeyResolver; -import com.iailab.framework.web.core.util.WebFrameworkUtils; -import org.aspectj.lang.JoinPoint; - -/** - * 用户级别的幂等 Key 解析器,使用方法名 + 方法参数 + userId + userType,组装成一个 Key - * - * 为了避免 Key 过长,使用 MD5 进行“压缩” - * - * @author iailab - */ -public class UserIdempotentKeyResolver implements IdempotentKeyResolver { - - @Override - public String resolver(JoinPoint joinPoint, Idempotent idempotent) { - String methodName = joinPoint.getSignature().toString(); - String argsStr = StrUtil.join(",", joinPoint.getArgs()); - Long userId = WebFrameworkUtils.getLoginUserId(); - Integer userType = WebFrameworkUtils.getLoginUserType(); - return SecureUtil.md5(methodName + argsStr + userId + userType); - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/redis/IdempotentRedisDAO.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/redis/IdempotentRedisDAO.java deleted file mode 100644 index 31b6b4b..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/core/redis/IdempotentRedisDAO.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.iailab.framework.idempotent.core.redis; - -import lombok.AllArgsConstructor; -import org.springframework.data.redis.core.StringRedisTemplate; - -import java.util.concurrent.TimeUnit; - -/** - * 幂等 Redis DAO - * - * @author iailab - */ -@AllArgsConstructor -public class IdempotentRedisDAO { - - /** - * 幂等操作 - * - * KEY 格式:idempotent:%s // 参数为 uuid - * VALUE 格式:String - * 过期时间:不固定 - */ - private static final String IDEMPOTENT = "idempotent:%s"; - - private final StringRedisTemplate redisTemplate; - - public Boolean setIfAbsent(String key, long timeout, TimeUnit timeUnit) { - String redisKey = formatKey(key); - return redisTemplate.opsForValue().setIfAbsent(redisKey, "", timeout, timeUnit); - } - - public void delete(String key) { - String redisKey = formatKey(key); - redisTemplate.delete(redisKey); - } - - private static String formatKey(String key) { - return String.format(IDEMPOTENT, key); - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/package-info.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/package-info.java deleted file mode 100644 index 052e4b7..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/idempotent/package-info.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * 幂等组件,参考 https://github.com/it4alla/idempotent 项目实现 - * 实现原理是,相同参数的方法,一段时间内,有且仅能执行一次。通过这样的方式,保证幂等性。 - * - * 使用场景:例如说,用户快速的双击了某个按钮,前端没有禁用该按钮,导致发送了两次重复的请求。 - * - * 和 it4alla/idempotent 组件的差异点,主要体现在两点: - * 1. 我们去掉了 @Idempotent 注解的 delKey 属性。原因是,本质上 delKey 为 true 时,实现的是分布式锁的能力 - * 此时,我们偏向使用 Lock4j 组件。原则上,一个组件只提供一种单一的能力。 - * 2. 考虑到组件的通用性,我们并未像 it4alla/idempotent 组件一样使用 Redisson RMap 结构,而是直接使用 Redis 的 String 数据格式。 - */ -package com.iailab.framework.idempotent; diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/lock4j/config/IailabLock4jConfiguration.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/lock4j/config/IailabLock4jConfiguration.java deleted file mode 100644 index 4e72e65..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/lock4j/config/IailabLock4jConfiguration.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.iailab.framework.lock4j.config; - -import com.iailab.framework.lock4j.core.DefaultLockFailureStrategy; -import com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.context.annotation.Bean; - -@AutoConfiguration(before = LockAutoConfiguration.class) -@ConditionalOnClass(name = "com.baomidou.lock.annotation.Lock4j") -public class IailabLock4jConfiguration { - - @Bean - public DefaultLockFailureStrategy lockFailureStrategy() { - return new DefaultLockFailureStrategy(); - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/lock4j/core/DefaultLockFailureStrategy.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/lock4j/core/DefaultLockFailureStrategy.java deleted file mode 100644 index d2187a7..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/lock4j/core/DefaultLockFailureStrategy.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.iailab.framework.lock4j.core; - -import com.iailab.framework.common.exception.ServiceException; -import com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants; -import com.baomidou.lock.LockFailureStrategy; -import lombok.extern.slf4j.Slf4j; - -import java.lang.reflect.Method; - -/** - * 自定义获取锁失败策略,抛出 {@link ServiceException} 异常 - */ -@Slf4j -public class DefaultLockFailureStrategy implements LockFailureStrategy { - - @Override - public void onLockFailure(String key, Method method, Object[] arguments) { - log.debug("[onLockFailure][线程:{} 获取锁失败,key:{} 获取失败:{} ]", Thread.currentThread().getName(), key, arguments); - throw new ServiceException(GlobalErrorCodeConstants.LOCKED); - } -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/lock4j/core/Lock4jRedisKeyConstants.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/lock4j/core/Lock4jRedisKeyConstants.java deleted file mode 100644 index fbb076f..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/lock4j/core/Lock4jRedisKeyConstants.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.iailab.framework.lock4j.core; - -/** - * Lock4j Redis Key 枚举类 - * - * @author iailab - */ -public interface Lock4jRedisKeyConstants { - - /** - * 分布式锁 - * - * KEY 格式:lock4j:%s // 参数来自 DefaultLockKeyBuilder 类 - * VALUE 数据格式:HASH // RLock.class:Redisson 的 Lock 锁,使用 Hash 数据结构 - * 过期时间:不固定 - */ - String LOCK4J = "lock4j:%s"; - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/lock4j/package-info.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/lock4j/package-info.java deleted file mode 100644 index 3016962..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/lock4j/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 分布式锁组件,使用 https://gitee.com/baomidou/lock4j 开源项目 - */ -package com.iailab.framework.lock4j; diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/config/IailabRateLimiterConfiguration.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/config/IailabRateLimiterConfiguration.java deleted file mode 100644 index 4ce5009..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/config/IailabRateLimiterConfiguration.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.iailab.framework.ratelimiter.config; - -import com.iailab.framework.ratelimiter.core.aop.RateLimiterAspect; -import com.iailab.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver; -import com.iailab.framework.ratelimiter.core.keyresolver.impl.*; -import com.iailab.framework.ratelimiter.core.redis.RateLimiterRedisDAO; -import com.iailab.framework.redis.config.IailabRedisAutoConfiguration; -import org.redisson.api.RedissonClient; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Bean; - -import java.util.List; - -@AutoConfiguration(after = IailabRedisAutoConfiguration.class) -public class IailabRateLimiterConfiguration { - - @Bean - public RateLimiterAspect rateLimiterAspect(List<RateLimiterKeyResolver> keyResolvers, RateLimiterRedisDAO rateLimiterRedisDAO) { - return new RateLimiterAspect(keyResolvers, rateLimiterRedisDAO); - } - - @Bean - @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") - public RateLimiterRedisDAO rateLimiterRedisDAO(RedissonClient redissonClient) { - return new RateLimiterRedisDAO(redissonClient); - } - - // ========== 各种 RateLimiterRedisDAO Bean ========== - - @Bean - public DefaultRateLimiterKeyResolver defaultRateLimiterKeyResolver() { - return new DefaultRateLimiterKeyResolver(); - } - - @Bean - public UserRateLimiterKeyResolver userRateLimiterKeyResolver() { - return new UserRateLimiterKeyResolver(); - } - - @Bean - public ClientIpRateLimiterKeyResolver clientIpRateLimiterKeyResolver() { - return new ClientIpRateLimiterKeyResolver(); - } - - @Bean - public ServerNodeRateLimiterKeyResolver serverNodeRateLimiterKeyResolver() { - return new ServerNodeRateLimiterKeyResolver(); - } - - @Bean - public ExpressionRateLimiterKeyResolver expressionRateLimiterKeyResolver() { - return new ExpressionRateLimiterKeyResolver(); - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/annotation/RateLimiter.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/annotation/RateLimiter.java deleted file mode 100644 index 1be2263..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/annotation/RateLimiter.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.iailab.framework.ratelimiter.core.annotation; - -import com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants; -import com.iailab.framework.idempotent.core.keyresolver.impl.ExpressionIdempotentKeyResolver; -import com.iailab.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver; -import com.iailab.framework.ratelimiter.core.keyresolver.impl.ClientIpRateLimiterKeyResolver; -import com.iailab.framework.ratelimiter.core.keyresolver.impl.DefaultRateLimiterKeyResolver; -import com.iailab.framework.ratelimiter.core.keyresolver.impl.ServerNodeRateLimiterKeyResolver; -import com.iailab.framework.ratelimiter.core.keyresolver.impl.UserRateLimiterKeyResolver; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.concurrent.TimeUnit; - -/** - * 限流注解 - * - * @author iailab - */ -@Target({ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface RateLimiter { - - /** - * 限流的时间,默认为 1 秒 - */ - int time() default 1; - /** - * 时间单位,默认为 SECONDS 秒 - */ - TimeUnit timeUnit() default TimeUnit.SECONDS; - - /** - * 限流次数 - */ - int count() default 100; - - /** - * 提示信息,请求过快的提示 - * - * @see GlobalErrorCodeConstants#TOO_MANY_REQUESTS - */ - String message() default ""; // 为空时,使用 TOO_MANY_REQUESTS 错误提示 - - /** - * 使用的 Key 解析器 - * - * @see DefaultRateLimiterKeyResolver 全局级别 - * @see UserRateLimiterKeyResolver 用户 ID 级别 - * @see ClientIpRateLimiterKeyResolver 用户 IP 级别 - * @see ServerNodeRateLimiterKeyResolver 服务器 Node 级别 - * @see ExpressionIdempotentKeyResolver 自定义表达式,通过 {@link #keyArg()} 计算 - */ - Class<? extends RateLimiterKeyResolver> keyResolver() default DefaultRateLimiterKeyResolver.class; - /** - * 使用的 Key 参数 - */ - String keyArg() default ""; - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/aop/RateLimiterAspect.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/aop/RateLimiterAspect.java deleted file mode 100644 index 1ca33d2..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/aop/RateLimiterAspect.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.iailab.framework.ratelimiter.core.aop; - -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.exception.ServiceException; -import com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants; -import com.iailab.framework.common.util.collection.CollectionUtils; -import com.iailab.framework.ratelimiter.core.annotation.RateLimiter; -import com.iailab.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver; -import com.iailab.framework.ratelimiter.core.redis.RateLimiterRedisDAO; -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; -import org.springframework.util.Assert; - -import java.util.List; -import java.util.Map; - -/** - * 拦截声明了 {@link RateLimiter} 注解的方法,实现限流操作 - * - * @author iailab - */ -@Aspect -@Slf4j -public class RateLimiterAspect { - - /** - * RateLimiterKeyResolver 集合 - */ - private final Map<Class<? extends RateLimiterKeyResolver>, RateLimiterKeyResolver> keyResolvers; - - private final RateLimiterRedisDAO rateLimiterRedisDAO; - - public RateLimiterAspect(List<RateLimiterKeyResolver> keyResolvers, RateLimiterRedisDAO rateLimiterRedisDAO) { - this.keyResolvers = CollectionUtils.convertMap(keyResolvers, RateLimiterKeyResolver::getClass); - this.rateLimiterRedisDAO = rateLimiterRedisDAO; - } - - @Before("@annotation(rateLimiter)") - public void beforePointCut(JoinPoint joinPoint, RateLimiter rateLimiter) { - // 获得 IdempotentKeyResolver 对象 - RateLimiterKeyResolver keyResolver = keyResolvers.get(rateLimiter.keyResolver()); - Assert.notNull(keyResolver, "找不到对应的 RateLimiterKeyResolver"); - // 解析 Key - String key = keyResolver.resolver(joinPoint, rateLimiter); - - // 获取 1 次限流 - boolean success = rateLimiterRedisDAO.tryAcquire(key, - rateLimiter.count(), rateLimiter.time(), rateLimiter.timeUnit()); - if (!success) { - log.info("[beforePointCut][方法({}) 参数({}) 请求过于频繁]", joinPoint.getSignature().toString(), joinPoint.getArgs()); - String message = StrUtil.blankToDefault(rateLimiter.message(), - GlobalErrorCodeConstants.TOO_MANY_REQUESTS.getMsg()); - throw new ServiceException(GlobalErrorCodeConstants.TOO_MANY_REQUESTS.getCode(), message); - } - } - -} - diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/RateLimiterKeyResolver.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/RateLimiterKeyResolver.java deleted file mode 100644 index e72d70d..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/RateLimiterKeyResolver.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iailab.framework.ratelimiter.core.keyresolver; - -import com.iailab.framework.ratelimiter.core.annotation.RateLimiter; -import org.aspectj.lang.JoinPoint; - -/** - * 限流 Key 解析器接口 - * - * @author iailab - */ -public interface RateLimiterKeyResolver { - - /** - * 解析一个 Key - * - * @param rateLimiter 限流注解 - * @param joinPoint AOP 切面 - * @return Key - */ - String resolver(JoinPoint joinPoint, RateLimiter rateLimiter); - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/ClientIpRateLimiterKeyResolver.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/ClientIpRateLimiterKeyResolver.java deleted file mode 100644 index 2e9be94..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/ClientIpRateLimiterKeyResolver.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.iailab.framework.ratelimiter.core.keyresolver.impl; - -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.SecureUtil; -import com.iailab.framework.common.util.servlet.ServletUtils; -import com.iailab.framework.ratelimiter.core.annotation.RateLimiter; -import com.iailab.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver; -import org.aspectj.lang.JoinPoint; - -/** - * IP 级别的限流 Key 解析器,使用方法名 + 方法参数 + IP,组装成一个 Key - * - * 为了避免 Key 过长,使用 MD5 进行“压缩” - * - * @author iailab - */ -public class ClientIpRateLimiterKeyResolver implements RateLimiterKeyResolver { - - @Override - public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) { - String methodName = joinPoint.getSignature().toString(); - String argsStr = StrUtil.join(",", joinPoint.getArgs()); - String clientIp = ServletUtils.getClientIP(); - return SecureUtil.md5(methodName + argsStr + clientIp); - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/DefaultRateLimiterKeyResolver.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/DefaultRateLimiterKeyResolver.java deleted file mode 100644 index 1b3af97..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/DefaultRateLimiterKeyResolver.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.ratelimiter.core.keyresolver.impl; - -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.SecureUtil; -import com.iailab.framework.ratelimiter.core.annotation.RateLimiter; -import com.iailab.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver; -import org.aspectj.lang.JoinPoint; - -/** - * 默认(全局级别)限流 Key 解析器,使用方法名 + 方法参数,组装成一个 Key - * - * 为了避免 Key 过长,使用 MD5 进行“压缩” - * - * @author iailab - */ -public class DefaultRateLimiterKeyResolver implements RateLimiterKeyResolver { - - @Override - public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) { - String methodName = joinPoint.getSignature().toString(); - String argsStr = StrUtil.join(",", joinPoint.getArgs()); - return SecureUtil.md5(methodName + argsStr); - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/ExpressionRateLimiterKeyResolver.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/ExpressionRateLimiterKeyResolver.java deleted file mode 100644 index 813daa3..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/ExpressionRateLimiterKeyResolver.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.iailab.framework.ratelimiter.core.keyresolver.impl; - -import cn.hutool.core.util.ArrayUtil; -import com.iailab.framework.ratelimiter.core.annotation.RateLimiter; -import com.iailab.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.core.DefaultParameterNameDiscoverer; -import org.springframework.core.ParameterNameDiscoverer; -import org.springframework.expression.Expression; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; - -import java.lang.reflect.Method; - -/** - * 基于 Spring EL 表达式的 {@link RateLimiterKeyResolver} 实现类 - * - * @author iailab - */ -public class ExpressionRateLimiterKeyResolver implements RateLimiterKeyResolver { - - private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); - - private final ExpressionParser expressionParser = new SpelExpressionParser(); - - @Override - public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) { - // 获得被拦截方法参数名列表 - Method method = getMethod(joinPoint); - Object[] args = joinPoint.getArgs(); - String[] parameterNames = this.parameterNameDiscoverer.getParameterNames(method); - // 准备 Spring EL 表达式解析的上下文 - StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); - if (ArrayUtil.isNotEmpty(parameterNames)) { - for (int i = 0; i < parameterNames.length; i++) { - evaluationContext.setVariable(parameterNames[i], args[i]); - } - } - - // 解析参数 - Expression expression = expressionParser.parseExpression(rateLimiter.keyArg()); - return expression.getValue(evaluationContext, String.class); - } - - private static Method getMethod(JoinPoint point) { - // 处理,声明在类上的情况 - MethodSignature signature = (MethodSignature) point.getSignature(); - Method method = signature.getMethod(); - if (!method.getDeclaringClass().isInterface()) { - return method; - } - - // 处理,声明在接口上的情况 - try { - return point.getTarget().getClass().getDeclaredMethod( - point.getSignature().getName(), method.getParameterTypes()); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/ServerNodeRateLimiterKeyResolver.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/ServerNodeRateLimiterKeyResolver.java deleted file mode 100644 index 16775f1..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/ServerNodeRateLimiterKeyResolver.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.iailab.framework.ratelimiter.core.keyresolver.impl; - -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.SecureUtil; -import cn.hutool.system.SystemUtil; -import com.iailab.framework.ratelimiter.core.annotation.RateLimiter; -import com.iailab.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver; -import org.aspectj.lang.JoinPoint; - -/** - * Server 节点级别的限流 Key 解析器,使用方法名 + 方法参数 + IP,组装成一个 Key - * - * 为了避免 Key 过长,使用 MD5 进行“压缩” - * - * @author iailab - */ -public class ServerNodeRateLimiterKeyResolver implements RateLimiterKeyResolver { - - @Override - public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) { - String methodName = joinPoint.getSignature().toString(); - String argsStr = StrUtil.join(",", joinPoint.getArgs()); - String serverNode = String.format("%s@%d", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID()); - return SecureUtil.md5(methodName + argsStr + serverNode); - } - -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/UserRateLimiterKeyResolver.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/UserRateLimiterKeyResolver.java deleted file mode 100644 index 9fed5cc..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/keyresolver/impl/UserRateLimiterKeyResolver.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.framework.ratelimiter.core.keyresolver.impl; - -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.SecureUtil; -import com.iailab.framework.ratelimiter.core.annotation.RateLimiter; -import com.iailab.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver; -import com.iailab.framework.web.core.util.WebFrameworkUtils; -import org.aspectj.lang.JoinPoint; - -/** - * 用户级别的限流 Key 解析器,使用方法名 + 方法参数 + userId + userType,组装成一个 Key - * - * 为了避免 Key 过长,使用 MD5 进行“压缩” - * - * @author iailab - */ -public class UserRateLimiterKeyResolver implements RateLimiterKeyResolver { - - @Override - public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) { - String methodName = joinPoint.getSignature().toString(); - String argsStr = StrUtil.join(",", joinPoint.getArgs()); - Long userId = WebFrameworkUtils.getLoginUserId(); - Integer userType = WebFrameworkUtils.getLoginUserType(); - return SecureUtil.md5(methodName + argsStr + userId + userType); - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/redis/RateLimiterRedisDAO.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/redis/RateLimiterRedisDAO.java deleted file mode 100644 index 5a113c3..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/core/redis/RateLimiterRedisDAO.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.iailab.framework.ratelimiter.core.redis; - -import lombok.AllArgsConstructor; -import org.redisson.api.*; - -import java.util.Objects; -import java.util.concurrent.TimeUnit; - -/** - * 限流 Redis DAO - * - * @author iailab - */ -@AllArgsConstructor -public class RateLimiterRedisDAO { - - /** - * 限流操作 - * - * KEY 格式:rate_limiter:%s // 参数为 uuid - * VALUE 格式:String - * 过期时间:不固定 - */ - private static final String RATE_LIMITER = "rate_limiter:%s"; - - private final RedissonClient redissonClient; - - public Boolean tryAcquire(String key, int count, int time, TimeUnit timeUnit) { - // 1. 获得 RRateLimiter,并设置 rate 速率 - RRateLimiter rateLimiter = getRRateLimiter(key, count, time, timeUnit); - // 2. 尝试获取 1 个 - return rateLimiter.tryAcquire(); - } - - private static String formatKey(String key) { - return String.format(RATE_LIMITER, key); - } - - private RRateLimiter getRRateLimiter(String key, long count, int time, TimeUnit timeUnit) { - String redisKey = formatKey(key); - RRateLimiter rateLimiter = redissonClient.getRateLimiter(redisKey); - long rateInterval = timeUnit.toSeconds(time); - // 1. 如果不存在,设置 rate 速率 - RateLimiterConfig config = rateLimiter.getConfig(); - if (config == null) { - rateLimiter.trySetRate(RateType.OVERALL, count, rateInterval, RateIntervalUnit.SECONDS); - return rateLimiter; - } - // 2. 如果存在,并且配置相同,则直接返回 - if (config.getRateType() == RateType.OVERALL - && Objects.equals(config.getRate(), count) - && Objects.equals(config.getRateInterval(), TimeUnit.SECONDS.toMillis(rateInterval))) { - return rateLimiter; - } - // 3. 如果存在,并且配置不同,则进行新建 - rateLimiter.setRate(RateType.OVERALL, count, rateInterval, RateIntervalUnit.SECONDS); - return rateLimiter; - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/package-info.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/package-info.java deleted file mode 100644 index fc519ac..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/ratelimiter/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 限流组件,基于 Redisson {@link org.redisson.api.RRateLimiter} 限流实现 - */ -package com.iailab.framework.ratelimiter; \ No newline at end of file diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/config/IailabApiSignatureAutoConfiguration.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/config/IailabApiSignatureAutoConfiguration.java deleted file mode 100644 index a036906..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/config/IailabApiSignatureAutoConfiguration.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.framework.signature.config; - -import com.iailab.framework.redis.config.IailabRedisAutoConfiguration; -import com.iailab.framework.signature.core.aop.ApiSignatureAspect; -import com.iailab.framework.signature.core.redis.ApiSignatureRedisDAO; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.data.redis.core.StringRedisTemplate; - -/** - * HTTP API 签名的自动配置类 - * - * @author Zhougang - */ -@AutoConfiguration(after = IailabRedisAutoConfiguration.class) -public class IailabApiSignatureAutoConfiguration { - - @Bean - public ApiSignatureAspect signatureAspect(ApiSignatureRedisDAO signatureRedisDAO) { - return new ApiSignatureAspect(signatureRedisDAO); - } - - @Bean - public ApiSignatureRedisDAO signatureRedisDAO(StringRedisTemplate stringRedisTemplate) { - return new ApiSignatureRedisDAO(stringRedisTemplate); - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/core/annotation/ApiSignature.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/core/annotation/ApiSignature.java deleted file mode 100644 index c0a263b..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/core/annotation/ApiSignature.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.iailab.framework.signature.core.annotation; - -import com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants; - -import java.lang.annotation.*; -import java.util.concurrent.TimeUnit; - - -/** - * HTTP API 签名注解 - * - * @author Zhougang - */ -@Inherited -@Documented -@Target({ElementType.METHOD, ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface ApiSignature { - - /** - * 同一个请求多长时间内有效 默认 60 秒 - */ - int timeout() default 60; - - /** - * 时间单位,默认为 SECONDS 秒 - */ - TimeUnit timeUnit() default TimeUnit.SECONDS; - - // ========================== 签名参数 ========================== - - /** - * 提示信息,签名失败的提示 - * - * @see GlobalErrorCodeConstants#BAD_REQUEST - */ - String message() default "签名不正确"; // 为空时,使用 BAD_REQUEST 错误提示 - - /** - * 签名字段:appId 应用ID - */ - String appId() default "appId"; - - /** - * 签名字段:timestamp 时间戳 - */ - String timestamp() default "timestamp"; - - /** - * 签名字段:nonce 随机数,10 位以上 - */ - String nonce() default "nonce"; - - /** - * sign 客户端签名 - */ - String sign() default "sign"; - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/core/aop/ApiSignatureAspect.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/core/aop/ApiSignatureAspect.java deleted file mode 100644 index 716f0de..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/core/aop/ApiSignatureAspect.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.iailab.framework.signature.core.aop; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.digest.DigestUtil; -import com.iailab.framework.common.exception.ServiceException; -import com.iailab.framework.common.util.servlet.ServletUtils; -import com.iailab.framework.signature.core.annotation.ApiSignature; -import com.iailab.framework.signature.core.redis.ApiSignatureRedisDAO; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; - -import javax.servlet.http.HttpServletRequest; -import java.util.Map; -import java.util.Objects; -import java.util.SortedMap; -import java.util.TreeMap; - -import static com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; - -/** - * 拦截声明了 {@link ApiSignature} 注解的方法,实现签名 - * - * @author Zhougang - */ -@Aspect -@Slf4j -@AllArgsConstructor -public class ApiSignatureAspect { - - private final ApiSignatureRedisDAO signatureRedisDAO; - - @Before("@annotation(signature)") - public void beforePointCut(JoinPoint joinPoint, ApiSignature signature) { - // 1. 验证通过,直接结束 - if (verifySignature(signature, Objects.requireNonNull(ServletUtils.getRequest()))) { - return; - } - - // 2. 验证不通过,抛出异常 - log.error("[beforePointCut][方法{} 参数({}) 签名失败]", joinPoint.getSignature().toString(), - joinPoint.getArgs()); - throw new ServiceException(BAD_REQUEST.getCode(), - StrUtil.blankToDefault(signature.message(), BAD_REQUEST.getMsg())); - } - - public boolean verifySignature(ApiSignature signature, HttpServletRequest request) { - // 1.1 校验 Header - if (!verifyHeaders(signature, request)) { - return false; - } - // 1.2 校验 appId 是否能获取到对应的 appSecret - String appId = request.getHeader(signature.appId()); - String appSecret = signatureRedisDAO.getAppSecret(appId); - Assert.notNull(appSecret, "[appId({})] 找不到对应的 appSecret", appId); - - // 2. 校验签名【重要!】 - String clientSignature = request.getHeader(signature.sign()); // 客户端签名 - String serverSignatureString = buildSignatureString(signature, request, appSecret); // 服务端签名字符串 - String serverSignature = DigestUtil.sha256Hex(serverSignatureString); // 服务端签名 - if (ObjUtil.notEqual(clientSignature, serverSignature)) { - return false; - } - - // 3. 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 ) - String nonce = request.getHeader(signature.nonce()); - signatureRedisDAO.setNonce(nonce, signature.timeout() * 2, signature.timeUnit()); - return true; - } - - /** - * 校验请求头加签参数 - * - * 1. appId 是否为空 - * 2. timestamp 是否为空,请求是否已经超时,默认 10 分钟 - * 3. nonce 是否为空,随机数是否 10 位以上,是否在规定时间内已经访问过了 - * 4. sign 是否为空 - * - * @param signature signature - * @param request request - * @return 是否校验 Header 通过 - */ - private boolean verifyHeaders(ApiSignature signature, HttpServletRequest request) { - // 1. 非空校验 - String appId = request.getHeader(signature.appId()); - if (StrUtil.isBlank(appId)) { - return false; - } - String timestamp = request.getHeader(signature.timestamp()); - if (StrUtil.isBlank(timestamp)) { - return false; - } - String nonce = request.getHeader(signature.nonce()); - if (StrUtil.length(nonce) < 10) { - return false; - } - String sign = request.getHeader(signature.sign()); - if (StrUtil.isBlank(sign)) { - return false; - } - - // 2. 检查 timestamp 是否超出允许的范围 (重点一:此处需要取绝对值) - long expireTime = signature.timeUnit().toMillis(signature.timeout()); - long requestTimestamp = Long.parseLong(timestamp); - long timestampDisparity = Math.abs(System.currentTimeMillis() - requestTimestamp); - if (timestampDisparity > expireTime) { - return false; - } - - // 3. 检查 nonce 是否存在,有且仅能使用一次 - return signatureRedisDAO.getNonce(nonce) == null; - } - - /** - * 构建签名字符串 - * - * 格式为 = 请求参数 + 请求体 + 请求头 + 密钥 - * - * @param signature signature - * @param request request - * @param appSecret appSecret - * @return 签名字符串 - */ - private String buildSignatureString(ApiSignature signature, HttpServletRequest request, String appSecret) { - SortedMap<String, String> parameterMap = getRequestParameterMap(request); // 请求头 - SortedMap<String, String> headerMap = getRequestHeaderMap(signature, request); // 请求参数 - String requestBody = StrUtil.nullToDefault(ServletUtils.getBody(request), ""); // 请求体 - return MapUtil.join(parameterMap, "&", "=") - + requestBody - + MapUtil.join(headerMap, "&", "=") - + appSecret; - } - - /** - * 获取请求头加签参数 Map - * - * @param request 请求 - * @param signature 签名注解 - * @return signature params - */ - private static SortedMap<String, String> getRequestHeaderMap(ApiSignature signature, HttpServletRequest request) { - SortedMap<String, String> sortedMap = new TreeMap<>(); - sortedMap.put(signature.appId(), request.getHeader(signature.appId())); - sortedMap.put(signature.timestamp(), request.getHeader(signature.timestamp())); - sortedMap.put(signature.nonce(), request.getHeader(signature.nonce())); - return sortedMap; - } - - /** - * 获取请求参数 Map - * - * @param request 请求 - * @return queryParams - */ - private static SortedMap<String, String> getRequestParameterMap(HttpServletRequest request) { - SortedMap<String, String> sortedMap = new TreeMap<>(); - for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) { - sortedMap.put(entry.getKey(), entry.getValue()[0]); - } - return sortedMap; - } - -} - diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/core/redis/ApiSignatureRedisDAO.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/core/redis/ApiSignatureRedisDAO.java deleted file mode 100644 index b6c8c8e..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/core/redis/ApiSignatureRedisDAO.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.iailab.framework.signature.core.redis; - -import lombok.AllArgsConstructor; -import org.springframework.data.redis.core.StringRedisTemplate; - -import java.util.concurrent.TimeUnit; - -/** - * HTTP API 签名 Redis DAO - * - * @author Zhougang - */ -@AllArgsConstructor -public class ApiSignatureRedisDAO { - - private final StringRedisTemplate stringRedisTemplate; - - /** - * 验签随机数 - * - * KEY 格式:signature_nonce:%s // 参数为 随机数 - * VALUE 格式:String - * 过期时间:不固定 - */ - private static final String SIGNATURE_NONCE = "api_signature_nonce:%s"; - - /** - * 签名密钥 - * - * HASH 结构 - * KEY 格式:%s // 参数为 appid - * VALUE 格式:String - * 过期时间:永不过期(预加载到 Redis) - */ - private static final String SIGNATURE_APPID = "api_signature_app"; - - // ========== 验签随机数 ========== - - public String getNonce(String nonce) { - return stringRedisTemplate.opsForValue().get(formatNonceKey(nonce)); - } - - public void setNonce(String nonce, int time, TimeUnit timeUnit) { - stringRedisTemplate.opsForValue().set(formatNonceKey(nonce), "", time, timeUnit); - } - - private static String formatNonceKey(String key) { - return String.format(SIGNATURE_NONCE, key); - } - - // ========== 签名密钥 ========== - - public String getAppSecret(String appId) { - return (String) stringRedisTemplate.opsForHash().get(SIGNATURE_APPID, appId); - } - -} diff --git a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/package-info.java b/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/package-info.java deleted file mode 100644 index 1f5f8ac..0000000 --- a/iailab-framework/iailab-common-protection/src/main/java/com/iailab/framework/signature/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * HTTP API 签名,校验安全性 - * - * @see <a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3>微信支付 —— 安全规范</a> - */ -package com.iailab.framework.signature; \ No newline at end of file diff --git a/iailab-framework/iailab-common-protection/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/iailab-framework/iailab-common-protection/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 85259d2..0000000 --- a/iailab-framework/iailab-common-protection/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,3 +0,0 @@ -com.iailab.framework.idempotent.config.IailabIdempotentConfiguration -com.iailab.framework.lock4j.config.IailabLock4jConfiguration -com.iailab.framework.ratelimiter.config.IailabRateLimiterConfiguration \ No newline at end of file diff --git a/iailab-framework/iailab-common-protection/src/test/java/com/iailab/framework/signature/core/ApiSignatureTest.java b/iailab-framework/iailab-common-protection/src/test/java/com/iailab/framework/signature/core/ApiSignatureTest.java deleted file mode 100644 index 8c94e79..0000000 --- a/iailab-framework/iailab-common-protection/src/test/java/com/iailab/framework/signature/core/ApiSignatureTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.iailab.framework.signature.core; - -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.IdUtil; -import cn.hutool.crypto.digest.DigestUtil; -import com.iailab.framework.signature.core.annotation.ApiSignature; -import com.iailab.framework.signature.core.aop.ApiSignatureAspect; -import com.iailab.framework.signature.core.redis.ApiSignatureRedisDAO; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import javax.servlet.http.HttpServletRequest; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.StringReader; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -/** - * {@link ApiSignatureTest} 的单元测试 - */ -@ExtendWith(MockitoExtension.class) -public class ApiSignatureTest { - - @InjectMocks - private ApiSignatureAspect apiSignatureAspect; - - @Mock - private ApiSignatureRedisDAO signatureRedisDAO; - - @Test - public void testSignatureGet() throws IOException { - // 搞一个签名 - Long timestamp = System.currentTimeMillis(); - String nonce = IdUtil.randomUUID(); - String appId = "xxxxxx"; - String appSecret = "yyyyyy"; - String signString = "k1=v1&v1=k1testappId=xxxxxx&nonce=" + nonce + "×tamp=" + timestamp + "yyyyyy"; - String sign = DigestUtil.sha256Hex(signString); - - // 准备参数 - ApiSignature apiSignature = mock(ApiSignature.class); - when(apiSignature.appId()).thenReturn("appId"); - when(apiSignature.timestamp()).thenReturn("timestamp"); - when(apiSignature.nonce()).thenReturn("nonce"); - when(apiSignature.sign()).thenReturn("sign"); - when(apiSignature.timeout()).thenReturn(60); - when(apiSignature.timeUnit()).thenReturn(TimeUnit.SECONDS); - HttpServletRequest request = mock(HttpServletRequest.class); - when(request.getHeader(eq("appId"))).thenReturn(appId); - when(request.getHeader(eq("timestamp"))).thenReturn(String.valueOf(timestamp)); - when(request.getHeader(eq("nonce"))).thenReturn(nonce); - when(request.getHeader(eq("sign"))).thenReturn(sign); - when(request.getParameterMap()).thenReturn(MapUtil.<String, String[]>builder() - .put("v1", new String[]{"k1"}).put("k1", new String[]{"v1"}).build()); - when(request.getContentType()).thenReturn("application/json"); - when(request.getReader()).thenReturn(new BufferedReader(new StringReader("test"))); - // mock 方法 - when(signatureRedisDAO.getAppSecret(eq(appId))).thenReturn(appSecret); - - // 调用 - boolean result = apiSignatureAspect.verifySignature(apiSignature, request); - // 断言结果 - assertTrue(result); - // 断言调用 - verify(signatureRedisDAO).setNonce(eq(nonce), eq(120), eq(TimeUnit.SECONDS)); - } - -} diff --git a/iailab-framework/iailab-common-redis/pom.xml b/iailab-framework/iailab-common-redis/pom.xml deleted file mode 100644 index 5940dae..0000000 --- a/iailab-framework/iailab-common-redis/pom.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>com.iailab</groupId> - <artifactId>iailab-framework</artifactId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-redis</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description>Redis 封装拓展</description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common</artifactId> - </dependency> - - <!-- DB 相关 --> - <dependency> - <groupId>org.redisson</groupId> - <artifactId>redisson-spring-boot-starter</artifactId> - </dependency> - - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-cache</artifactId> <!-- 实现对 Caches 的自动化配置 --> - </dependency> - - <dependency> - <groupId>com.fasterxml.jackson.datatype</groupId> - <artifactId>jackson-datatype-jsr310</artifactId> - </dependency> - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/config/IailabCacheAutoConfiguration.java b/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/config/IailabCacheAutoConfiguration.java deleted file mode 100644 index 167d147..0000000 --- a/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/config/IailabCacheAutoConfiguration.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.iailab.framework.redis.config; - -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.redis.core.TimeoutRedisCacheManager; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.cache.CacheProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; -import org.springframework.data.redis.cache.BatchStrategies; -import org.springframework.data.redis.cache.RedisCacheConfiguration; -import org.springframework.data.redis.cache.RedisCacheManager; -import org.springframework.data.redis.cache.RedisCacheWriter; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.RedisSerializationContext; -import org.springframework.util.StringUtils; - -import java.util.Objects; - -import static com.iailab.framework.redis.config.IailabRedisAutoConfiguration.buildRedisSerializer; - -/** - * Cache 配置类,基于 Redis 实现 - */ -@AutoConfiguration -@EnableConfigurationProperties({CacheProperties.class, IailabCacheProperties.class}) -@EnableCaching -public class IailabCacheAutoConfiguration { - - /** - * RedisCacheConfiguration Bean - * <p> - * 参考 org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration 的 createConfiguration 方法 - */ - @Bean - @Primary - public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) { - RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); - // 设置使用 : 单冒号,而不是双 :: 冒号,避免 Redis Desktop Manager 多余空格 - // 详细可见 https://blog.csdn.net/chuixue24/article/details/103928965 博客 - // 再次修复单冒号,而不是双 :: 冒号问题,Issues 详情:https://gitee.com/zhijiantianya/iailab-cloud/issues/I86VY2 - config = config.computePrefixWith(cacheName -> { - String keyPrefix = cacheProperties.getRedis().getKeyPrefix(); - if (StringUtils.hasText(keyPrefix)) { - keyPrefix = keyPrefix.lastIndexOf(StrUtil.COLON) == -1 ? keyPrefix + StrUtil.COLON : keyPrefix; - return keyPrefix + cacheName + StrUtil.COLON; - } - return cacheName + StrUtil.COLON; - }); - // 设置使用 JSON 序列化方式 - config = config.serializeValuesWith( - RedisSerializationContext.SerializationPair.fromSerializer(buildRedisSerializer())); - - // 设置 CacheProperties.Redis 的属性 - CacheProperties.Redis redisProperties = cacheProperties.getRedis(); - if (redisProperties.getTimeToLive() != null) { - config = config.entryTtl(redisProperties.getTimeToLive()); - } - if (!redisProperties.isCacheNullValues()) { - config = config.disableCachingNullValues(); - } - if (!redisProperties.isUseKeyPrefix()) { - config = config.disableKeyPrefix(); - } - return config; - } - - @Bean - public RedisCacheManager redisCacheManager(RedisTemplate<String, Object> redisTemplate, - RedisCacheConfiguration redisCacheConfiguration, - IailabCacheProperties iailabCacheProperties) { - // 创建 RedisCacheWriter 对象 - RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory()); - RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory, - BatchStrategies.scan(iailabCacheProperties.getRedisScanBatchSize())); - // 创建 TenantRedisCacheManager 对象 - return new TimeoutRedisCacheManager(cacheWriter, redisCacheConfiguration); - } - -} diff --git a/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/config/IailabCacheProperties.java b/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/config/IailabCacheProperties.java deleted file mode 100644 index 2cc155f..0000000 --- a/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/config/IailabCacheProperties.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.iailab.framework.redis.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.validation.annotation.Validated; - -/** - * Cache 配置项 - * - * @author Wanwan - */ -@ConfigurationProperties("iailab.cache") -@Data -@Validated -public class IailabCacheProperties { - - /** - * {@link #redisScanBatchSize} 默认值 - */ - private static final Integer REDIS_SCAN_BATCH_SIZE_DEFAULT = 30; - - /** - * redis scan 一次返回数量 - */ - private Integer redisScanBatchSize = REDIS_SCAN_BATCH_SIZE_DEFAULT; - -} diff --git a/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/config/IailabRedisAutoConfiguration.java b/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/config/IailabRedisAutoConfiguration.java deleted file mode 100644 index e63df54..0000000 --- a/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/config/IailabRedisAutoConfiguration.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.iailab.framework.redis.config; - -import cn.hutool.core.util.ReflectUtil; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import org.redisson.spring.starter.RedissonAutoConfiguration; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.RedisSerializer; - -/** - * Redis 配置类 - */ -@AutoConfiguration(before = RedissonAutoConfiguration.class) // 目的:使用自己定义的 RedisTemplate Bean -public class IailabRedisAutoConfiguration { - - /** - * 创建 RedisTemplate Bean,使用 JSON 序列化方式 - */ - @Bean - public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { - // 创建 RedisTemplate 对象 - RedisTemplate<String, Object> template = new RedisTemplate<>(); - // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友,可以自己去撸下。 - template.setConnectionFactory(factory); - // 使用 String 序列化方式,序列化 KEY 。 - template.setKeySerializer(RedisSerializer.string()); - template.setHashKeySerializer(RedisSerializer.string()); - // 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。 - template.setValueSerializer(buildRedisSerializer()); - template.setHashValueSerializer(buildRedisSerializer()); - return template; - } - - public static RedisSerializer<?> buildRedisSerializer() { - RedisSerializer<Object> json = RedisSerializer.json(); - // 解决 LocalDateTime 的序列化 - ObjectMapper objectMapper = (ObjectMapper) ReflectUtil.getFieldValue(json, "mapper"); - objectMapper.registerModules(new JavaTimeModule()); - return json; - } - -} diff --git a/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/core/TimeoutRedisCacheManager.java b/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/core/TimeoutRedisCacheManager.java deleted file mode 100644 index de86678..0000000 --- a/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/core/TimeoutRedisCacheManager.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.iailab.framework.redis.core; - -import cn.hutool.core.util.NumberUtil; -import cn.hutool.core.util.StrUtil; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.data.redis.cache.RedisCache; -import org.springframework.data.redis.cache.RedisCacheConfiguration; -import org.springframework.data.redis.cache.RedisCacheManager; -import org.springframework.data.redis.cache.RedisCacheWriter; - -import java.time.Duration; - -/** - * 支持自定义过期时间的 {@link RedisCacheManager} 实现类 - * - * 在 {@link Cacheable#cacheNames()} 格式为 "key#ttl" 时,# 后面的 ttl 为过期时间。 - * 单位为最后一个字母(支持的单位有:d 天,h 小时,m 分钟,s 秒),默认单位为 s 秒 - * - * @author iailab - */ -public class TimeoutRedisCacheManager extends RedisCacheManager { - - private static final String SPLIT = "#"; - - public TimeoutRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) { - super(cacheWriter, defaultCacheConfiguration); - } - - @Override - protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) { - if (StrUtil.isEmpty(name)) { - return super.createRedisCache(name, cacheConfig); - } - // 如果使用 # 分隔,大小不为 2,则说明不使用自定义过期时间 - String[] names = StrUtil.splitToArray(name, SPLIT); - if (names.length != 2) { - return super.createRedisCache(name, cacheConfig); - } - - // 核心:通过修改 cacheConfig 的过期时间,实现自定义过期时间 - if (cacheConfig != null) { - // 移除 # 后面的 : 以及后面的内容,避免影响解析 - String ttlStr = StrUtil.subBefore(names[1], StrUtil.COLON, false); // 获得 ttlStr 时间部分 - names[1] = StrUtil.subAfter(names[1], ttlStr, false); // 移除掉 ttlStr 时间部分 - // 解析时间 - Duration duration = parseDuration(ttlStr); - cacheConfig = cacheConfig.entryTtl(duration); - } - - // 创建 RedisCache 对象,需要忽略掉 ttlStr - return super.createRedisCache(names[0] + names[1], cacheConfig); - } - - /** - * 解析过期时间 Duration - * - * @param ttlStr 过期时间字符串 - * @return 过期时间 Duration - */ - private Duration parseDuration(String ttlStr) { - String timeUnit = StrUtil.subSuf(ttlStr, -1); - switch (timeUnit) { - case "d": - return Duration.ofDays(removeDurationSuffix(ttlStr)); - case "h": - return Duration.ofHours(removeDurationSuffix(ttlStr)); - case "m": - return Duration.ofMinutes(removeDurationSuffix(ttlStr)); - case "s": - return Duration.ofSeconds(removeDurationSuffix(ttlStr)); - default: - return Duration.ofSeconds(Long.parseLong(ttlStr)); - } - } - - /** - * 移除多余的后缀,返回具体的时间 - * - * @param ttlStr 过期时间字符串 - * @return 时间 - */ - private Long removeDurationSuffix(String ttlStr) { - return NumberUtil.parseLong(StrUtil.sub(ttlStr, 0, ttlStr.length() - 1)); - } - -} diff --git a/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/package-info.java b/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/package-info.java deleted file mode 100644 index 6ce6b3a..0000000 --- a/iailab-framework/iailab-common-redis/src/main/java/com/iailab/framework/redis/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 采用 Spring Data Redis 操作 Redis,底层使用 Redisson 作为客户端 - */ -package com.iailab.framework.redis; diff --git a/iailab-framework/iailab-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/iailab-framework/iailab-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 1b9e718..0000000 --- a/iailab-framework/iailab-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,2 +0,0 @@ -com.iailab.framework.redis.config.IailabRedisAutoConfiguration -com.iailab.framework.redis.config.IailabCacheAutoConfiguration diff --git a/iailab-framework/iailab-common-rpc/pom.xml b/iailab-framework/iailab-common-rpc/pom.xml deleted file mode 100644 index d27e89a..0000000 --- a/iailab-framework/iailab-common-rpc/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <artifactId>iailab-framework</artifactId> - <groupId>com.iailab</groupId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-rpc</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description> - OpenFeign:提供 RESTful API 的调用 - </description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common</artifactId> - </dependency> - - <!-- RPC 远程调用相关 --> - <dependency> - <groupId>org.springframework.cloud</groupId> - <artifactId>spring-cloud-starter-loadbalancer</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.cloud</groupId> - <artifactId>spring-cloud-starter-openfeign</artifactId> - </dependency> - <dependency> - <groupId>io.github.openfeign</groupId> - <artifactId>feign-okhttp</artifactId> - </dependency> - - <!-- 工具相关 --> - <dependency> - <groupId>jakarta.validation</groupId> - <artifactId>jakarta.validation-api</artifactId> - </dependency> - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common-rpc/src/main/java/com/iailab/framework/rpc/config/package-info.java b/iailab-framework/iailab-common-rpc/src/main/java/com/iailab/framework/rpc/config/package-info.java deleted file mode 100644 index 60ee765..0000000 --- a/iailab-framework/iailab-common-rpc/src/main/java/com/iailab/framework/rpc/config/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占坑 TODO - */ -package com.iailab.framework.rpc.config; diff --git a/iailab-framework/iailab-common-rpc/src/main/java/com/iailab/framework/rpc/core/package-info.java b/iailab-framework/iailab-common-rpc/src/main/java/com/iailab/framework/rpc/core/package-info.java deleted file mode 100644 index c89314e..0000000 --- a/iailab-framework/iailab-common-rpc/src/main/java/com/iailab/framework/rpc/core/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占坑 TODO - */ -package com.iailab.framework.rpc.core; diff --git a/iailab-framework/iailab-common-rpc/src/main/java/com/iailab/framework/rpc/package-info.java b/iailab-framework/iailab-common-rpc/src/main/java/com/iailab/framework/rpc/package-info.java deleted file mode 100644 index b263430..0000000 --- a/iailab-framework/iailab-common-rpc/src/main/java/com/iailab/framework/rpc/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * OpenFeign:提供 RESTful API 的调用 - * - * @author iailab - */ -package com.iailab.framework.rpc; diff --git a/iailab-framework/iailab-common-security/pom.xml b/iailab-framework/iailab-common-security/pom.xml deleted file mode 100644 index feb7855..0000000 --- a/iailab-framework/iailab-common-security/pom.xml +++ /dev/null @@ -1,78 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>com.iailab</groupId> - <artifactId>iailab-framework</artifactId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-security</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description> - 1. security:用户的认证、权限的校验,实现「谁」可以做「什么事」 - 2. operatelog:操作日志,实现「谁」在「什么时间」对「什么」做了「什么事」 - </description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common</artifactId> - </dependency> - - <!-- Spring 核心 --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-aop</artifactId> - </dependency> - - <!-- Web 相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-web</artifactId> - </dependency> - <!-- spring boot 配置所需依赖 --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-configuration-processor</artifactId> - <optional>true</optional> - </dependency> - - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-security</artifactId> - </dependency> - - <!-- RPC 远程调用相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-rpc</artifactId> - <optional>true</optional> - </dependency> - - <!-- 业务组件 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-module-system-api</artifactId> <!-- 需要使用它,进行 Token 的校验 --> - <version>${revision}</version> - </dependency> - - <!-- 工具类相关 --> - <dependency> - <groupId>com.google.guava</groupId> - <artifactId>guava</artifactId> - </dependency> - - <dependency> - <!-- Spring Boot 通用操作日志组件,基于注解实现 --> - <!-- 此组件解决的问题是:「谁」在「什么时间」对「什么」做了「什么事」 --> - <groupId>io.github.mouzt</groupId> - <artifactId>bizlog-sdk</artifactId> - </dependency> - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/config/IailabOperateLogConfiguration.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/config/IailabOperateLogConfiguration.java deleted file mode 100644 index 22b90b8..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/config/IailabOperateLogConfiguration.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.iailab.framework.operatelog.config; - -import com.iailab.framework.operatelog.core.service.LogRecordServiceImpl; -import com.mzt.logapi.service.ILogRecordService; -import com.mzt.logapi.starter.annotation.EnableLogRecord; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; - -/** - * 操作日志配置类 - * - * @author HUIHUI - */ -@EnableLogRecord(tenant = "") // 貌似用不上 tenant 这玩意给个空好啦 -@AutoConfiguration -@Slf4j -public class IailabOperateLogConfiguration { - - @Bean - @Primary - public ILogRecordService iLogRecordServiceImpl() { - return new LogRecordServiceImpl(); - } - -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/config/IailabOperateLogRpcAutoConfiguration.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/config/IailabOperateLogRpcAutoConfiguration.java deleted file mode 100644 index ef72d29..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/config/IailabOperateLogRpcAutoConfiguration.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.iailab.framework.operatelog.config; - -import com.iailab.module.system.api.logger.OperateLogApi; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.cloud.openfeign.EnableFeignClients; - -/** - * OperateLog 使用到 Feign 的配置项 - * - * @author iailab - */ -@AutoConfiguration -@EnableFeignClients(clients = {OperateLogApi.class}) // 主要是引入相关的 API 服务 -public class IailabOperateLogRpcAutoConfiguration { -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/core/package-info.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/core/package-info.java deleted file mode 100644 index 80abe28..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/core/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占位,无特殊作用 - */ -package com.iailab.framework.operatelog.core; \ No newline at end of file diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/core/service/LogRecordServiceImpl.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/core/service/LogRecordServiceImpl.java deleted file mode 100644 index 0e95b34..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/core/service/LogRecordServiceImpl.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.iailab.framework.operatelog.core.service; - -import com.iailab.framework.common.util.monitor.TracerUtils; -import com.iailab.framework.common.util.servlet.ServletUtils; -import com.iailab.framework.security.core.LoginUser; -import com.iailab.framework.security.core.util.SecurityFrameworkUtils; -import com.iailab.module.system.api.logger.OperateLogApi; -import com.iailab.module.system.api.logger.dto.OperateLogCreateReqDTO; -import com.mzt.logapi.beans.LogRecord; -import com.mzt.logapi.service.ILogRecordService; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; -import java.util.List; - -/** - * 操作日志 ILogRecordService 实现类 - * - * 基于 {@link OperateLogApi} 实现,记录操作日志 - * - * @author HUIHUI - */ -@Slf4j -public class LogRecordServiceImpl implements ILogRecordService { - - @Resource - private OperateLogApi operateLogApi; - - @Override - public void record(LogRecord logRecord) { - // 1. 补全通用字段 - OperateLogCreateReqDTO reqDTO = new OperateLogCreateReqDTO(); - reqDTO.setTraceId(TracerUtils.getTraceId()); - // 补充用户信息 - fillUserFields(reqDTO); - // 补全模块信息 - fillModuleFields(reqDTO, logRecord); - // 补全请求信息 - fillRequestFields(reqDTO); - - // 2. 异步记录日志 - operateLogApi.createOperateLog(reqDTO); - } - - private static void fillUserFields(OperateLogCreateReqDTO reqDTO) { - // 使用 SecurityFrameworkUtils。因为要考虑,rpc、mq、job,它其实不是 web; - LoginUser loginUser = SecurityFrameworkUtils.getLoginUser(); - if (loginUser == null) { - return; - } - reqDTO.setUserId(loginUser.getId()); - reqDTO.setUserType(loginUser.getUserType()); - } - - public static void fillModuleFields(OperateLogCreateReqDTO reqDTO, LogRecord logRecord) { - reqDTO.setType(logRecord.getType()); // 大模块类型,例如:CRM 客户 - reqDTO.setSubType(logRecord.getSubType());// 操作名称,例如:转移客户 - reqDTO.setBizId(Long.parseLong(logRecord.getBizNo())); // 业务编号,例如:客户编号 - reqDTO.setAction(logRecord.getAction());// 操作内容,例如:修改编号为 1 的用户信息,将性别从男改成女,将姓名从平台改成源码。 - reqDTO.setExtra(logRecord.getExtra()); // 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ),例如说,记录订单编号,{ orderId: "1"} - } - - private static void fillRequestFields(OperateLogCreateReqDTO reqDTO) { - // 获得 Request 对象 - HttpServletRequest request = ServletUtils.getRequest(); - if (request == null) { - return; - } - // 补全请求信息 - reqDTO.setRequestMethod(request.getMethod()); - reqDTO.setRequestUrl(request.getRequestURI()); - reqDTO.setUserIp(ServletUtils.getClientIP(request)); - reqDTO.setUserAgent(ServletUtils.getUserAgent(request)); - } - - @Override - public List<LogRecord> queryLog(String bizNo, String type) { - throw new UnsupportedOperationException("使用 OperateLogApi 进行操作日志的查询"); - } - - @Override - public List<LogRecord> queryLogByBizNo(String bizNo, String type, String subType) { - throw new UnsupportedOperationException("使用 OperateLogApi 进行操作日志的查询"); - } - -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/package-info.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/package-info.java deleted file mode 100644 index 9df12ab..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/operatelog/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * 基于 mzt-log 框架 - * 实现操作日志功能 - * - * @author HUIHUI - */ -package com.iailab.framework.operatelog; diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/AuthorizeRequestsCustomizer.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/AuthorizeRequestsCustomizer.java deleted file mode 100644 index 063c85e..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/AuthorizeRequestsCustomizer.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.iailab.framework.security.config; - -import com.iailab.framework.web.config.WebProperties; -import org.springframework.core.Ordered; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; -import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; - -import javax.annotation.Resource; - -/** - * 自定义的 URL 的安全配置 - * 目的:每个 Maven Module 可以自定义规则! - * - * @author iailab - */ -public abstract class AuthorizeRequestsCustomizer - implements Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry>, Ordered { - - @Resource - private WebProperties webProperties; - - protected String buildAdminApi(String url) { - return webProperties.getAdminApi().getPrefix() + url; - } - - protected String buildAppApi(String url) { - return webProperties.getAppApi().getPrefix() + url; - } - - @Override - public int getOrder() { - return 0; - } - -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/IailabSecurityAutoConfiguration.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/IailabSecurityAutoConfiguration.java deleted file mode 100644 index 4ab66ee..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/IailabSecurityAutoConfiguration.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.iailab.framework.security.config; - -import cn.hutool.extra.spring.SpringUtil; -import com.iailab.framework.security.core.aop.PreAuthenticatedAspect; -import com.iailab.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy; -import com.iailab.framework.security.core.filter.TokenAuthenticationFilter; -import com.iailab.framework.security.core.handler.AccessDeniedHandlerImpl; -import com.iailab.framework.security.core.handler.AuthenticationEntryPointImpl; -import com.iailab.framework.security.core.service.SecurityFrameworkService; -import com.iailab.framework.security.core.service.SecurityFrameworkServiceImpl; -import com.iailab.framework.web.core.handler.GlobalExceptionHandler; -import com.iailab.module.system.api.oauth2.OAuth2TokenApi; -import com.iailab.module.system.api.permission.PermissionApi; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.config.MethodInvokingFactoryBean; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.AutoConfigureOrder; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.access.AccessDeniedHandler; - -import javax.annotation.Resource; - -/** - * Spring Security 自动配置类,主要用于相关组件的配置 - * - * 注意,不能和 {@link IailabWebSecurityConfigurerAdapter} 用一个,原因是会导致初始化报错。 - * 参见 https://stackoverflow.com/questions/53847050/spring-boot-delegatebuilder-cannot-be-null-on-autowiring-authenticationmanager 文档。 - * - * @author iailab - */ -@AutoConfiguration -@AutoConfigureOrder(-1) // 目的:先于 Spring Security 自动配置,避免一键改包后,org.* 基础包无法生效 -@EnableConfigurationProperties(SecurityProperties.class) -public class IailabSecurityAutoConfiguration { - - @Resource - private SecurityProperties securityProperties; - - /** - * 处理用户未登录拦截的切面的 Bean - */ - @Bean - public PreAuthenticatedAspect preAuthenticatedAspect() { - return new PreAuthenticatedAspect(); - } - - /** - * 认证失败处理类 Bean - */ - @Bean - public AuthenticationEntryPoint authenticationEntryPoint() { - return new AuthenticationEntryPointImpl(); - } - - /** - * 权限不够处理器 Bean - */ - @Bean - public AccessDeniedHandler accessDeniedHandler() { - return new AccessDeniedHandlerImpl(); - } - - /** - * Spring Security 加密器 - * 考虑到安全性,这里采用 BCryptPasswordEncoder 加密器 - * - * @see <a href="http://stackabuse.com/password-encoding-with-spring-security/">Password Encoding with Spring Security</a> - */ - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(securityProperties.getPasswordEncoderLength()); - } - - /** - * Token 认证过滤器 Bean - */ - @Bean - public TokenAuthenticationFilter authenticationTokenFilter(GlobalExceptionHandler globalExceptionHandler, - OAuth2TokenApi oauth2TokenApi) { - try { - OAuth2TokenApi oAuth2TokenApi = SpringUtil.getBean("aAuth2TokenApiImpl", OAuth2TokenApi.class); - if (oAuth2TokenApi != null) { - oauth2TokenApi = oAuth2TokenApi; - } - } catch (Exception ignored) {} - return new TokenAuthenticationFilter(securityProperties, globalExceptionHandler, oauth2TokenApi); - } - - @Bean("ss") // 使用 Spring Security 的缩写,方便使用 - public SecurityFrameworkService securityFrameworkService(PermissionApi permissionApi) { - try { - PermissionApi permissionApiImpl = SpringUtil.getBean("permissionApiImpl", PermissionApi.class); - if (permissionApiImpl != null) { - permissionApi = permissionApiImpl; - } - } catch (Exception ignored) {} - return new SecurityFrameworkServiceImpl(permissionApi); - } - - /** - * 声明调用 {@link SecurityContextHolder#setStrategyName(String)} 方法, - * 设置使用 {@link TransmittableThreadLocalSecurityContextHolderStrategy} 作为 Security 的上下文策略 - */ - @Bean - public MethodInvokingFactoryBean securityContextHolderMethodInvokingFactoryBean() { - MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean(); - methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class); - methodInvokingFactoryBean.setTargetMethod("setStrategyName"); - methodInvokingFactoryBean.setArguments(TransmittableThreadLocalSecurityContextHolderStrategy.class.getName()); - return methodInvokingFactoryBean; - } - -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/IailabSecurityRpcAutoConfiguration.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/IailabSecurityRpcAutoConfiguration.java deleted file mode 100644 index c707419..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/IailabSecurityRpcAutoConfiguration.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.security.config; - -import com.iailab.framework.security.core.rpc.LoginUserRequestInterceptor; -import com.iailab.module.system.api.oauth2.OAuth2TokenApi; -import com.iailab.module.system.api.permission.PermissionApi; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.cloud.openfeign.EnableFeignClients; -import org.springframework.context.annotation.Bean; - -/** - * Security 使用到 Feign 的配置项 - * - * @author iailab - */ -@AutoConfiguration -@EnableFeignClients(clients = {OAuth2TokenApi.class, // 主要是引入相关的 API 服务 - PermissionApi.class}) -public class IailabSecurityRpcAutoConfiguration { - - @Bean - public LoginUserRequestInterceptor loginUserRequestInterceptor() { - return new LoginUserRequestInterceptor(); - } - -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/IailabWebSecurityConfigurerAdapter.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/IailabWebSecurityConfigurerAdapter.java deleted file mode 100644 index 4813ecf..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/IailabWebSecurityConfigurerAdapter.java +++ /dev/null @@ -1,217 +0,0 @@ -package com.iailab.framework.security.config; - -import cn.hutool.core.collection.CollUtil; -import com.iailab.framework.security.core.filter.TokenAuthenticationFilter; -import com.iailab.framework.web.config.WebProperties; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.AutoConfigureOrder; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; -import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.mvc.method.RequestMappingInfo; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -import org.springframework.web.util.pattern.PathPattern; - -import javax.annotation.Resource; -import javax.annotation.security.PermitAll; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static com.iailab.framework.common.util.collection.CollectionUtils.convertList; - -/** - * 自定义的 Spring Security 配置适配器实现 - * - * @author iailab - */ -@AutoConfiguration -@AutoConfigureOrder(-1) // 目的:先于 Spring Security 自动配置,避免一键改包后,org.* 基础包无法生效 -@EnableMethodSecurity(securedEnabled = true) -public class IailabWebSecurityConfigurerAdapter { - - @Resource - private WebProperties webProperties; - @Resource - private SecurityProperties securityProperties; - - /** - * 认证失败处理类 Bean - */ - @Resource - private AuthenticationEntryPoint authenticationEntryPoint; - /** - * 权限不够处理器 Bean - */ - @Resource - private AccessDeniedHandler accessDeniedHandler; - /** - * Token 认证过滤器 Bean - */ - @Resource - private TokenAuthenticationFilter authenticationTokenFilter; - - /** - * 自定义的权限映射 Bean 们 - * - * @see #filterChain(HttpSecurity) - */ - @Resource - private List<AuthorizeRequestsCustomizer> authorizeRequestsCustomizers; - - @Resource - private ApplicationContext applicationContext; - - /** - * 由于 Spring Security 创建 AuthenticationManager 对象时,没声明 @Bean 注解,导致无法被注入 - * 通过覆写父类的该方法,添加 @Bean 注解,解决该问题 - */ - @Bean - public AuthenticationManager authenticationManagerBean(AuthenticationConfiguration authenticationConfiguration) throws Exception { - return authenticationConfiguration.getAuthenticationManager(); - } - - /** - * 配置 URL 的安全配置 - * - * anyRequest | 匹配所有请求路径 - * access | SpringEl表达式结果为true时可以访问 - * anonymous | 匿名可以访问 - * denyAll | 用户不能访问 - * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) - * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 - * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 - * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 - * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 - * hasRole | 如果有参数,参数表示角色,则其角色可以访问 - * permitAll | 用户可以任意访问 - * rememberMe | 允许通过remember-me登录的用户访问 - * authenticated | 用户登录后可访问 - */ - @Bean - protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { - // 登出 - httpSecurity - // 开启跨域 - .cors(Customizer.withDefaults()) - // CSRF 禁用,因为不使用 Session - .csrf(AbstractHttpConfigurer::disable) - // 基于 token 机制,所以不需要 Session - .sessionManagement(c -> c.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .headers(c -> c.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) - // 一堆自定义的 Spring Security 处理器 - .exceptionHandling(c -> c.authenticationEntryPoint(authenticationEntryPoint) - .accessDeniedHandler(accessDeniedHandler)); - // 登录、登录暂时不使用 Spring Security 的拓展点,主要考虑一方面拓展多用户、多种登录方式相对复杂,一方面用户的学习成本较高 - - // 获得 @PermitAll 带来的 URL 列表,免登录 - Multimap<HttpMethod, String> permitAllUrls = getPermitAllUrlsFromAnnotations(); - // 设置每个请求的权限 - httpSecurity - // ①:全局共享规则 - .authorizeHttpRequests(c -> c - // 1.1 静态资源,可匿名访问 - .requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll() - // 1.2 设置 @PermitAll 无需认证 - .requestMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll() - .requestMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll() - .requestMatchers(HttpMethod.PUT, permitAllUrls.get(HttpMethod.PUT).toArray(new String[0])).permitAll() - .requestMatchers(HttpMethod.DELETE, permitAllUrls.get(HttpMethod.DELETE).toArray(new String[0])).permitAll() - .requestMatchers(HttpMethod.HEAD, permitAllUrls.get(HttpMethod.HEAD).toArray(new String[0])).permitAll() - .requestMatchers(HttpMethod.PATCH, permitAllUrls.get(HttpMethod.PATCH).toArray(new String[0])).permitAll() - // 1.3 基于 yudao.security.permit-all-urls 无需认证 - .requestMatchers(securityProperties.getPermitAllUrls().toArray(new String[0])).permitAll() - ) - // ②:每个项目的自定义规则 - .authorizeHttpRequests(c -> authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(c))) - // ③:兜底规则,必须认证 - .authorizeHttpRequests(c -> c.anyRequest().authenticated()); - - // 添加 Token Filter - httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); - return httpSecurity.build(); - } - - private String buildAppApi(String url) { - return webProperties.getAppApi().getPrefix() + url; - } - - private Multimap<HttpMethod, String> getPermitAllUrlsFromAnnotations() { - Multimap<HttpMethod, String> result = HashMultimap.create(); - // 获得接口对应的 HandlerMethod 集合 - RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) - applicationContext.getBean("requestMappingHandlerMapping"); - Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods(); - // 获得有 @PermitAll 注解的接口 - for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) { - HandlerMethod handlerMethod = entry.getValue(); - if (!handlerMethod.hasMethodAnnotation(PermitAll.class)) { - continue; - } - Set<String> urls = new HashSet<>(); - if (entry.getKey().getPatternsCondition() != null) { - urls.addAll(entry.getKey().getPatternsCondition().getPatterns()); - } - if (entry.getKey().getPathPatternsCondition() != null) { - urls.addAll(convertList(entry.getKey().getPathPatternsCondition().getPatterns(), PathPattern::getPatternString)); - } - if (urls.isEmpty()) { - continue; - } - - // 特殊:使用 @RequestMapping 注解,并且未写 method 属性,此时认为都需要免登录 - Set<RequestMethod> methods = entry.getKey().getMethodsCondition().getMethods(); - if (CollUtil.isEmpty(methods)) { - result.putAll(HttpMethod.GET, urls); - result.putAll(HttpMethod.POST, urls); - result.putAll(HttpMethod.PUT, urls); - result.putAll(HttpMethod.DELETE, urls); - result.putAll(HttpMethod.HEAD, urls); - result.putAll(HttpMethod.PATCH, urls); - continue; - } - // 根据请求方法,添加到 result 结果 - entry.getKey().getMethodsCondition().getMethods().forEach(requestMethod -> { - switch (requestMethod) { - case GET: - result.putAll(HttpMethod.GET, urls); - break; - case POST: - result.putAll(HttpMethod.POST, urls); - break; - case PUT: - result.putAll(HttpMethod.PUT, urls); - break; - case DELETE: - result.putAll(HttpMethod.DELETE, urls); - break; - case HEAD: - result.putAll(HttpMethod.HEAD, urls); - break; - case PATCH: - result.putAll(HttpMethod.PATCH, urls); - break; - } - }); - } - return result; - } - -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/SecurityProperties.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/SecurityProperties.java deleted file mode 100644 index 57a6b47..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/config/SecurityProperties.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.iailab.framework.security.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.validation.annotation.Validated; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.Collections; -import java.util.List; - -@ConfigurationProperties(prefix = "iailab.security") -@Validated -@Data -public class SecurityProperties { - - /** - * HTTP 请求时,访问令牌的请求 Header - */ - @NotEmpty(message = "Token Header 不能为空") - private String tokenHeader = "Authorization"; - /** - * HTTP 请求时,访问令牌的请求参数 - * - * 初始目的:解决 WebSocket 无法通过 header 传参,只能通过 token 参数拼接 - */ - @NotEmpty(message = "Token Parameter 不能为空") - private String tokenParameter = "token"; - - /** - * mock 模式的开关 - */ - @NotNull(message = "mock 模式的开关不能为空") - private Boolean mockEnable = false; - /** - * mock 模式的密钥 - * 一定要配置密钥,保证安全性 - */ - @NotEmpty(message = "mock 模式的密钥不能为空") // 这里设置了一个默认值,因为实际上只有 mockEnable 为 true 时才需要配置。 - private String mockSecret = "test"; - - /** - * 免登录的 URL 列表 - */ - private List<String> permitAllUrls = Collections.emptyList(); - - /** - * PasswordEncoder 加密复杂度,越高开销越大 - */ - private Integer passwordEncoderLength = 4; -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/LoginUser.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/LoginUser.java deleted file mode 100644 index 989ef9a..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/LoginUser.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.iailab.framework.security.core; - -import cn.hutool.core.map.MapUtil; -import com.iailab.framework.common.enums.UserTypeEnum; -import com.fasterxml.jackson.annotation.JsonIgnore; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * 登录用户信息 - * - * @author iailab - */ -@Data -public class LoginUser { - - public static final String INFO_KEY_NICKNAME = "nickname"; - public static final String INFO_KEY_DEPT_ID = "deptId"; - - /** - * 用户编号 - */ - private Long id; - /** - * 用户类型 - * - * 关联 {@link UserTypeEnum} - */ - private Integer userType; - /** - * 额外的用户信息 - */ - private Map<String, String> info; - /** - * 租户编号 - */ - private Long tenantId; - /** - * 授权范围 - */ - private List<String> scopes; - /** - * 过期时间 - */ - private LocalDateTime expiresTime; - /** - * 访问令牌 - */ - private String accessToken; - - // ========== 上下文 ========== - /** - * 上下文字段,不进行持久化 - * - * 1. 用于基于 LoginUser 维度的临时缓存 - */ - @JsonIgnore - private Map<String, Object> context; - - public void setContext(String key, Object value) { - if (context == null) { - context = new HashMap<>(); - } - context.put(key, value); - } - - public <T> T getContext(String key, Class<T> type) { - return MapUtil.get(context, key, type); - } - -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/annotations/PreAuthenticated.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/annotations/PreAuthenticated.java deleted file mode 100644 index f2f6c6c..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/annotations/PreAuthenticated.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.iailab.framework.security.core.annotations; - -import java.lang.annotation.*; - -/** - * 声明用户需要登录 - * - * 为什么不使用 {@link org.springframework.security.access.prepost.PreAuthorize} 注解,原因是不通过时,抛出的是认证不通过,而不是未登录 - * - * @author iailab - */ -@Target({ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -@Inherited -@Documented -public @interface PreAuthenticated { -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/aop/PreAuthenticatedAspect.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/aop/PreAuthenticatedAspect.java deleted file mode 100644 index 806207a..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/aop/PreAuthenticatedAspect.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.security.core.aop; - -import com.iailab.framework.security.core.annotations.PreAuthenticated; -import com.iailab.framework.security.core.util.SecurityFrameworkUtils; -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; - -import static com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED; -import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception; - -@Aspect -@Slf4j -public class PreAuthenticatedAspect { - - @Around("@annotation(preAuthenticated)") - public Object around(ProceedingJoinPoint joinPoint, PreAuthenticated preAuthenticated) throws Throwable { - if (SecurityFrameworkUtils.getLoginUser() == null) { - throw exception(UNAUTHORIZED); - } - return joinPoint.proceed(); - } - -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/context/TransmittableThreadLocalSecurityContextHolderStrategy.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/context/TransmittableThreadLocalSecurityContextHolderStrategy.java deleted file mode 100644 index 5eefcfe..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/context/TransmittableThreadLocalSecurityContextHolderStrategy.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.iailab.framework.security.core.context; - -import com.alibaba.ttl.TransmittableThreadLocal; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolderStrategy; -import org.springframework.security.core.context.SecurityContextImpl; -import org.springframework.util.Assert; - -/** - * 基于 TransmittableThreadLocal 实现的 Security Context 持有者策略 - * 目的是,避免 @Async 等异步执行时,原生 ThreadLocal 的丢失问题 - * - * @author iailab - */ -public class TransmittableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy { - - /** - * 使用 TransmittableThreadLocal 作为上下文 - */ - private static final ThreadLocal<SecurityContext> CONTEXT_HOLDER = new TransmittableThreadLocal<>(); - - @Override - public void clearContext() { - CONTEXT_HOLDER.remove(); - } - - @Override - public SecurityContext getContext() { - SecurityContext ctx = CONTEXT_HOLDER.get(); - if (ctx == null) { - ctx = createEmptyContext(); - CONTEXT_HOLDER.set(ctx); - } - return ctx; - } - - @Override - public void setContext(SecurityContext context) { - Assert.notNull(context, "Only non-null SecurityContext instances are permitted"); - CONTEXT_HOLDER.set(context); - } - - @Override - public SecurityContext createEmptyContext() { - return new SecurityContextImpl(); - } - -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/filter/TokenAuthenticationFilter.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/filter/TokenAuthenticationFilter.java deleted file mode 100644 index 161e12f..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/filter/TokenAuthenticationFilter.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.iailab.framework.security.core.filter; - -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.exception.ServiceException; -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.framework.common.util.servlet.ServletUtils; -import com.iailab.framework.security.config.SecurityProperties; -import com.iailab.framework.security.core.LoginUser; -import com.iailab.framework.security.core.util.SecurityFrameworkUtils; -import com.iailab.framework.web.core.handler.GlobalExceptionHandler; -import com.iailab.framework.web.core.util.WebFrameworkUtils; -import com.iailab.module.system.api.oauth2.OAuth2TokenApi; -import com.iailab.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; - -/** - * Token 过滤器,验证 token 的有效性 - * 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文 - * - * @author iailab - */ -@RequiredArgsConstructor -@Slf4j -public class TokenAuthenticationFilter extends OncePerRequestFilter { - - private final SecurityProperties securityProperties; - - private final GlobalExceptionHandler globalExceptionHandler; - - private final OAuth2TokenApi oauth2TokenApi; - - @Override - @SuppressWarnings("NullableProblems") - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) - throws ServletException, IOException { - // 情况一,基于 header[login-user] 获得用户,例如说来自 Gateway 或者其它服务透传 - LoginUser loginUser = buildLoginUserByHeader(request); - - // 情况二,基于 Token 获得用户 - // 注意,这里主要满足直接使用 Nginx 直接转发到 Spring Cloud 服务的场景。 - if (loginUser == null) { - String token = SecurityFrameworkUtils.obtainAuthorization(request, - securityProperties.getTokenHeader(), securityProperties.getTokenParameter()); - if (StrUtil.isNotEmpty(token)) { - Integer userType = WebFrameworkUtils.getLoginUserType(request); - try { - // 1.1 基于 token 构建登录用户 - loginUser = buildLoginUserByToken(token, userType); - // 1.2 模拟 Login 功能,方便日常开发调试 - if (loginUser == null) { - loginUser = mockLoginUser(request, token, userType); - } - } catch (Throwable ex) { - CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex); - ServletUtils.writeJSON(response, result); - return; - } - } - } - - // 设置当前用户 - if (loginUser != null) { - SecurityFrameworkUtils.setLoginUser(loginUser, request); - } - // 继续过滤链 - chain.doFilter(request, response); - } - - private LoginUser buildLoginUserByToken(String token, Integer userType) { - try { - // 校验访问令牌 - OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token).getCheckedData(); - if (accessToken == null) { - return null; - } - // 用户类型不匹配,无权限 - // 注意:只有 /admin-api/* 和 /app-api/* 有 userType,才需要比对用户类型 - // 类似 WebSocket 的 /ws/* 连接地址,是不需要比对用户类型的 - if (userType != null - && ObjectUtil.notEqual(accessToken.getUserType(), userType)) { - throw new AccessDeniedException("错误的用户类型"); - } - // 构建登录用户 - return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType()) - .setInfo(accessToken.getUserInfo()) // 额外的用户信息 - .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes()) - .setExpiresTime(accessToken.getExpiresTime()); - } catch (ServiceException serviceException) { - // 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可 - return null; - } - } - - /** - * 模拟登录用户,方便日常开发调试 - * - * 注意,在线上环境下,一定要关闭该功能!!! - * - * @param request 请求 - * @param token 模拟的 token,格式为 {@link SecurityProperties#getMockSecret()} + 用户编号 - * @param userType 用户类型 - * @return 模拟的 LoginUser - */ - private LoginUser mockLoginUser(HttpServletRequest request, String token, Integer userType) { - if (!securityProperties.getMockEnable()) { - return null; - } - // 必须以 mockSecret 开头 - if (!token.startsWith(securityProperties.getMockSecret())) { - return null; - } - // 构建模拟用户 - Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length())); - return new LoginUser().setId(userId).setUserType(userType) - .setTenantId(WebFrameworkUtils.getTenantId(request)); - } - - @SneakyThrows - private LoginUser buildLoginUserByHeader(HttpServletRequest request) { - String loginUserStr = request.getHeader(SecurityFrameworkUtils.LOGIN_USER_HEADER); - if (StrUtil.isEmpty(loginUserStr)) { - return null; - } - try { - loginUserStr = URLDecoder.decode(loginUserStr, StandardCharsets.UTF_8.name()); // 解码,解决中文乱码问题 - return JsonUtils.parseObject(loginUserStr, LoginUser.class); - } catch (Exception ex) { - log.error("[buildLoginUserByHeader][解析 LoginUser({}) 发生异常]", loginUserStr, ex); ; - throw ex; - } - } - -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/handler/AccessDeniedHandlerImpl.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/handler/AccessDeniedHandlerImpl.java deleted file mode 100644 index 0573f50..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/handler/AccessDeniedHandlerImpl.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.iailab.framework.security.core.handler; - -import com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants; -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.security.core.util.SecurityFrameworkUtils; -import com.iailab.framework.common.util.servlet.ServletUtils; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.security.web.access.ExceptionTranslationFilter; -import org.springframework.stereotype.Component; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -import static com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants.FORBIDDEN; -import static com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED; - -/** - * 访问一个需要认证的 URL 资源,已经认证(登录)但是没有权限的情况下,返回 {@link GlobalErrorCodeConstants#FORBIDDEN} 错误码。 - * - * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#handleAccessDeniedException(HttpServletRequest, HttpServletResponse, FilterChain, AccessDeniedException)} 方法,调用当前类 - * - * @author iailab - */ -@Slf4j -@SuppressWarnings("JavadocReference") -public class AccessDeniedHandlerImpl implements AccessDeniedHandler { - - @Override - public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) - throws IOException, ServletException { - // 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏 - log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(), - SecurityFrameworkUtils.getLoginUserId(), e); - // 返回 403 - ServletUtils.writeJSON(response, CommonResult.error(FORBIDDEN)); - } - -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/handler/AuthenticationEntryPointImpl.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/handler/AuthenticationEntryPointImpl.java deleted file mode 100644 index dc769b7..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/handler/AuthenticationEntryPointImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.iailab.framework.security.core.handler; - -import com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants; -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.util.servlet.ServletUtils; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.access.ExceptionTranslationFilter; - -import javax.servlet.FilterChain; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import static com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED; - -/** - * 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页 - * - * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类 - * - * @author ruoyi - */ -@Slf4j -@SuppressWarnings("JavadocReference") // 忽略文档引用报错 -public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint { - - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) { - log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e); - // 返回 401 - ServletUtils.writeJSON(response, CommonResult.error(UNAUTHORIZED)); - } - -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/rpc/LoginUserRequestInterceptor.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/rpc/LoginUserRequestInterceptor.java deleted file mode 100644 index 4f89c15..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/rpc/LoginUserRequestInterceptor.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.iailab.framework.security.core.rpc; - -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.framework.security.core.LoginUser; -import com.iailab.framework.security.core.util.SecurityFrameworkUtils; -import feign.RequestInterceptor; -import feign.RequestTemplate; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; - -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; - -/** - * LoginUser 的 RequestInterceptor 实现类:Feign 请求时,将 {@link LoginUser} 设置到 header 中,继续透传给被调用的服务 - * - * @author iailab - */ -@Slf4j -public class LoginUserRequestInterceptor implements RequestInterceptor { - - @Override - @SneakyThrows - public void apply(RequestTemplate requestTemplate) { - LoginUser user = SecurityFrameworkUtils.getLoginUser(); - if (user == null) { - return; - } - try { - String userStr = JsonUtils.toJsonString(user); - userStr = URLEncoder.encode(userStr, StandardCharsets.UTF_8.name()); // 编码,避免中文乱码 - requestTemplate.header(SecurityFrameworkUtils.LOGIN_USER_HEADER, userStr); - } catch (Exception ex) { - log.error("[apply][序列化 LoginUser({}) 发生异常]", user, ex); - throw ex; - } - } - -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/service/SecurityFrameworkService.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/service/SecurityFrameworkService.java deleted file mode 100644 index a32f9f5..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/service/SecurityFrameworkService.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.iailab.framework.security.core.service; - -/** - * Security 框架 Service 接口,定义权限相关的校验操作 - * - * @author iailab - */ -public interface SecurityFrameworkService { - - /** - * 判断是否有权限 - * - * @param permission 权限 - * @return 是否 - */ - boolean hasPermission(String permission); - - /** - * 判断是否有权限,任一一个即可 - * - * @param permissions 权限 - * @return 是否 - */ - boolean hasAnyPermissions(String... permissions); - - /** - * 判断是否有角色 - * - * 注意,角色使用的是 SysRoleDO 的 code 标识 - * - * @param role 角色 - * @return 是否 - */ - boolean hasRole(String role); - - /** - * 判断是否有角色,任一一个即可 - * - * @param roles 角色数组 - * @return 是否 - */ - boolean hasAnyRoles(String... roles); - - /** - * 判断是否有授权 - * - * @param scope 授权 - * @return 是否 - */ - boolean hasScope(String scope); - - /** - * 判断是否有授权范围,任一一个即可 - * - * @param scope 授权范围数组 - * @return 是否 - */ - boolean hasAnyScopes(String... scope); -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/service/SecurityFrameworkServiceImpl.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/service/SecurityFrameworkServiceImpl.java deleted file mode 100644 index c4e4ec1..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/service/SecurityFrameworkServiceImpl.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.iailab.framework.security.core.service; - -import cn.hutool.core.collection.CollUtil; -import com.iailab.framework.common.core.KeyValue; -import com.iailab.framework.security.core.LoginUser; -import com.iailab.framework.security.core.util.SecurityFrameworkUtils; -import com.iailab.module.system.api.permission.PermissionApi; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import lombok.AllArgsConstructor; -import lombok.SneakyThrows; - -import java.time.Duration; -import java.util.Arrays; -import java.util.List; - -import static com.iailab.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache; -import static com.iailab.framework.common.util.cache.CacheUtils.buildCache; -import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -/** - * 默认的 {@link SecurityFrameworkService} 实现类 - * - * @author iailab - */ -@AllArgsConstructor -public class SecurityFrameworkServiceImpl implements SecurityFrameworkService { - - private final PermissionApi permissionApi; - - /** - * 针对 {@link #hasAnyRoles(String...)} 的缓存 - */ - private final LoadingCache<KeyValue<Long, List<String>>, Boolean> hasAnyRolesCache = buildCache( - Duration.ofMinutes(1L), // 过期时间 1 分钟 - new CacheLoader<KeyValue<Long, List<String>>, Boolean>() { - - @Override - public Boolean load(KeyValue<Long, List<String>> key) { - return permissionApi.hasAnyRoles(key.getKey(), key.getValue().toArray(new String[0])).getCheckedData(); - } - - }); - - /** - * 针对 {@link #hasAnyPermissions(String...)} 的缓存 - */ - private final LoadingCache<KeyValue<Long, List<String>>, Boolean> hasAnyPermissionsCache = buildCache( - Duration.ofMinutes(1L), // 过期时间 1 分钟 - new CacheLoader<KeyValue<Long, List<String>>, Boolean>() { - - @Override - public Boolean load(KeyValue<Long, List<String>> key) { - return permissionApi.hasAnyPermissions(key.getKey(), key.getValue().toArray(new String[0])).getCheckedData(); - } - - }); - - @Override - public boolean hasPermission(String permission) { - return hasAnyPermissions(permission); - } - - @Override - @SneakyThrows - public boolean hasAnyPermissions(String... permissions) { - Long userId = getLoginUserId(); - if (userId == null) { - return false; - } - return hasAnyPermissionsCache.get(new KeyValue<>(userId, Arrays.asList(permissions))); - } - - @Override - public boolean hasRole(String role) { - return hasAnyRoles(role); - } - - @Override - @SneakyThrows - public boolean hasAnyRoles(String... roles) { - Long userId = getLoginUserId(); - if (userId == null) { - return false; - } - return hasAnyRolesCache.get(new KeyValue<>(userId, Arrays.asList(roles))); - } - - @Override - public boolean hasScope(String scope) { - return hasAnyScopes(scope); - } - - @Override - public boolean hasAnyScopes(String... scope) { - LoginUser user = SecurityFrameworkUtils.getLoginUser(); - if (user == null) { - return false; - } - return CollUtil.containsAny(user.getScopes(), Arrays.asList(scope)); - } - -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/util/SecurityFrameworkUtils.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/util/SecurityFrameworkUtils.java deleted file mode 100644 index c940895..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/util/SecurityFrameworkUtils.java +++ /dev/null @@ -1,142 +0,0 @@ -package com.iailab.framework.security.core.util; - -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.security.core.LoginUser; -import com.iailab.framework.web.core.util.WebFrameworkUtils; -import org.springframework.lang.Nullable; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.util.StringUtils; - -import javax.servlet.http.HttpServletRequest; -import java.util.Collections; - -/** - * 安全服务工具类 - * - * @author iailab - */ -public class SecurityFrameworkUtils { - - /** - * HEADER 认证头 value 的前缀 - */ - public static final String AUTHORIZATION_BEARER = "Bearer"; - - public static final String LOGIN_USER_HEADER = "login-user"; - - private SecurityFrameworkUtils() {} - - /** - * 从请求中,获得认证 Token - * - * @param request 请求 - * @param headerName 认证 Token 对应的 Header 名字 - * @param parameterName 认证 Token 对应的 Parameter 名字 - * @return 认证 Token - */ - public static String obtainAuthorization(HttpServletRequest request, - String headerName, String parameterName) { - // 1. 获得 Token。优先级:Header > Parameter - String token = request.getHeader(headerName); - if (StrUtil.isEmpty(token)) { - token = request.getParameter(parameterName); - } - if (!StringUtils.hasText(token)) { - return null; - } - // 2. 去除 Token 中带的 Bearer - int index = token.indexOf(AUTHORIZATION_BEARER + " "); - return index >= 0 ? token.substring(index + 7).trim() : token; - } - - /** - * 获得当前认证信息 - * - * @return 认证信息 - */ - public static Authentication getAuthentication() { - SecurityContext context = SecurityContextHolder.getContext(); - if (context == null) { - return null; - } - return context.getAuthentication(); - } - - /** - * 获取当前用户 - * - * @return 当前用户 - */ - @Nullable - public static LoginUser getLoginUser() { - Authentication authentication = getAuthentication(); - if (authentication == null) { - return null; - } - return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null; - } - - /** - * 获得当前用户的编号,从上下文中 - * - * @return 用户编号 - */ - @Nullable - public static Long getLoginUserId() { - LoginUser loginUser = getLoginUser(); - return loginUser != null ? loginUser.getId() : null; - } - - /** - * 获得当前用户的昵称,从上下文中 - * - * @return 昵称 - */ - @Nullable - public static String getLoginUserNickname() { - LoginUser loginUser = getLoginUser(); - return loginUser != null ? MapUtil.getStr(loginUser.getInfo(), LoginUser.INFO_KEY_NICKNAME) : null; - } - - /** - * 获得当前用户的部门编号,从上下文中 - * - * @return 部门编号 - */ - @Nullable - public static Long getLoginUserDeptId() { - LoginUser loginUser = getLoginUser(); - return loginUser != null ? MapUtil.getLong(loginUser.getInfo(), LoginUser.INFO_KEY_DEPT_ID) : null; - } - - /** - * 设置当前用户 - * - * @param loginUser 登录用户 - * @param request 请求 - */ - public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) { - // 创建 Authentication,并设置到上下文 - Authentication authentication = buildAuthentication(loginUser, request); - SecurityContextHolder.getContext().setAuthentication(authentication); - - // 额外设置到 request 中,用于 ApiAccessLogFilter 可以获取到用户编号; - // 原因是,Spring Security 的 Filter 在 ApiAccessLogFilter 后面,在它记录访问日志时,线上上下文已经没有用户编号等信息 - WebFrameworkUtils.setLoginUserId(request, loginUser.getId()); - WebFrameworkUtils.setLoginUserType(request, loginUser.getUserType()); - } - - private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) { - // 创建 UsernamePasswordAuthenticationToken 对象 - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( - loginUser, null, Collections.emptyList()); - authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - return authenticationToken; - } - -} diff --git a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/package-info.java b/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/package-info.java deleted file mode 100644 index aac86f2..0000000 --- a/iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * 基于 Spring Security 框架 - * 实现安全认证功能 - * - * @author iailab - */ -package com.iailab.framework.security; diff --git a/iailab-framework/iailab-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/iailab-framework/iailab-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 7af8b2d..0000000 --- a/iailab-framework/iailab-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,5 +0,0 @@ -com.iailab.framework.security.config.IailabSecurityRpcAutoConfiguration -com.iailab.framework.security.config.IailabSecurityAutoConfiguration -com.iailab.framework.security.config.IailabWebSecurityConfigurerAdapter -com.iailab.framework.operatelog.config.IailabOperateLogConfiguration -com.iailab.framework.operatelog.config.IailabOperateLogRpcAutoConfiguration \ No newline at end of file diff --git a/iailab-framework/iailab-common-security/src/main/resources/assembly.xml b/iailab-framework/iailab-common-security/src/main/resources/assembly.xml deleted file mode 100644 index 22ad348..0000000 --- a/iailab-framework/iailab-common-security/src/main/resources/assembly.xml +++ /dev/null @@ -1,50 +0,0 @@ -<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd"> -<!-- <id>jar-with-dependencies</id>--> - <formats> - <format>jar</format> - </formats> - <includeBaseDirectory>true</includeBaseDirectory> - - <!--依赖jar包以及项目打包文件存储文件--> - <dependencySets> - <dependencySet> - <!--存储在projectName-assembly-version/lib下--> - <outputDirectory>lib</outputDirectory> - </dependencySet> - </dependencySets> - - <files> - <file> - <fileMode>775</fileMode> - <source>target/${project.build.finalName}.jar</source> - <destName>demo-maven-assembly.jar</destName> - <outputDirectory>./target</outputDirectory> - </file> - </files> - - <fileSets> - <fileSet> - <directory>src/main/resources</directory> - <outputDirectory>./conf</outputDirectory> - <includes> - <include>*.yml</include> - <include>*.properties</include> - </includes> - </fileSet> - <fileSet> - <fileMode>775</fileMode> - <directory>deploy/bin</directory> - <outputDirectory>./bin</outputDirectory> - </fileSet> - <fileSet> - <directory>${basedir}</directory> - <includes> - <include>*.md</include> - </includes> - </fileSet> - - </fileSets> - -</assembly> \ No newline at end of file diff --git a/iailab-framework/iailab-common-test/pom.xml b/iailab-framework/iailab-common-test/pom.xml deleted file mode 100644 index 98e1a74..0000000 --- a/iailab-framework/iailab-common-test/pom.xml +++ /dev/null @@ -1,60 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>com.iailab</groupId> - <artifactId>iailab-framework</artifactId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-test</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description>测试组件,用于单元测试、集成测试</description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common</artifactId> - </dependency> - - <!-- DB 相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-mybatis</artifactId> - </dependency> - - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-redis</artifactId> - </dependency> - - <!-- Test 测试相关 --> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-inline</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-test</artifactId> - </dependency> - - <dependency> - <groupId>com.h2database</groupId> <!-- 单元测试,我们采用 H2 作为数据库 --> - <artifactId>h2</artifactId> - </dependency> - - <dependency> - <groupId>com.github.fppt</groupId> <!-- 单元测试,我们采用内嵌的 Redis 数据库 --> - <artifactId>jedis-mock</artifactId> - </dependency> - - <dependency> - <groupId>uk.co.jemos.podam</groupId> <!-- 单元测试,随机生成 POJO 类 --> - <artifactId>podam</artifactId> - </dependency> - </dependencies> -</project> diff --git a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/config/RedisTestConfiguration.java b/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/config/RedisTestConfiguration.java deleted file mode 100644 index 1070768..0000000 --- a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/config/RedisTestConfiguration.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.iailab.framework.test.config; - -import com.github.fppt.jedismock.RedisServer; -import org.springframework.boot.autoconfigure.data.redis.RedisProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Lazy; - -import java.io.IOException; - -/** - * Redis 测试 Configuration,主要实现内嵌 Redis 的启动 - * - * @author iailab - */ -@Configuration(proxyBeanMethods = false) -@Lazy(false) // 禁止延迟加载 -@EnableConfigurationProperties(RedisProperties.class) -public class RedisTestConfiguration { - - /** - * 创建模拟的 Redis Server 服务器 - */ - @Bean - public RedisServer redisServer(RedisProperties properties) throws IOException { - RedisServer redisServer = new RedisServer(properties.getPort()); - // 一次执行多个单元测试时,貌似创建多个 spring 容器,导致不进行 stop。这样,就导致端口被占用,无法启动。。。 - try { - redisServer.start(); - } catch (Exception ignore) {} - return redisServer; - } - -} diff --git a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/config/SqlInitializationTestConfiguration.java b/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/config/SqlInitializationTestConfiguration.java deleted file mode 100644 index 9703f2c..0000000 --- a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/config/SqlInitializationTestConfiguration.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.iailab.framework.test.config; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; -import org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer; -import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer; -import org.springframework.boot.sql.init.DatabaseInitializationSettings; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Lazy; - -import javax.sql.DataSource; - -/** - * SQL 初始化的测试 Configuration - * - * 为什么不使用 org.springframework.boot.autoconfigure.sql.init.DataSourceInitializationConfiguration 呢? - * 因为我们在单元测试会使用 spring.main.lazy-initialization 为 true,开启延迟加载。此时,会导致 DataSourceInitializationConfiguration 初始化 - * 不过呢,当前类的实现代码,基本是复制 DataSourceInitializationConfiguration 的哈! - * - * @author iailab - */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnMissingBean(AbstractScriptDatabaseInitializer.class) -@ConditionalOnSingleCandidate(DataSource.class) -@ConditionalOnClass(name = "org.springframework.jdbc.datasource.init.DatabasePopulator") -@Lazy(value = false) // 禁止延迟加载 -@EnableConfigurationProperties(SqlInitializationProperties.class) -public class SqlInitializationTestConfiguration { - - @Bean - public DataSourceScriptDatabaseInitializer dataSourceScriptDatabaseInitializer(DataSource dataSource, - SqlInitializationProperties initializationProperties) { - DatabaseInitializationSettings settings = createFrom(initializationProperties); - return new DataSourceScriptDatabaseInitializer(dataSource, settings); - } - - static DatabaseInitializationSettings createFrom(SqlInitializationProperties properties) { - DatabaseInitializationSettings settings = new DatabaseInitializationSettings(); - settings.setSchemaLocations(properties.getSchemaLocations()); - settings.setDataLocations(properties.getDataLocations()); - settings.setContinueOnError(properties.isContinueOnError()); - settings.setSeparator(properties.getSeparator()); - settings.setEncoding(properties.getEncoding()); - settings.setMode(properties.getMode()); - return settings; - } - -} diff --git a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/BaseDbAndRedisUnitTest.java b/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/BaseDbAndRedisUnitTest.java deleted file mode 100644 index 1c4b8d9..0000000 --- a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/BaseDbAndRedisUnitTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.iailab.framework.test.core.ut; - -import com.iailab.framework.datasource.config.IailabDataSourceAutoConfiguration; -import com.iailab.framework.mybatis.config.IailabMybatisAutoConfiguration; -import com.iailab.framework.redis.config.IailabRedisAutoConfiguration; -import com.iailab.framework.test.config.RedisTestConfiguration; -import com.iailab.framework.test.config.SqlInitializationTestConfiguration; -import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; -import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; -import org.redisson.spring.starter.RedissonAutoConfiguration; -import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.jdbc.Sql; - -/** - * 依赖内存 DB + Redis 的单元测试 - * - * 相比 {@link BaseDbUnitTest} 来说,额外增加了内存 Redis - * - * @author iailab - */ -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisUnitTest.Application.class) -@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件 -@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB -public class BaseDbAndRedisUnitTest { - - @Import({ - // DB 配置类 - IailabDataSourceAutoConfiguration.class, // 自己的 DB 配置类 - DataSourceAutoConfiguration.class, // Spring DB 自动配置类 - DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类 - DruidDataSourceAutoConfigure.class, // Druid 自动配置类 - SqlInitializationTestConfiguration.class, // SQL 初始化 - // MyBatis 配置类 - IailabMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类 - MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类 - - // Redis 配置类 - RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer - IailabRedisAutoConfiguration.class, // 自己的 Redis 配置类 - RedisAutoConfiguration.class, // Spring Redis 自动配置类 - RedissonAutoConfiguration.class, // Redisson 自动配置类 - }) - public static class Application { - } - -} diff --git a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/BaseDbUnitTest.java b/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/BaseDbUnitTest.java deleted file mode 100644 index bd8d0f5..0000000 --- a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/BaseDbUnitTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.iailab.framework.test.core.ut; - -import com.iailab.framework.datasource.config.IailabDataSourceAutoConfiguration; -import com.iailab.framework.mybatis.config.IailabMybatisAutoConfiguration; -import com.iailab.framework.test.config.SqlInitializationTestConfiguration; -import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; -import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; -import com.github.yulichang.autoconfigure.MybatisPlusJoinAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.jdbc.Sql; - -/** - * 依赖内存 DB 的单元测试 - * - * 注意,Service 层同样适用。对于 Service 层的单元测试,我们针对自己模块的 Mapper 走的是 H2 内存数据库,针对别的模块的 Service 走的是 Mock 方法 - * - * @author iailab - */ -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbUnitTest.Application.class) -@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件 -@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB -public class BaseDbUnitTest { - - @Import({ - // DB 配置类 - IailabDataSourceAutoConfiguration.class, // 自己的 DB 配置类 - DataSourceAutoConfiguration.class, // Spring DB 自动配置类 - DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类 - DruidDataSourceAutoConfigure.class, // Druid 自动配置类 - SqlInitializationTestConfiguration.class, // SQL 初始化 - // MyBatis 配置类 - IailabMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类 - MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类 - MybatisPlusJoinAutoConfiguration.class, // MyBatis 的Join配置类 - }) - public static class Application { - } - -} diff --git a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/BaseMockitoUnitTest.java b/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/BaseMockitoUnitTest.java deleted file mode 100644 index 198c6d0..0000000 --- a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/BaseMockitoUnitTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.iailab.framework.test.core.ut; - -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -/** - * 纯 Mockito 的单元测试 - * - * @author iailab - */ -@ExtendWith(MockitoExtension.class) -public class BaseMockitoUnitTest { -} diff --git a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/BaseRedisUnitTest.java b/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/BaseRedisUnitTest.java deleted file mode 100644 index dc1acd9..0000000 --- a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/BaseRedisUnitTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.iailab.framework.test.core.ut; - -import com.iailab.framework.redis.config.IailabRedisAutoConfiguration; -import com.iailab.framework.test.config.RedisTestConfiguration; -import org.redisson.spring.starter.RedissonAutoConfiguration; -import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.ActiveProfiles; - -/** - * 依赖内存 Redis 的单元测试 - * - * 相比 {@link BaseDbUnitTest} 来说,从内存 DB 改成了内存 Redis - * - * @author iailab - */ -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseRedisUnitTest.Application.class) -@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件 -public class BaseRedisUnitTest { - - @Import({ - // Redis 配置类 - RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer - RedisAutoConfiguration.class, // Spring Redis 自动配置类 - IailabRedisAutoConfiguration.class, // 自己的 Redis 配置类 - RedissonAutoConfiguration.class, // Redisson 自动配置类 - }) - public static class Application { - } - -} diff --git a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/package-info.java b/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/package-info.java deleted file mode 100644 index faa9f02..0000000 --- a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/ut/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 提供单元测试 Unit Test 的基类 - */ -package com.iailab.framework.test.core.ut; diff --git a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/util/AssertUtils.java b/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/util/AssertUtils.java deleted file mode 100644 index db91b59..0000000 --- a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/util/AssertUtils.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.iailab.framework.test.core.util; - -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.ReflectUtil; -import com.iailab.framework.common.exception.ErrorCode; -import com.iailab.framework.common.exception.ServiceException; -import com.iailab.framework.common.exception.util.ServiceExceptionUtil; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.function.Executable; - -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.Objects; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -/** - * 单元测试,assert 断言工具类 - * - * @author iailab - */ -public class AssertUtils { - - /** - * 比对两个对象的属性是否一致 - * - * 注意,如果 expected 存在的属性,actual 不存在的时候,会进行忽略 - * - * @param expected 期望对象 - * @param actual 实际对象 - * @param ignoreFields 忽略的属性数组 - */ - public static void assertPojoEquals(Object expected, Object actual, String... ignoreFields) { - Field[] expectedFields = ReflectUtil.getFields(expected.getClass()); - Arrays.stream(expectedFields).forEach(expectedField -> { - // 忽略 jacoco 自动生成的 $jacocoData 属性的情况 - if (expectedField.isSynthetic()) { - return; - } - // 如果是忽略的属性,则不进行比对 - if (ArrayUtil.contains(ignoreFields, expectedField.getName())) { - return; - } - // 忽略不存在的属性 - Field actualField = ReflectUtil.getField(actual.getClass(), expectedField.getName()); - if (actualField == null) { - return; - } - // 比对 - Assertions.assertEquals( - ReflectUtil.getFieldValue(expected, expectedField), - ReflectUtil.getFieldValue(actual, actualField), - String.format("Field(%s) 不匹配", expectedField.getName()) - ); - }); - } - - /** - * 比对两个对象的属性是否一致 - * - * 注意,如果 expected 存在的属性,actual 不存在的时候,会进行忽略 - * - * @param expected 期望对象 - * @param actual 实际对象 - * @param ignoreFields 忽略的属性数组 - * @return 是否一致 - */ - public static boolean isPojoEquals(Object expected, Object actual, String... ignoreFields) { - Field[] expectedFields = ReflectUtil.getFields(expected.getClass()); - return Arrays.stream(expectedFields).allMatch(expectedField -> { - // 如果是忽略的属性,则不进行比对 - if (ArrayUtil.contains(ignoreFields, expectedField.getName())) { - return true; - } - // 忽略不存在的属性 - Field actualField = ReflectUtil.getField(actual.getClass(), expectedField.getName()); - if (actualField == null) { - return true; - } - return Objects.equals(ReflectUtil.getFieldValue(expected, expectedField), - ReflectUtil.getFieldValue(actual, actualField)); - }); - } - - /** - * 执行方法,校验抛出的 Service 是否符合条件 - * - * @param executable 业务异常 - * @param errorCode 错误码对象 - * @param messageParams 消息参数 - */ - public static void assertServiceException(Executable executable, ErrorCode errorCode, Object... messageParams) { - // 调用方法 - ServiceException serviceException = assertThrows(ServiceException.class, executable); - // 校验错误码 - Assertions.assertEquals(errorCode.getCode(), serviceException.getCode(), "错误码不匹配"); - String message = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMsg(), messageParams); - Assertions.assertEquals(message, serviceException.getMessage(), "错误提示不匹配"); - } - -} diff --git a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/util/RandomUtils.java b/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/util/RandomUtils.java deleted file mode 100644 index 976786c..0000000 --- a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/core/util/RandomUtils.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.iailab.framework.test.core.util; - -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.RandomUtil; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.enums.CommonStatusEnum; -import uk.co.jemos.podam.api.PodamFactory; -import uk.co.jemos.podam.api.PodamFactoryImpl; - -import java.lang.reflect.Type; -import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Set; -import java.util.function.Consumer; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * 随机工具类 - * - * @author iailab - */ -public class RandomUtils { - - private static final int RANDOM_STRING_LENGTH = 10; - - private static final int TINYINT_MAX = 127; - - private static final int RANDOM_DATE_MAX = 30; - - private static final int RANDOM_COLLECTION_LENGTH = 5; - - private static final PodamFactory PODAM_FACTORY = new PodamFactoryImpl(); - - static { - // 字符串 - PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(String.class, - (dataProviderStrategy, attributeMetadata, map) -> randomString()); - // Integer - PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(Integer.class, (dataProviderStrategy, attributeMetadata, map) -> { - // 如果是 status 的字段,返回 0 或 1 - if ("status".equals(attributeMetadata.getAttributeName())) { - return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus(); - } - // 如果是 type、status 结尾的字段,返回 tinyint 范围 - if (StrUtil.endWithAnyIgnoreCase(attributeMetadata.getAttributeName(), - "type", "status", "category", "scope", "result")) { - return RandomUtil.randomInt(0, TINYINT_MAX + 1); - } - return RandomUtil.randomInt(); - }); - // LocalDateTime - PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(LocalDateTime.class, - (dataProviderStrategy, attributeMetadata, map) -> randomLocalDateTime()); - // Boolean - PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(Boolean.class, (dataProviderStrategy, attributeMetadata, map) -> { - // 如果是 deleted 的字段,返回非删除 - if ("deleted".equals(attributeMetadata.getAttributeName())) { - return false; - } - return RandomUtil.randomBoolean(); - }); - } - - public static String randomString() { - return RandomUtil.randomString(RANDOM_STRING_LENGTH); - } - - public static Long randomLongId() { - return RandomUtil.randomLong(0, Long.MAX_VALUE); - } - - public static Integer randomInteger() { - return RandomUtil.randomInt(0, Integer.MAX_VALUE); - } - - public static Date randomDate() { - return RandomUtil.randomDay(0, RANDOM_DATE_MAX); - } - - public static LocalDateTime randomLocalDateTime() { - // 设置 Nano 为零的原因,避免 MySQL、H2 存储不到时间戳 - return LocalDateTimeUtil.of(randomDate()).withNano(0); - } - - public static Short randomShort() { - return (short) RandomUtil.randomInt(0, Short.MAX_VALUE); - } - - public static <T> Set<T> randomSet(Class<T> clazz) { - return Stream.iterate(0, i -> i).limit(RandomUtil.randomInt(1, RANDOM_COLLECTION_LENGTH)) - .map(i -> randomPojo(clazz)).collect(Collectors.toSet()); - } - - public static Integer randomCommonStatus() { - return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus(); - } - - public static String randomEmail() { - return randomString() + "@qq.com"; - } - - public static String randomURL() { - return "https://www.baidu.com/" + randomString(); - } - - @SafeVarargs - public static <T> T randomPojo(Class<T> clazz, Consumer<T>... consumers) { - T pojo = PODAM_FACTORY.manufacturePojo(clazz); - // 非空时,回调逻辑。通过它,可以实现 Pojo 的进一步处理 - if (ArrayUtil.isNotEmpty(consumers)) { - Arrays.stream(consumers).forEach(consumer -> consumer.accept(pojo)); - } - return pojo; - } - - @SafeVarargs - public static <T> T randomPojo(Class<T> clazz, Type type, Consumer<T>... consumers) { - T pojo = PODAM_FACTORY.manufacturePojo(clazz, type); - // 非空时,回调逻辑。通过它,可以实现 Pojo 的进一步处理 - if (ArrayUtil.isNotEmpty(consumers)) { - Arrays.stream(consumers).forEach(consumer -> consumer.accept(pojo)); - } - return pojo; - } - - @SafeVarargs - public static <T> List<T> randomPojoList(Class<T> clazz, Consumer<T>... consumers) { - int size = RandomUtil.randomInt(1, RANDOM_COLLECTION_LENGTH); - return Stream.iterate(0, i -> i).limit(size).map(o -> randomPojo(clazz, consumers)) - .collect(Collectors.toList()); - } - -} diff --git a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/package-info.java b/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/package-info.java deleted file mode 100644 index ee6520a..0000000 --- a/iailab-framework/iailab-common-test/src/main/java/com/iailab/framework/test/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 测试组件,用于单元测试、集成测试等等 - */ -package com.iailab.framework.test; diff --git a/iailab-framework/iailab-common-web/pom.xml b/iailab-framework/iailab-common-web/pom.xml deleted file mode 100644 index 6fb1917..0000000 --- a/iailab-framework/iailab-common-web/pom.xml +++ /dev/null @@ -1,99 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>com.iailab</groupId> - <artifactId>iailab-framework</artifactId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-web</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description>Web 框架,全局异常、API 日志、脱敏、错误码等</description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common</artifactId> - </dependency> - <dependency> - <groupId>com.baomidou</groupId> - <artifactId>mybatis-plus-boot-starter</artifactId> <!-- 捕获mybatis全局异常 --> - </dependency> - - <!-- Spring Boot 配置所需依赖 --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-configuration-processor</artifactId> - <optional>true</optional> - </dependency> - - <!-- Web 相关 --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-web</artifactId> - </dependency> - - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-validation</artifactId> - </dependency> - - <dependency> - <groupId>org.springframework.security</groupId> - <artifactId>spring-security-core</artifactId> -<!-- <scope>provided</scope> <!– 设置为 provided,主要是 GlobalExceptionHandler 使用 –>--> - </dependency> - - <dependency> - <groupId>com.github.xiaoymin</groupId> <!-- 接口文档 --> - <artifactId>knife4j-openapi3-spring-boot-starter</artifactId> - </dependency> - <dependency> - <groupId>org.springdoc</groupId> <!-- 接口文档 --> - <artifactId>springdoc-openapi-ui</artifactId> - </dependency> - - <!-- RPC 远程调用相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-rpc</artifactId> - <optional>true</optional> - </dependency> - - <!-- 业务组件 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-module-infra-api</artifactId> <!-- 需要使用它,进行操作日志的记录 --> - <version>${revision}</version> - </dependency> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-module-system-api</artifactId> <!-- 需要使用它,进行错误码的记录 --> - <version>${revision}</version> - </dependency> - - <!-- xss --> - <dependency> - <groupId>org.jsoup</groupId> - <artifactId>jsoup</artifactId> - </dependency> - - <!-- Test 测试相关 --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-test</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-inline</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/config/IailabApiLogAutoConfiguration.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/config/IailabApiLogAutoConfiguration.java deleted file mode 100644 index 8edd048..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/config/IailabApiLogAutoConfiguration.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.iailab.framework.apilog.config; - -import com.iailab.framework.apilog.core.filter.ApiAccessLogFilter; -import com.iailab.framework.apilog.core.interceptor.ApiAccessLogInterceptor; -import com.iailab.framework.apilog.core.service.ApiAccessLogFrameworkService; -import com.iailab.framework.apilog.core.service.ApiAccessLogFrameworkServiceImpl; -import com.iailab.framework.apilog.core.service.ApiErrorLogFrameworkService; -import com.iailab.framework.apilog.core.service.ApiErrorLogFrameworkServiceImpl; -import com.iailab.framework.common.enums.WebFilterOrderEnum; -import com.iailab.framework.web.config.WebProperties; -import com.iailab.framework.web.config.IailabWebAutoConfiguration; -import com.iailab.module.infra.api.logger.ApiAccessLogApi; -import com.iailab.module.infra.api.logger.ApiErrorLogApi; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import javax.servlet.Filter; - -@AutoConfiguration(after = IailabWebAutoConfiguration.class) -public class IailabApiLogAutoConfiguration implements WebMvcConfigurer { - - @Bean - @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") - public ApiAccessLogFrameworkService apiAccessLogFrameworkService(ApiAccessLogApi apiAccessLogApi) { - return new ApiAccessLogFrameworkServiceImpl(apiAccessLogApi); - } - - @Bean - @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") - public ApiErrorLogFrameworkService apiErrorLogFrameworkService(ApiErrorLogApi apiErrorLogApi) { - return new ApiErrorLogFrameworkServiceImpl(apiErrorLogApi); - } - - /** - * 创建 ApiAccessLogFilter Bean,记录 API 请求日志 - */ - @Bean - @ConditionalOnProperty(prefix = "iailab.access-log", value = "enable", matchIfMissing = true) // 允许使用 iailab.access-log.enable=false 禁用访问日志 - public FilterRegistrationBean<ApiAccessLogFilter> apiAccessLogFilter(WebProperties webProperties, - @Value("${spring.application.name}") String applicationName, - ApiAccessLogFrameworkService apiAccessLogFrameworkService) { - ApiAccessLogFilter filter = new ApiAccessLogFilter(webProperties, applicationName, apiAccessLogFrameworkService); - return createFilterBean(filter, WebFilterOrderEnum.API_ACCESS_LOG_FILTER); - } - - private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) { - FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter); - bean.setOrder(order); - return bean; - } - - @Override - public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(new ApiAccessLogInterceptor()); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/config/IailabApiLogRpcAutoConfiguration.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/config/IailabApiLogRpcAutoConfiguration.java deleted file mode 100644 index acb5fee..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/config/IailabApiLogRpcAutoConfiguration.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.iailab.framework.apilog.config; - -import com.iailab.module.infra.api.logger.ApiAccessLogApi; -import com.iailab.module.infra.api.logger.ApiErrorLogApi; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.cloud.openfeign.EnableFeignClients; -import org.springframework.context.annotation.Configuration; - -/** - * API 日志使用到 Feign 的配置项 - * - * @author iailab - */ -@AutoConfiguration -@EnableFeignClients(clients = {ApiAccessLogApi.class, // 主要是引入相关的 API 服务 - ApiErrorLogApi.class}) -public class IailabApiLogRpcAutoConfiguration { -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/annotation/ApiAccessLog.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/annotation/ApiAccessLog.java deleted file mode 100644 index d5ac165..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/annotation/ApiAccessLog.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.iailab.framework.apilog.core.annotation; - -import com.iailab.framework.apilog.core.enums.OperateTypeEnum; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 访问日志注解 - * - * @author iailab - */ -@Target({ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface ApiAccessLog { - - // ========== 开关字段 ========== - - /** - * 是否记录访问日志 - */ - boolean enable() default true; - /** - * 是否记录请求参数 - * - * 默认记录,主要考虑请求数据一般不大。可手动设置为 false 进行关闭 - */ - boolean requestEnable() default true; - /** - * 是否记录响应结果 - * - * 默认不记录,主要考虑响应数据可能比较大。可手动设置为 true 进行打开 - */ - boolean responseEnable() default false; - /** - * 敏感参数数组 - * - * 添加后,请求参数、响应结果不会记录该参数 - */ - String[] sanitizeKeys() default {}; - - // ========== 模块字段 ========== - - /** - * 操作模块 - * - * 为空时,会尝试读取 {@link io.swagger.v3.oas.annotations.tags.Tag#name()} 属性 - */ - String operateModule() default ""; - /** - * 操作名 - * - * 为空时,会尝试读取 {@link io.swagger.v3.oas.annotations.Operation#summary()} 属性 - */ - String operateName() default ""; - /** - * 操作分类 - * - * 实际并不是数组,因为枚举不能设置 null 作为默认值 - */ - OperateTypeEnum[] operateType() default {}; - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/enums/OperateTypeEnum.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/enums/OperateTypeEnum.java deleted file mode 100644 index a8fdc80..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/enums/OperateTypeEnum.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.iailab.framework.apilog.core.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 操作日志的操作类型 - * - * @author ruoyi - */ -@Getter -@AllArgsConstructor -public enum OperateTypeEnum { - - /** - * 查询 - */ - GET(1), - /** - * 新增 - */ - CREATE(2), - /** - * 修改 - */ - UPDATE(3), - /** - * 删除 - */ - DELETE(4), - /** - * 导出 - */ - EXPORT(5), - /** - * 导入 - */ - IMPORT(6), - /** - * 其它 - * - * 在无法归类时,可以选择使用其它。因为还有操作名可以进一步标识 - */ - OTHER(0); - - /** - * 类型 - */ - private final Integer type; - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/filter/ApiAccessLogFilter.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/filter/ApiAccessLogFilter.java deleted file mode 100644 index 773c55c..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/filter/ApiAccessLogFilter.java +++ /dev/null @@ -1,251 +0,0 @@ -package com.iailab.framework.apilog.core.filter; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.exceptions.ExceptionUtil; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.BooleanUtil; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.apilog.core.annotation.ApiAccessLog; -import com.iailab.framework.apilog.core.enums.OperateTypeEnum; -import com.iailab.framework.apilog.core.service.ApiAccessLogFrameworkService; -import com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants; -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.framework.common.util.monitor.TracerUtils; -import com.iailab.framework.common.util.servlet.ServletUtils; -import com.iailab.framework.web.config.WebProperties; -import com.iailab.framework.web.core.filter.ApiRequestFilter; -import com.iailab.framework.web.core.util.WebFrameworkUtils; -import com.iailab.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; -import com.fasterxml.jackson.databind.JsonNode; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.method.HandlerMethod; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.Iterator; -import java.util.Map; - -import static com.iailab.framework.apilog.core.interceptor.ApiAccessLogInterceptor.*; -import static com.iailab.framework.common.util.json.JsonUtils.toJsonString; - -/** - * API 访问日志 Filter - * - * 目的:记录 API 访问日志到数据库中 - * - * @author iailab - */ -@Slf4j -public class ApiAccessLogFilter extends ApiRequestFilter { - - private static final String[] SANITIZE_KEYS = new String[]{"password", "token", "accessToken", "refreshToken"}; - - private final String applicationName; - - private final ApiAccessLogFrameworkService apiAccessLogFrameworkService; - - public ApiAccessLogFilter(WebProperties webProperties, String applicationName, ApiAccessLogFrameworkService apiAccessLogFrameworkService) { - super(webProperties); - this.applicationName = applicationName; - this.apiAccessLogFrameworkService = apiAccessLogFrameworkService; - } - - @Override - @SuppressWarnings("NullableProblems") - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - // 获得开始时间 - LocalDateTime beginTime = LocalDateTime.now(); - // 提前获得参数,避免 XssFilter 过滤处理 - Map<String, String> queryString = ServletUtils.getParamMap(request); - String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null; - - try { - // 继续过滤器 - filterChain.doFilter(request, response); - // 正常执行,记录日志 - createApiAccessLog(request, beginTime, queryString, requestBody, null); - } catch (Exception ex) { - // 异常执行,记录日志 - createApiAccessLog(request, beginTime, queryString, requestBody, ex); - throw ex; - } - } - - private void createApiAccessLog(HttpServletRequest request, LocalDateTime beginTime, - Map<String, String> queryString, String requestBody, Exception ex) { - ApiAccessLogCreateReqDTO accessLog = new ApiAccessLogCreateReqDTO(); - try { - boolean enable = buildApiAccessLog(accessLog, request, beginTime, queryString, requestBody, ex); - if (!enable) { - return; - } - apiAccessLogFrameworkService.createApiAccessLog(accessLog); - } catch (Throwable th) { - log.error("[createApiAccessLog][url({}) log({}) 发生异常]", request.getRequestURI(), toJsonString(accessLog), th); - } - } - - private boolean buildApiAccessLog(ApiAccessLogCreateReqDTO accessLog, HttpServletRequest request, LocalDateTime beginTime, - Map<String, String> queryString, String requestBody, Exception ex) { - // 判断:是否要记录操作日志 - HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(ATTRIBUTE_HANDLER_METHOD); - ApiAccessLog accessLogAnnotation = null; - if (handlerMethod != null) { - accessLogAnnotation = handlerMethod.getMethodAnnotation(ApiAccessLog.class); - if (accessLogAnnotation != null && BooleanUtil.isFalse(accessLogAnnotation.enable())) { - return false; - } - } - - // 处理用户信息 - accessLog.setUserId(WebFrameworkUtils.getLoginUserId(request)) - .setUserType(WebFrameworkUtils.getLoginUserType(request)); - // 设置访问结果 - CommonResult<?> result = WebFrameworkUtils.getCommonResult(request); - if (result != null) { - accessLog.setResultCode(result.getCode()).setResultMsg(result.getMsg()); - } else if (ex != null) { - accessLog.setResultCode(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode()) - .setResultMsg(ExceptionUtil.getRootCauseMessage(ex)); - } else { - accessLog.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode()).setResultMsg(""); - } - // 设置请求字段 - accessLog.setTraceId(TracerUtils.getTraceId()).setApplicationName(applicationName) - .setRequestUrl(request.getRequestURI()).setRequestMethod(request.getMethod()) - .setUserAgent(ServletUtils.getUserAgent(request)).setUserIp(ServletUtils.getClientIP(request)); - String[] sanitizeKeys = accessLogAnnotation != null ? accessLogAnnotation.sanitizeKeys() : null; - Boolean requestEnable = accessLogAnnotation != null ? accessLogAnnotation.requestEnable() : Boolean.TRUE; - if (!BooleanUtil.isFalse(requestEnable)) { // 默认记录,所以判断 !false - Map<String, Object> requestParams = MapUtil.<String, Object>builder() - .put("query", sanitizeMap(queryString, sanitizeKeys)) - .put("body", sanitizeJson(requestBody, sanitizeKeys)).build(); - accessLog.setRequestParams(toJsonString(requestParams)); - } - Boolean responseEnable = accessLogAnnotation != null ? accessLogAnnotation.responseEnable() : Boolean.FALSE; - if (BooleanUtil.isTrue(responseEnable)) { // 默认不记录,默认强制要求 true - accessLog.setResponseBody(sanitizeJson(result, sanitizeKeys)); - } - // 持续时间 - accessLog.setBeginTime(beginTime).setEndTime(LocalDateTime.now()) - .setDuration((int) LocalDateTimeUtil.between(accessLog.getBeginTime(), accessLog.getEndTime(), ChronoUnit.MILLIS)); - - // 操作模块 - if (handlerMethod != null) { - Tag tagAnnotation = handlerMethod.getBeanType().getAnnotation(Tag.class); - Operation operationAnnotation = handlerMethod.getMethodAnnotation(Operation.class); - String operateModule = accessLogAnnotation != null ? accessLogAnnotation.operateModule() : - tagAnnotation != null ? StrUtil.nullToDefault(tagAnnotation.name(), tagAnnotation.description()) : null; - String operateName = accessLogAnnotation != null ? accessLogAnnotation.operateName() : - operationAnnotation != null ? operationAnnotation.summary() : null; - OperateTypeEnum operateType = accessLogAnnotation != null && accessLogAnnotation.operateType().length > 0 ? - accessLogAnnotation.operateType()[0] : parseOperateLogType(request); - accessLog.setOperateModule(operateModule).setOperateName(operateName).setOperateType(operateType.getType()); - } - return true; - } - - // ========== 解析 @ApiAccessLog、@Swagger 注解 ========== - - private static OperateTypeEnum parseOperateLogType(HttpServletRequest request) { - RequestMethod requestMethod = ArrayUtil.firstMatch(method -> - StrUtil.equalsAnyIgnoreCase(method.name(), request.getMethod()), RequestMethod.values()); - if (requestMethod == null) { - return OperateTypeEnum.OTHER; - } - switch (requestMethod) { - case GET: - return OperateTypeEnum.GET; - case POST: - return OperateTypeEnum.CREATE; - case PUT: - return OperateTypeEnum.UPDATE; - case DELETE: - return OperateTypeEnum.DELETE; - default: - return OperateTypeEnum.OTHER; - } - } - - // ========== 请求和响应的脱敏逻辑,移除类似 password、token 等敏感字段 ========== - - private static String sanitizeMap(Map<String, ?> map, String[] sanitizeKeys) { - if (CollUtil.isNotEmpty(map)) { - return null; - } - if (sanitizeKeys != null) { - MapUtil.removeAny(map, sanitizeKeys); - } - MapUtil.removeAny(map, SANITIZE_KEYS); - return JsonUtils.toJsonString(map); - } - - private static String sanitizeJson(String jsonString, String[] sanitizeKeys) { - if (StrUtil.isEmpty(jsonString)) { - return null; - } - try { - JsonNode rootNode = JsonUtils.parseTree(jsonString); - sanitizeJson(rootNode, sanitizeKeys); - return JsonUtils.toJsonString(rootNode); - } catch (Exception e) { - // 脱敏失败的情况下,直接忽略异常,避免影响用户请求 - log.error("[sanitizeJson][脱敏({}) 发生异常]", jsonString, e); - return jsonString; - } - } - - private static String sanitizeJson(CommonResult<?> commonResult, String[] sanitizeKeys) { - if (commonResult == null) { - return null; - } - String jsonString = toJsonString(commonResult); - try { - JsonNode rootNode = JsonUtils.parseTree(jsonString); - sanitizeJson(rootNode.get("data"), sanitizeKeys); // 只处理 data 字段,不处理 code、msg 字段,避免错误被脱敏掉 - return JsonUtils.toJsonString(rootNode); - } catch (Exception e) { - // 脱敏失败的情况下,直接忽略异常,避免影响用户请求 - log.error("[sanitizeJson][脱敏({}) 发生异常]", jsonString, e); - return jsonString; - } - } - - private static void sanitizeJson(JsonNode node, String[] sanitizeKeys) { - // 情况一:数组,遍历处理 - if (node.isArray()) { - for (JsonNode childNode : node) { - sanitizeJson(childNode, sanitizeKeys); - } - return; - } - // 情况二:非 Object,只是某个值,直接返回 - if (!node.isObject()) { - return; - } - // 情况三:Object,遍历处理 - Iterator<Map.Entry<String, JsonNode>> iterator = node.fields(); - while (iterator.hasNext()) { - Map.Entry<String, JsonNode> entry = iterator.next(); - if (ArrayUtil.contains(sanitizeKeys, entry.getKey()) - || ArrayUtil.contains(SANITIZE_KEYS, entry.getKey())) { - iterator.remove(); - continue; - } - sanitizeJson(entry.getValue(), sanitizeKeys); - } - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java deleted file mode 100644 index f22500a..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.iailab.framework.apilog.core.interceptor; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.util.servlet.ServletUtils; -import com.iailab.framework.common.util.spring.SpringUtils; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.StopWatch; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.HandlerInterceptor; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.lang.reflect.Method; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.IntStream; - -/** - * API 访问日志 Interceptor - * - * 目的:在非 prod 环境时,打印 request 和 response 两条日志到日志文件(控制台)中。 - * - * @author iailab - */ -@Slf4j -public class ApiAccessLogInterceptor implements HandlerInterceptor { - - public static final String ATTRIBUTE_HANDLER_METHOD = "HANDLER_METHOD"; - - private static final String ATTRIBUTE_STOP_WATCH = "ApiAccessLogInterceptor.StopWatch"; - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - // 记录 HandlerMethod,提供给 ApiAccessLogFilter 使用 - HandlerMethod handlerMethod = handler instanceof HandlerMethod ? (HandlerMethod) handler : null; - if (handlerMethod != null) { - request.setAttribute(ATTRIBUTE_HANDLER_METHOD, handlerMethod); - } - - // 打印 request 日志 - if (!SpringUtils.isProd()) { - Map<String, String> queryString = ServletUtils.getParamMap(request); - String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null; - if (CollUtil.isEmpty(queryString) && StrUtil.isEmpty(requestBody)) { - log.info("[preHandle][开始请求 URL({}) 无参数]", request.getRequestURI()); - } else { - log.info("[preHandle][开始请求 URL({}) 参数({})]", request.getRequestURI(), - StrUtil.blankToDefault(requestBody, queryString.toString())); - } - // 计时 - StopWatch stopWatch = new StopWatch(); - stopWatch.start(); - request.setAttribute(ATTRIBUTE_STOP_WATCH, stopWatch); - // 打印 Controller 路径 - printHandlerMethodPosition(handlerMethod); - } - return true; - } - - @Override - public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { - // 打印 response 日志 - if (!SpringUtils.isProd()) { - StopWatch stopWatch = (StopWatch) request.getAttribute(ATTRIBUTE_STOP_WATCH); - stopWatch.stop(); - log.info("[afterCompletion][完成请求 URL({}) 耗时({} ms)]", - request.getRequestURI(), stopWatch.getTotalTimeMillis()); - } - } - - /** - * 打印 Controller 方法路径 - */ - private void printHandlerMethodPosition(HandlerMethod handlerMethod) { - if (handlerMethod == null) { - return; - } - Method method = handlerMethod.getMethod(); - Class<?> clazz = method.getDeclaringClass(); - try { - // 获取 method 的 lineNumber - List<String> clazzContents = FileUtil.readUtf8Lines( - ResourceUtil.getResource(null, clazz).getPath().replace("/target/classes/", "/src/main/java/") - + clazz.getSimpleName() + ".java"); - Optional<Integer> lineNumber = IntStream.range(0, clazzContents.size()) - .filter(i -> clazzContents.get(i).contains(" " + method.getName() + "(")) // 简单匹配,不考虑方法重名 - .mapToObj(i -> i + 1) // 行号从 1 开始 - .findFirst(); - if (!lineNumber.isPresent()) { - return; - } - // 打印结果 - System.out.printf("\tController 方法路径:%s(%s.java:%d)\n", clazz.getName(), clazz.getSimpleName(), lineNumber.get()); - } catch (Exception ignore) { - // 忽略异常。原因:仅仅打印,非重要逻辑 - } - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiAccessLogFrameworkService.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiAccessLogFrameworkService.java deleted file mode 100644 index 6793b93..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiAccessLogFrameworkService.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.iailab.framework.apilog.core.service; - -import com.iailab.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; - -/** - * API 访问日志 Framework Service 接口 - * - * @author iailab - */ -public interface ApiAccessLogFrameworkService { - - /** - * 创建 API 访问日志 - * - * @param reqDTO API 访问日志 - */ - void createApiAccessLog(ApiAccessLogCreateReqDTO reqDTO); - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiAccessLogFrameworkServiceImpl.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiAccessLogFrameworkServiceImpl.java deleted file mode 100644 index 82501a5..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiAccessLogFrameworkServiceImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.iailab.framework.apilog.core.service; - -import com.iailab.module.infra.api.logger.ApiAccessLogApi; -import com.iailab.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.scheduling.annotation.Async; - -/** - * API 访问日志 Framework Service 实现类 - * - * 基于 {@link ApiAccessLogApi} 服务,记录访问日志 - * - * @author iailab - */ -@RequiredArgsConstructor -@Slf4j -public class ApiAccessLogFrameworkServiceImpl implements ApiAccessLogFrameworkService { - - private final ApiAccessLogApi apiAccessLogApi; - - @Override - @Async - public void createApiAccessLog(ApiAccessLogCreateReqDTO reqDTO) { - try { - apiAccessLogApi.createApiAccessLog(reqDTO); - } catch (Throwable ex) { - // 由于 @Async 异步调用,这里打印下日志,更容易跟进 - log.error("[createApiAccessLog][url({}) log({}) 发生异常]", reqDTO.getRequestUrl(), reqDTO, ex); - } - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiErrorLogFrameworkService.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiErrorLogFrameworkService.java deleted file mode 100644 index 43ac23e..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiErrorLogFrameworkService.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.iailab.framework.apilog.core.service; - -import com.iailab.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; - -/** - * API 错误日志 Framework Service 接口 - * - * @author iailab - */ -public interface ApiErrorLogFrameworkService { - - /** - * 创建 API 错误日志 - * - * @param reqDTO API 错误日志 - */ - void createApiErrorLog(ApiErrorLogCreateReqDTO reqDTO); - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java deleted file mode 100644 index 87055ce..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.iailab.framework.apilog.core.service; - -import com.iailab.module.infra.api.logger.ApiErrorLogApi; -import com.iailab.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.scheduling.annotation.Async; - -/** - * API 错误日志 Framework Service 实现类 - * - * 基于 {@link ApiErrorLogApi} 服务,记录错误日志 - * - * @author iailab - */ -@RequiredArgsConstructor -@Slf4j -public class ApiErrorLogFrameworkServiceImpl implements ApiErrorLogFrameworkService { - - private final ApiErrorLogApi apiErrorLogApi; - - @Override - @Async - public void createApiErrorLog(ApiErrorLogCreateReqDTO reqDTO) { - try { - apiErrorLogApi.createApiErrorLog(reqDTO); - } catch (Throwable ex) { - // 由于 @Async 异步调用,这里打印下日志,更容易跟进 - log.error("[createApiErrorLog][url({}) log({}) 发生异常]", reqDTO.getRequestUrl(), reqDTO, ex); - } - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/package-info.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/package-info.java deleted file mode 100644 index 4dec8e5..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -/** - * API 日志:包含两类 - * 1. API 访问日志:记录用户访问 API 的访问日志,定期归档历史日志。 - * 2. 异常日志:记录用户访问 API 的系统异常,方便日常排查问题与告警。 - * - * @author iailab - */ -package com.iailab.framework.apilog; diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/banner/config/IailabBannerAutoConfiguration.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/banner/config/IailabBannerAutoConfiguration.java deleted file mode 100644 index ae7abfc..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/banner/config/IailabBannerAutoConfiguration.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.iailab.framework.banner.config; - -import com.iailab.framework.banner.core.BannerApplicationRunner; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Bean; - -/** - * Banner 的自动配置类 - * - * @author iailab - */ -@AutoConfiguration -public class IailabBannerAutoConfiguration { - - @Bean - public BannerApplicationRunner bannerApplicationRunner() { - return new BannerApplicationRunner(); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/banner/core/BannerApplicationRunner.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/banner/core/BannerApplicationRunner.java deleted file mode 100644 index fd4c918..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/banner/core/BannerApplicationRunner.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.framework.banner.core; - -import cn.hutool.core.thread.ThreadUtil; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; - -import java.util.concurrent.TimeUnit; - -/** - * 项目启动成功后,提供文档相关的地址 - * - * @author iailab - */ -@Slf4j -public class BannerApplicationRunner implements ApplicationRunner { - - @Override - public void run(ApplicationArguments args) { - ThreadUtil.execute(() -> { - ThreadUtil.sleep(1, TimeUnit.SECONDS); // 延迟 1 秒,保证输出到结尾 - log.info("\n----------------------------------------------------------\n\t" + - "项目启动成功!\n\t" + - "----------------------------------------------------------"); - }); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/banner/package-info.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/banner/package-info.java deleted file mode 100644 index 8567532..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/banner/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Banner 用于在 console 控制台,打印开发文档、接口文档等 - * - * @author iailab - */ -package com.iailab.framework.banner; diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/base/annotation/DesensitizeBy.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/base/annotation/DesensitizeBy.java deleted file mode 100644 index 1894a51..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/base/annotation/DesensitizeBy.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.iailab.framework.desensitize.core.base.annotation; - -import com.iailab.framework.desensitize.core.base.handler.DesensitizationHandler; -import com.iailab.framework.desensitize.core.base.serializer.StringDesensitizeSerializer; -import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 顶级脱敏注解,自定义注解需要使用此注解 - * - * @author gaibu - */ -@Documented -@Target(ElementType.ANNOTATION_TYPE) -@Retention(RetentionPolicy.RUNTIME) -@JacksonAnnotationsInside // 此注解是其他所有 jackson 注解的元注解,打上了此注解的注解表明是 jackson 注解的一部分 -@JsonSerialize(using = StringDesensitizeSerializer.class) // 指定序列化器 -public @interface DesensitizeBy { - - /** - * 脱敏处理器 - */ - @SuppressWarnings("rawtypes") - Class<? extends DesensitizationHandler> handler(); - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/base/handler/DesensitizationHandler.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/base/handler/DesensitizationHandler.java deleted file mode 100644 index a761c0e..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/base/handler/DesensitizationHandler.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.iailab.framework.desensitize.core.base.handler; - -import java.lang.annotation.Annotation; - -/** - * 脱敏处理器接口 - * - * @author gaibu - */ -public interface DesensitizationHandler<T extends Annotation> { - - /** - * 脱敏 - * - * @param origin 原始字符串 - * @param annotation 注解信息 - * @return 脱敏后的字符串 - */ - String desensitize(String origin, T annotation); - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/base/serializer/StringDesensitizeSerializer.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/base/serializer/StringDesensitizeSerializer.java deleted file mode 100644 index 2d737f9..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/base/serializer/StringDesensitizeSerializer.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.iailab.framework.desensitize.core.base.serializer; - -import cn.hutool.core.annotation.AnnotationUtil; -import cn.hutool.core.lang.Singleton; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.ReflectUtil; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.desensitize.core.base.annotation.DesensitizeBy; -import com.iailab.framework.desensitize.core.base.handler.DesensitizationHandler; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.BeanProperty; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import lombok.Getter; -import lombok.Setter; - -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; - -/** - * 脱敏序列化器 - * - * 实现 JSON 返回数据时,使用 {@link DesensitizationHandler} 对声明脱敏注解的字段,进行脱敏处理。 - * - * @author gaibu - */ -@SuppressWarnings("rawtypes") -public class StringDesensitizeSerializer extends StdSerializer<String> implements ContextualSerializer { - - @Getter - @Setter - private DesensitizationHandler desensitizationHandler; - - protected StringDesensitizeSerializer() { - super(String.class); - } - - @Override - public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) { - DesensitizeBy annotation = beanProperty.getAnnotation(DesensitizeBy.class); - if (annotation == null) { - return this; - } - // 创建一个 StringDesensitizeSerializer 对象,使用 DesensitizeBy 对应的处理器 - StringDesensitizeSerializer serializer = new StringDesensitizeSerializer(); - serializer.setDesensitizationHandler(Singleton.get(annotation.handler())); - return serializer; - } - - @Override - @SuppressWarnings("unchecked") - public void serialize(String value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException { - if (StrUtil.isBlank(value)) { - gen.writeNull(); - return; - } - // 获取序列化字段 - Field field = getField(gen); - - // 自定义处理器 - DesensitizeBy[] annotations = AnnotationUtil.getCombinationAnnotations(field, DesensitizeBy.class); - if (ArrayUtil.isEmpty(annotations)) { - gen.writeString(value); - return; - } - for (Annotation annotation : field.getAnnotations()) { - if (AnnotationUtil.hasAnnotation(annotation.annotationType(), DesensitizeBy.class)) { - value = this.desensitizationHandler.desensitize(value, annotation); - gen.writeString(value); - return; - } - } - gen.writeString(value); - } - - /** - * 获取字段 - * - * @param generator JsonGenerator - * @return 字段 - */ - private Field getField(JsonGenerator generator) { - String currentName = generator.getOutputContext().getCurrentName(); - Object currentValue = generator.getCurrentValue(); - Class<?> currentValueClass = currentValue.getClass(); - return ReflectUtil.getField(currentValueClass, currentName); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/annotation/EmailDesensitize.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/annotation/EmailDesensitize.java deleted file mode 100644 index 65d31f4..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/annotation/EmailDesensitize.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.iailab.framework.desensitize.core.regex.annotation; - -import com.iailab.framework.desensitize.core.base.annotation.DesensitizeBy; -import com.iailab.framework.desensitize.core.regex.handler.EmailDesensitizationHandler; -import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 邮箱脱敏注解 - * - * @author gaibu - */ -@Documented -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@JacksonAnnotationsInside -@DesensitizeBy(handler = EmailDesensitizationHandler.class) -public @interface EmailDesensitize { - - /** - * 匹配的正则表达式 - */ - String regex() default "(^.)[^@]*(@.*$)"; - - /** - * 替换规则,邮箱; - * - * 比如:example@gmail.com 脱敏之后为 e****@gmail.com - */ - String replacer() default "$1****$2"; -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/annotation/RegexDesensitize.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/annotation/RegexDesensitize.java deleted file mode 100644 index 072a6ed..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/annotation/RegexDesensitize.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.iailab.framework.desensitize.core.regex.annotation; - -import com.iailab.framework.desensitize.core.base.annotation.DesensitizeBy; -import com.iailab.framework.desensitize.core.regex.handler.DefaultRegexDesensitizationHandler; -import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 正则脱敏注解 - * - * @author gaibu - */ -@Documented -@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@JacksonAnnotationsInside -@DesensitizeBy(handler = DefaultRegexDesensitizationHandler.class) -public @interface RegexDesensitize { - - /** - * 匹配的正则表达式(默认匹配所有) - */ - String regex() default "^[\\s\\S]*$"; - - /** - * 替换规则,会将匹配到的字符串全部替换成 replacer - * - * 例如:regex=123; replacer=****** - * 原始字符串 123456789 - * 脱敏后字符串 ******456789 - */ - String replacer() default "******"; -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/handler/AbstractRegexDesensitizationHandler.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/handler/AbstractRegexDesensitizationHandler.java deleted file mode 100644 index c5ecf05..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/handler/AbstractRegexDesensitizationHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.iailab.framework.desensitize.core.regex.handler; - -import com.iailab.framework.desensitize.core.base.handler.DesensitizationHandler; - -import java.lang.annotation.Annotation; - -/** - * 正则表达式脱敏处理器抽象类,已实现通用的方法 - * - * @author gaibu - */ -public abstract class AbstractRegexDesensitizationHandler<T extends Annotation> - implements DesensitizationHandler<T> { - - @Override - public String desensitize(String origin, T annotation) { - String regex = getRegex(annotation); - String replacer = getReplacer(annotation); - return origin.replaceAll(regex, replacer); - } - - /** - * 获取注解上的 regex 参数 - * - * @param annotation 注解信息 - * @return 正则表达式 - */ - abstract String getRegex(T annotation); - - /** - * 获取注解上的 replacer 参数 - * - * @param annotation 注解信息 - * @return 待替换的字符串 - */ - abstract String getReplacer(T annotation); - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/handler/DefaultRegexDesensitizationHandler.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/handler/DefaultRegexDesensitizationHandler.java deleted file mode 100644 index 7adee18..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/handler/DefaultRegexDesensitizationHandler.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.iailab.framework.desensitize.core.regex.handler; - -import com.iailab.framework.desensitize.core.regex.annotation.RegexDesensitize; - -/** - * {@link RegexDesensitize} 的正则脱敏处理器 - * - * @author gaibu - */ -public class DefaultRegexDesensitizationHandler extends AbstractRegexDesensitizationHandler<RegexDesensitize> { - - @Override - String getRegex(RegexDesensitize annotation) { - return annotation.regex(); - } - - @Override - String getReplacer(RegexDesensitize annotation) { - return annotation.replacer(); - } -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/handler/EmailDesensitizationHandler.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/handler/EmailDesensitizationHandler.java deleted file mode 100644 index 6b4d60b..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/regex/handler/EmailDesensitizationHandler.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iailab.framework.desensitize.core.regex.handler; - -import com.iailab.framework.desensitize.core.regex.annotation.EmailDesensitize; - -/** - * {@link EmailDesensitize} 的脱敏处理器 - * - * @author gaibu - */ -public class EmailDesensitizationHandler extends AbstractRegexDesensitizationHandler<EmailDesensitize> { - - @Override - String getRegex(EmailDesensitize annotation) { - return annotation.regex(); - } - - @Override - String getReplacer(EmailDesensitize annotation) { - return annotation.replacer(); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/BankCardDesensitize.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/BankCardDesensitize.java deleted file mode 100644 index 67d3d9c..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/BankCardDesensitize.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.annotation; - -import com.iailab.framework.desensitize.core.base.annotation.DesensitizeBy; -import com.iailab.framework.desensitize.core.slider.handler.BankCardDesensitization; -import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 银行卡号 - * - * @author gaibu - */ -@Documented -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@JacksonAnnotationsInside -@DesensitizeBy(handler = BankCardDesensitization.class) -public @interface BankCardDesensitize { - - /** - * 前缀保留长度 - */ - int prefixKeep() default 6; - - /** - * 后缀保留长度 - */ - int suffixKeep() default 2; - - /** - * 替换规则,银行卡号; 比如:9988002866797031 脱敏之后为 998800********31 - */ - String replacer() default "*"; - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/CarLicenseDesensitize.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/CarLicenseDesensitize.java deleted file mode 100644 index ff33308..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/CarLicenseDesensitize.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.annotation; - -import com.iailab.framework.desensitize.core.base.annotation.DesensitizeBy; -import com.iailab.framework.desensitize.core.slider.handler.CarLicenseDesensitization; -import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 车牌号 - * - * @author gaibu - */ -@Documented -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@JacksonAnnotationsInside -@DesensitizeBy(handler = CarLicenseDesensitization.class) -public @interface CarLicenseDesensitize { - - /** - * 前缀保留长度 - */ - int prefixKeep() default 3; - - /** - * 后缀保留长度 - */ - int suffixKeep() default 1; - - /** - * 替换规则,车牌号;比如:粤A66666 脱敏之后为粤A6***6 - */ - String replacer() default "*"; - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/ChineseNameDesensitize.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/ChineseNameDesensitize.java deleted file mode 100644 index 7bf7094..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/ChineseNameDesensitize.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.annotation; - -import com.iailab.framework.desensitize.core.base.annotation.DesensitizeBy; -import com.iailab.framework.desensitize.core.slider.handler.ChineseNameDesensitization; -import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 中文名 - * - * @author gaibu - */ -@Documented -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@JacksonAnnotationsInside -@DesensitizeBy(handler = ChineseNameDesensitization.class) -public @interface ChineseNameDesensitize { - - /** - * 前缀保留长度 - */ - int prefixKeep() default 1; - - /** - * 后缀保留长度 - */ - int suffixKeep() default 0; - - /** - * 替换规则,中文名;比如:刘子豪脱敏之后为刘** - */ - String replacer() default "*"; - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/FixedPhoneDesensitize.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/FixedPhoneDesensitize.java deleted file mode 100644 index b43a1ff..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/FixedPhoneDesensitize.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.annotation; - -import com.iailab.framework.desensitize.core.base.annotation.DesensitizeBy; -import com.iailab.framework.desensitize.core.slider.handler.FixedPhoneDesensitization; -import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 固定电话 - * - * @author gaibu - */ -@Documented -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@JacksonAnnotationsInside -@DesensitizeBy(handler = FixedPhoneDesensitization.class) -public @interface FixedPhoneDesensitize { - - /** - * 前缀保留长度 - */ - int prefixKeep() default 4; - - /** - * 后缀保留长度 - */ - int suffixKeep() default 2; - - /** - * 替换规则,固定电话;比如:01086551122 脱敏之后为 0108*****22 - */ - String replacer() default "*"; - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/IdCardDesensitize.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/IdCardDesensitize.java deleted file mode 100644 index dde01ba..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/IdCardDesensitize.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.annotation; - -import com.iailab.framework.desensitize.core.base.annotation.DesensitizeBy; -import com.iailab.framework.desensitize.core.slider.handler.IdCardDesensitization; -import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 身份证 - * - * @author gaibu - */ -@Documented -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@JacksonAnnotationsInside -@DesensitizeBy(handler = IdCardDesensitization.class) -public @interface IdCardDesensitize { - - /** - * 前缀保留长度 - */ - int prefixKeep() default 6; - - /** - * 后缀保留长度 - */ - int suffixKeep() default 2; - - /** - * 替换规则,身份证号码;比如:530321199204074611 脱敏之后为 530321**********11 - */ - String replacer() default "*"; - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/MobileDesensitize.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/MobileDesensitize.java deleted file mode 100644 index 5de127f..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/MobileDesensitize.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.annotation; - -import com.iailab.framework.desensitize.core.base.annotation.DesensitizeBy; -import com.iailab.framework.desensitize.core.slider.handler.MobileDesensitization; -import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 手机号 - * - * @author gaibu - */ -@Documented -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@JacksonAnnotationsInside -@DesensitizeBy(handler = MobileDesensitization.class) -public @interface MobileDesensitize { - - /** - * 前缀保留长度 - */ - int prefixKeep() default 3; - - /** - * 后缀保留长度 - */ - int suffixKeep() default 4; - - /** - * 替换规则,手机号;比如:13248765917 脱敏之后为 132****5917 - */ - String replacer() default "*"; - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/PasswordDesensitize.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/PasswordDesensitize.java deleted file mode 100644 index 6b5ba79..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/PasswordDesensitize.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.annotation; - -import com.iailab.framework.desensitize.core.base.annotation.DesensitizeBy; -import com.iailab.framework.desensitize.core.slider.handler.PasswordDesensitization; -import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 密码 - * - * @author gaibu - */ -@Documented -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@JacksonAnnotationsInside -@DesensitizeBy(handler = PasswordDesensitization.class) -public @interface PasswordDesensitize { - - /** - * 前缀保留长度 - */ - int prefixKeep() default 0; - - /** - * 后缀保留长度 - */ - int suffixKeep() default 0; - - /** - * 替换规则,密码; - * - * 比如:123456 脱敏之后为 ****** - */ - String replacer() default "*"; - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/SliderDesensitize.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/SliderDesensitize.java deleted file mode 100644 index c655d4e..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/annotation/SliderDesensitize.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.annotation; - -import com.iailab.framework.desensitize.core.base.annotation.DesensitizeBy; -import com.iailab.framework.desensitize.core.slider.handler.DefaultDesensitizationHandler; -import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 滑动脱敏注解 - * - * @author gaibu - */ -@Documented -@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@JacksonAnnotationsInside -@DesensitizeBy(handler = DefaultDesensitizationHandler.class) -public @interface SliderDesensitize { - - /** - * 后缀保留长度 - */ - int suffixKeep() default 0; - - /** - * 替换规则,会将前缀后缀保留后,全部替换成 replacer - * - * 例如:prefixKeep = 1; suffixKeep = 2; replacer = "*"; - * 原始字符串 123456 - * 脱敏后 1***56 - */ - String replacer() default "*"; - - /** - * 前缀保留长度 - */ - int prefixKeep() default 0; -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/AbstractSliderDesensitizationHandler.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/AbstractSliderDesensitizationHandler.java deleted file mode 100644 index 160fa15..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/AbstractSliderDesensitizationHandler.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.handler; - -import com.iailab.framework.desensitize.core.base.handler.DesensitizationHandler; - -import java.lang.annotation.Annotation; - -/** - * 滑动脱敏处理器抽象类,已实现通用的方法 - * - * @author gaibu - */ -public abstract class AbstractSliderDesensitizationHandler<T extends Annotation> - implements DesensitizationHandler<T> { - - @Override - public String desensitize(String origin, T annotation) { - int prefixKeep = getPrefixKeep(annotation); - int suffixKeep = getSuffixKeep(annotation); - String replacer = getReplacer(annotation); - int length = origin.length(); - - // 情况一:原始字符串长度小于等于保留长度,则原始字符串全部替换 - if (prefixKeep >= length || suffixKeep >= length) { - return buildReplacerByLength(replacer, length); - } - - // 情况二:原始字符串长度小于等于前后缀保留字符串长度,则原始字符串全部替换 - if ((prefixKeep + suffixKeep) >= length) { - return buildReplacerByLength(replacer, length); - } - - // 情况三:原始字符串长度大于前后缀保留字符串长度,则替换中间字符串 - int interval = length - prefixKeep - suffixKeep; - return origin.substring(0, prefixKeep) + - buildReplacerByLength(replacer, interval) + - origin.substring(prefixKeep + interval); - } - - /** - * 根据长度循环构建替换符 - * - * @param replacer 替换符 - * @param length 长度 - * @return 构建后的替换符 - */ - private String buildReplacerByLength(String replacer, int length) { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < length; i++) { - builder.append(replacer); - } - return builder.toString(); - } - - /** - * 前缀保留长度 - * - * @param annotation 注解信息 - * @return 前缀保留长度 - */ - abstract Integer getPrefixKeep(T annotation); - - /** - * 后缀保留长度 - * - * @param annotation 注解信息 - * @return 后缀保留长度 - */ - abstract Integer getSuffixKeep(T annotation); - - /** - * 替换符 - * - * @param annotation 注解信息 - * @return 替换符 - */ - abstract String getReplacer(T annotation); - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/BankCardDesensitization.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/BankCardDesensitization.java deleted file mode 100644 index 64a3215..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/BankCardDesensitization.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.handler; - -import com.iailab.framework.desensitize.core.slider.annotation.BankCardDesensitize; - -/** - * {@link BankCardDesensitize} 的脱敏处理器 - * - * @author gaibu - */ -public class BankCardDesensitization extends AbstractSliderDesensitizationHandler<BankCardDesensitize> { - - @Override - Integer getPrefixKeep(BankCardDesensitize annotation) { - return annotation.prefixKeep(); - } - - @Override - Integer getSuffixKeep(BankCardDesensitize annotation) { - return annotation.suffixKeep(); - } - - @Override - String getReplacer(BankCardDesensitize annotation) { - return annotation.replacer(); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/CarLicenseDesensitization.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/CarLicenseDesensitization.java deleted file mode 100644 index 0280f99..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/CarLicenseDesensitization.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.handler; - -import com.iailab.framework.desensitize.core.slider.annotation.CarLicenseDesensitize; - -/** - * {@link CarLicenseDesensitize} 的脱敏处理器 - * - * @author gaibu - */ -public class CarLicenseDesensitization extends AbstractSliderDesensitizationHandler<CarLicenseDesensitize> { - @Override - Integer getPrefixKeep(CarLicenseDesensitize annotation) { - return annotation.prefixKeep(); - } - - @Override - Integer getSuffixKeep(CarLicenseDesensitize annotation) { - return annotation.suffixKeep(); - } - - @Override - String getReplacer(CarLicenseDesensitize annotation) { - return annotation.replacer(); - } -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/ChineseNameDesensitization.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/ChineseNameDesensitization.java deleted file mode 100644 index 0eb5bc2..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/ChineseNameDesensitization.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.handler; - -import com.iailab.framework.desensitize.core.slider.annotation.ChineseNameDesensitize; - -/** - * {@link ChineseNameDesensitize} 的脱敏处理器 - * - * @author gaibu - */ -public class ChineseNameDesensitization extends AbstractSliderDesensitizationHandler<ChineseNameDesensitize> { - - @Override - Integer getPrefixKeep(ChineseNameDesensitize annotation) { - return annotation.prefixKeep(); - } - - @Override - Integer getSuffixKeep(ChineseNameDesensitize annotation) { - return annotation.suffixKeep(); - } - - @Override - String getReplacer(ChineseNameDesensitize annotation) { - return annotation.replacer(); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/DefaultDesensitizationHandler.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/DefaultDesensitizationHandler.java deleted file mode 100644 index 9b9f2bb..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/DefaultDesensitizationHandler.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.handler; - -import com.iailab.framework.desensitize.core.slider.annotation.SliderDesensitize; - -/** - * {@link SliderDesensitize} 的脱敏处理器 - * - * @author gaibu - */ -public class DefaultDesensitizationHandler extends AbstractSliderDesensitizationHandler<SliderDesensitize> { - @Override - Integer getPrefixKeep(SliderDesensitize annotation) { - return annotation.prefixKeep(); - } - - @Override - Integer getSuffixKeep(SliderDesensitize annotation) { - return annotation.suffixKeep(); - } - - @Override - String getReplacer(SliderDesensitize annotation) { - return annotation.replacer(); - } -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/FixedPhoneDesensitization.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/FixedPhoneDesensitization.java deleted file mode 100644 index 53e44f3..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/FixedPhoneDesensitization.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.handler; - -import com.iailab.framework.desensitize.core.slider.annotation.FixedPhoneDesensitize; - -/** - * {@link FixedPhoneDesensitize} 的脱敏处理器 - * - * @author gaibu - */ -public class FixedPhoneDesensitization extends AbstractSliderDesensitizationHandler<FixedPhoneDesensitize> { - @Override - Integer getPrefixKeep(FixedPhoneDesensitize annotation) { - return annotation.prefixKeep(); - } - - @Override - Integer getSuffixKeep(FixedPhoneDesensitize annotation) { - return annotation.suffixKeep(); - } - - @Override - String getReplacer(FixedPhoneDesensitize annotation) { - return annotation.replacer(); - } -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/IdCardDesensitization.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/IdCardDesensitization.java deleted file mode 100644 index fde3851..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/IdCardDesensitization.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.handler; - -import com.iailab.framework.desensitize.core.slider.annotation.IdCardDesensitize; - -/** - * {@link IdCardDesensitize} 的脱敏处理器 - * - * @author gaibu - */ -public class IdCardDesensitization extends AbstractSliderDesensitizationHandler<IdCardDesensitize> { - @Override - Integer getPrefixKeep(IdCardDesensitize annotation) { - return annotation.prefixKeep(); - } - - @Override - Integer getSuffixKeep(IdCardDesensitize annotation) { - return annotation.suffixKeep(); - } - - @Override - String getReplacer(IdCardDesensitize annotation) { - return annotation.replacer(); - } -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/MobileDesensitization.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/MobileDesensitization.java deleted file mode 100644 index 26216aa..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/MobileDesensitization.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.handler; - -import com.iailab.framework.desensitize.core.slider.annotation.MobileDesensitize; - -/** - * {@link MobileDesensitize} 的脱敏处理器 - * - * @author gaibu - */ -public class MobileDesensitization extends AbstractSliderDesensitizationHandler<MobileDesensitize> { - - @Override - Integer getPrefixKeep(MobileDesensitize annotation) { - return annotation.prefixKeep(); - } - - @Override - Integer getSuffixKeep(MobileDesensitize annotation) { - return annotation.suffixKeep(); - } - - @Override - String getReplacer(MobileDesensitize annotation) { - return annotation.replacer(); - } -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/PasswordDesensitization.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/PasswordDesensitization.java deleted file mode 100644 index 55dfc56..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/core/slider/handler/PasswordDesensitization.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.desensitize.core.slider.handler; - -import com.iailab.framework.desensitize.core.slider.annotation.PasswordDesensitize; - -/** - * {@link PasswordDesensitize} 的码脱敏处理器 - * - * @author gaibu - */ -public class PasswordDesensitization extends AbstractSliderDesensitizationHandler<PasswordDesensitize> { - @Override - Integer getPrefixKeep(PasswordDesensitize annotation) { - return annotation.prefixKeep(); - } - - @Override - Integer getSuffixKeep(PasswordDesensitize annotation) { - return annotation.suffixKeep(); - } - - @Override - String getReplacer(PasswordDesensitize annotation) { - return annotation.replacer(); - } -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/package-info.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/package-info.java deleted file mode 100644 index 0634924..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/desensitize/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 脱敏组件:支持 JSON 返回数据时,将邮箱、手机等字段进行脱敏 - */ -package com.iailab.framework.desensitize; diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/config/IailabJacksonAutoConfiguration.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/config/IailabJacksonAutoConfiguration.java deleted file mode 100644 index 713525c..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/config/IailabJacksonAutoConfiguration.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.iailab.framework.jackson.config; - -import cn.hutool.core.collection.CollUtil; -import com.iailab.framework.common.util.json.JsonUtils; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; -import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; -import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; -import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; -import com.iailab.framework.common.util.json.databind.NumberSerializer; -import com.iailab.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer; -import com.iailab.framework.common.util.json.databind.TimestampLocalDateTimeSerializer; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Bean; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.List; - -@AutoConfiguration -@Slf4j -public class IailabJacksonAutoConfiguration { - - @Bean - @SuppressWarnings("InstantiationOfUtilityClass") - public JsonUtils jsonUtils(List<ObjectMapper> objectMappers) { - // 1.1 创建 SimpleModule 对象 - SimpleModule simpleModule = new SimpleModule(); - simpleModule - // 新增 Long 类型序列化规则,数值超过 2^53-1,在 JS 会出现精度丢失问题,因此 Long 自动序列化为字符串类型 - .addSerializer(Long.class, NumberSerializer.INSTANCE) - .addSerializer(Long.TYPE, NumberSerializer.INSTANCE) - .addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE) - .addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE) - .addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE) - .addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE) - // 新增 LocalDateTime 序列化、反序列化规则,使用 Long 时间戳 - .addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE) - .addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE); - // 1.2 注册到 objectMapper - objectMappers.forEach(objectMapper -> objectMapper.registerModule(simpleModule)); - - // 2. 设置 objectMapper 到 JsonUtils - JsonUtils.init(CollUtil.getFirst(objectMappers)); - log.info("[init][初始化 JsonUtils 成功]"); - return new JsonUtils(); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/core/databind/NumberSerializer.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/core/databind/NumberSerializer.java deleted file mode 100644 index 53a6a75..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/core/databind/NumberSerializer.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.iailab.framework.jackson.core.databind; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; - -import java.io.IOException; - -/** - * Long 序列化规则 - * - * 会将超长 long 值转换为 string,解决前端 JavaScript 最大安全整数是 2^53-1 的问题 - * - * @author 星语 - */ -@JacksonStdImpl -public class NumberSerializer extends com.fasterxml.jackson.databind.ser.std.NumberSerializer { - - private static final long MAX_SAFE_INTEGER = 9007199254740991L; - private static final long MIN_SAFE_INTEGER = -9007199254740991L; - - public static final NumberSerializer INSTANCE = new NumberSerializer(Number.class); - - public NumberSerializer(Class<? extends Number> rawType) { - super(rawType); - } - - @Override - public void serialize(Number value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - // 超出范围 序列化位字符串 - if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) { - super.serialize(value, gen, serializers); - } else { - gen.writeString(value.toString()); - } - } -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/core/databind/TimestampLocalDateTimeDeserializer.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/core/databind/TimestampLocalDateTimeDeserializer.java deleted file mode 100644 index 4266193..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/core/databind/TimestampLocalDateTimeDeserializer.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.iailab.framework.jackson.core.databind; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; - -import java.io.IOException; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; - -/** - * 基于时间戳的 LocalDateTime 反序列化器 - * - * @author 老五 - */ -public class TimestampLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> { - - public static final TimestampLocalDateTimeDeserializer INSTANCE = new TimestampLocalDateTimeDeserializer(); - - @Override - public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - // 将 Long 时间戳,转换为 LocalDateTime 对象 - return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault()); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/core/databind/TimestampLocalDateTimeSerializer.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/core/databind/TimestampLocalDateTimeSerializer.java deleted file mode 100644 index f6485db..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/core/databind/TimestampLocalDateTimeSerializer.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.iailab.framework.jackson.core.databind; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; - -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.ZoneId; - -/** - * 基于时间戳的 LocalDateTime 序列化器 - * - * @author 老五 - */ -public class TimestampLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> { - - public static final TimestampLocalDateTimeSerializer INSTANCE = new TimestampLocalDateTimeSerializer(); - - @Override - public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - // 将 LocalDateTime 对象,转换为 Long 时间戳 - gen.writeNumber(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/core/package-info.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/core/package-info.java deleted file mode 100644 index 922937d..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/core/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.iailab.framework.jackson.core; diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/package-info.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/package-info.java deleted file mode 100644 index 0deb23c..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Web 框架,全局异常、API 日志等 - */ -package com.iailab.framework; diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/swagger/config/IailabSwaggerAutoConfiguration.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/swagger/config/IailabSwaggerAutoConfiguration.java deleted file mode 100644 index 331e7f0..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/swagger/config/IailabSwaggerAutoConfiguration.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.iailab.framework.swagger.config; - -import io.swagger.v3.oas.models.Components; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.info.Contact; -import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.info.License; -import io.swagger.v3.oas.models.media.IntegerSchema; -import io.swagger.v3.oas.models.media.StringSchema; -import io.swagger.v3.oas.models.parameters.Parameter; -import io.swagger.v3.oas.models.security.SecurityRequirement; -import io.swagger.v3.oas.models.security.SecurityScheme; -import org.springdoc.core.*; -import org.springdoc.core.customizers.OpenApiBuilderCustomizer; -import org.springdoc.core.customizers.ServerBaseUrlCustomizer; -import org.springdoc.core.providers.JavadocProvider; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -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.Primary; -import org.springframework.http.HttpHeaders; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import static com.iailab.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; - -/** - * Swagger 自动配置类,基于 OpenAPI + Springdoc 实现。 - * - * 友情提示: - * 1. Springdoc 文档地址:<a href="https://github.com/springdoc/springdoc-openapi">仓库</a> - * 2. Swagger 规范,于 2015 更名为 OpenAPI 规范,本质是一个东西 - * - * @author iailab - */ -@AutoConfiguration -@ConditionalOnClass({OpenAPI.class}) -@EnableConfigurationProperties(SwaggerProperties.class) -@ConditionalOnProperty(prefix = "springdoc.api-docs", name = "enabled", havingValue = "true", matchIfMissing = true) // 设置为 false 时,禁用 -public class IailabSwaggerAutoConfiguration { - - // ========== 全局 OpenAPI 配置 ========== - - @Bean - public OpenAPI createApi(SwaggerProperties properties) { - Map<String, SecurityScheme> securitySchemas = buildSecuritySchemes(); - OpenAPI openAPI = new OpenAPI() - // 接口信息 - .info(buildInfo(properties)) - // 接口安全配置 - .components(new Components().securitySchemes(securitySchemas)) - .addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION)); - securitySchemas.keySet().forEach(key -> openAPI.addSecurityItem(new SecurityRequirement().addList(key))); - return openAPI; - } - - /** - * API 摘要信息 - */ - private Info buildInfo(SwaggerProperties properties) { - return new Info() - .title(properties.getTitle()) - .description(properties.getDescription()) - .version(properties.getVersion()) - .contact(new Contact().name(properties.getAuthor()).url(properties.getUrl()).email(properties.getEmail())) - .license(new License().name(properties.getLicense()).url(properties.getLicenseUrl())); - } - - /** - * 安全模式,这里配置通过请求头 Authorization 传递 token 参数 - */ - private Map<String, SecurityScheme> buildSecuritySchemes() { - Map<String, SecurityScheme> securitySchemes = new HashMap<>(); - SecurityScheme securityScheme = new SecurityScheme() - .type(SecurityScheme.Type.APIKEY) // 类型 - .name(HttpHeaders.AUTHORIZATION) // 请求头的 name - .in(SecurityScheme.In.HEADER); // token 所在位置 - securitySchemes.put(HttpHeaders.AUTHORIZATION, securityScheme); - return securitySchemes; - } - - /** - * 自定义 OpenAPI 处理器 - */ - @Bean - @Primary // 目的:以我们创建的 OpenAPIService Bean 为主,避免一键改包后,启动报错! - public OpenAPIService openApiBuilder(Optional<OpenAPI> openAPI, - SecurityService securityParser, - SpringDocConfigProperties springDocConfigProperties, - PropertyResolverUtils propertyResolverUtils, - Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers, - Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers, - Optional<JavadocProvider> javadocProvider) { - - return new OpenAPIService(openAPI, securityParser, springDocConfigProperties, - propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider); - } - - // ========== 分组 OpenAPI 配置 ========== - - /** - * 所有模块的 API 分组 - */ - @Bean - public GroupedOpenApi allGroupedOpenApi() { - return buildGroupedOpenApi("all", ""); - } - - public static GroupedOpenApi buildGroupedOpenApi(String group) { - return buildGroupedOpenApi(group, group); - } - - public static GroupedOpenApi buildGroupedOpenApi(String group, String path) { - return GroupedOpenApi.builder() - .group(group) - .pathsToMatch("/admin-api/" + path + "/**", "/app-api/" + path + "/**") - .addOperationCustomizer((operation, handlerMethod) -> operation - .addParametersItem(buildTenantHeaderParameter()) - .addParametersItem(buildSecurityHeaderParameter())) - .build(); - } - - /** - * 构建 Tenant 租户编号请求头参数 - * - * @return 多租户参数 - */ - private static Parameter buildTenantHeaderParameter() { - return new Parameter() - .name(HEADER_TENANT_ID) // header 名 - .description("租户编号") // 描述 - .in(String.valueOf(SecurityScheme.In.HEADER)) // 请求 header - .schema(new IntegerSchema()._default(1L).name(HEADER_TENANT_ID).description("租户编号")); // 默认:使用租户编号为 1 - } - - /** - * 构建 Authorization 认证请求头参数 - * - * 解决 Knife4j <a href="https://gitee.com/xiaoym/knife4j/issues/I69QBU">Authorize 未生效,请求header里未包含参数</a> - * - * @return 认证参数 - */ - private static Parameter buildSecurityHeaderParameter() { - return new Parameter() - .name(HttpHeaders.AUTHORIZATION) // header 名 - .description("认证 Token") // 描述 - .in(String.valueOf(SecurityScheme.In.HEADER)) // 请求 header - .schema(new StringSchema()._default("Bearer test1").name(HEADER_TENANT_ID).description("认证 Token")); // 默认:使用用户编号为 1 - } - -} - diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/swagger/config/SwaggerProperties.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/swagger/config/SwaggerProperties.java deleted file mode 100644 index ab3bfeb..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/swagger/config/SwaggerProperties.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.iailab.framework.swagger.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; - -import javax.validation.constraints.NotEmpty; - -/** - * Swagger 配置属性 - * - * @author iailab - */ -@ConfigurationProperties("iailab.swagger") -@Data -public class SwaggerProperties { - - /** - * 标题 - */ - @NotEmpty(message = "标题不能为空") - private String title; - /** - * 描述 - */ - @NotEmpty(message = "描述不能为空") - private String description; - /** - * 作者 - */ - @NotEmpty(message = "作者不能为空") - private String author; - /** - * 版本 - */ - @NotEmpty(message = "版本不能为空") - private String version; - /** - * url - */ - @NotEmpty(message = "扫描的 package 不能为空") - private String url; - /** - * email - */ - @NotEmpty(message = "扫描的 email 不能为空") - private String email; - - /** - * license - */ - @NotEmpty(message = "扫描的 license 不能为空") - private String license; - - /** - * license-url - */ - @NotEmpty(message = "扫描的 license-url 不能为空") - private String licenseUrl; - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/swagger/package-info.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/swagger/package-info.java deleted file mode 100644 index f30bbfb..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/swagger/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 基于 Swagger + Knife4j 实现 API 接口文档 - * - * @author iailab - */ -package com.iailab.framework.swagger; diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/config/IailabWebAutoConfiguration.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/config/IailabWebAutoConfiguration.java deleted file mode 100644 index 0a4b9cb..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/config/IailabWebAutoConfiguration.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.iailab.framework.web.config; - -import com.iailab.framework.apilog.core.service.ApiErrorLogFrameworkService; -import com.iailab.framework.common.enums.WebFilterOrderEnum; -import com.iailab.framework.web.core.filter.CacheRequestBodyFilter; -import com.iailab.framework.web.core.filter.DemoFilter; -import com.iailab.framework.web.core.handler.GlobalExceptionHandler; -import com.iailab.framework.web.core.handler.GlobalResponseBodyHandler; -import com.iailab.framework.web.core.util.WebFrameworkUtils; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.util.AntPathMatcher; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; -import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import javax.annotation.Resource; -import javax.servlet.Filter; - -@AutoConfiguration -@EnableConfigurationProperties(WebProperties.class) -public class IailabWebAutoConfiguration implements WebMvcConfigurer { - - @Resource - private WebProperties webProperties; - /** - * 应用名 - */ - @Value("${spring.application.name}") - private String applicationName; - - @Override - public void configurePathMatch(PathMatchConfigurer configurer) { - configurePathMatch(configurer, webProperties.getAdminApi()); - configurePathMatch(configurer, webProperties.getAppApi()); - } - - /** - * 设置 API 前缀,仅仅匹配 controller 包下的 - * - * @param configurer 配置 - * @param api API 配置 - */ - private void configurePathMatch(PathMatchConfigurer configurer, WebProperties.Api api) { - AntPathMatcher antPathMatcher = new AntPathMatcher("."); - configurer.addPathPrefix(api.getPrefix(), clazz -> clazz.isAnnotationPresent(RestController.class) - && antPathMatcher.match(api.getController(), clazz.getPackage().getName())); // 仅仅匹配 controller 包 - } - - @Bean - public GlobalExceptionHandler globalExceptionHandler(ApiErrorLogFrameworkService ApiErrorLogFrameworkService) { - return new GlobalExceptionHandler(applicationName, ApiErrorLogFrameworkService); - } - - @Bean - public GlobalResponseBodyHandler globalResponseBodyHandler() { - return new GlobalResponseBodyHandler(); - } - - @Bean - @SuppressWarnings("InstantiationOfUtilityClass") - public WebFrameworkUtils webFrameworkUtils(WebProperties webProperties) { - // 由于 WebFrameworkUtils 需要使用到 webProperties 属性,所以注册为一个 Bean - return new WebFrameworkUtils(webProperties); - } - - // ========== Filter 相关 ========== - - /** - * 创建 CorsFilter Bean,解决跨域问题 - */ - @Bean - public FilterRegistrationBean<CorsFilter> corsFilterBean() { - // 创建 CorsConfiguration 对象 - CorsConfiguration config = new CorsConfiguration(); - config.setAllowCredentials(true); - config.addAllowedOriginPattern("*"); // 设置访问源地址 - config.addAllowedHeader("*"); // 设置访问源请求头 - config.addAllowedMethod("*"); // 设置访问源请求方法 - // 创建 UrlBasedCorsConfigurationSource 对象 - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", config); // 对接口配置跨域设置 - return createFilterBean(new CorsFilter(source), WebFilterOrderEnum.CORS_FILTER); - } - - /** - * 创建 RequestBodyCacheFilter Bean,可重复读取请求内容 - */ - @Bean - public FilterRegistrationBean<CacheRequestBodyFilter> requestBodyCacheFilter() { - return createFilterBean(new CacheRequestBodyFilter(), WebFilterOrderEnum.REQUEST_BODY_CACHE_FILTER); - } - - /** - * 创建 DemoFilter Bean,演示模式 - */ - @Bean - @ConditionalOnProperty(value = "iailab.demo", havingValue = "true") - public FilterRegistrationBean<DemoFilter> demoFilter() { - return createFilterBean(new DemoFilter(), WebFilterOrderEnum.DEMO_FILTER); - } - - public static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) { - FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter); - bean.setOrder(order); - return bean; - } - - /** - * 创建 RestTemplate 实例 - * - * @param restTemplateBuilder {@link RestTemplateAutoConfiguration#restTemplateBuilder} - */ - @Bean - @ConditionalOnMissingBean - public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { - return restTemplateBuilder.build(); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/config/WebProperties.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/config/WebProperties.java deleted file mode 100644 index ecc5ab0..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/config/WebProperties.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.iailab.framework.web.config; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; - -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -@ConfigurationProperties(prefix = "iailab.web") -@Validated -@Data -public class WebProperties { - - @NotNull(message = "APP API 不能为空") - private Api appApi = new Api("/app-api", "**.controller.app.**"); - @NotNull(message = "Admin API 不能为空") - private Api adminApi = new Api("/admin-api", "**.controller.admin.**"); - - @NotNull(message = "Admin UI 不能为空") - private Ui adminUi; - - @Data - @AllArgsConstructor - @NoArgsConstructor - @Valid - public static class Api { - - /** - * API 前缀,实现所有 Controller 提供的 RESTFul API 的统一前缀 - * - * - * 意义:通过该前缀,避免 Swagger、Actuator 意外通过 Nginx 暴露出来给外部,带来安全性问题 - * 这样,Nginx 只需要配置转发到 /api/* 的所有接口即可。 - * - * @see IailabWebAutoConfiguration#configurePathMatch(PathMatchConfigurer) - */ - @NotEmpty(message = "API 前缀不能为空") - private String prefix; - - /** - * Controller 所在包的 Ant 路径规则 - * - * 主要目的是,给该 Controller 设置指定的 {@link #prefix} - */ - @NotEmpty(message = "Controller 所在包不能为空") - private String controller; - - } - - @Data - @Valid - public static class Ui { - - /** - * 访问地址 - */ - private String url; - - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/filter/ApiRequestFilter.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/filter/ApiRequestFilter.java deleted file mode 100644 index d3e4e46..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/filter/ApiRequestFilter.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.iailab.framework.web.core.filter; - -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.web.config.WebProperties; -import lombok.RequiredArgsConstructor; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.http.HttpServletRequest; - -/** - * 过滤 /admin-api、/app-api 等 API 请求的过滤器 - * - * @author iailab - */ -@RequiredArgsConstructor -public abstract class ApiRequestFilter extends OncePerRequestFilter { - - protected final WebProperties webProperties; - - @Override - protected boolean shouldNotFilter(HttpServletRequest request) { - // 只过滤 API 请求的地址 - return !StrUtil.startWithAny(request.getRequestURI(), webProperties.getAdminApi().getPrefix(), - webProperties.getAppApi().getPrefix()); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/filter/CacheRequestBodyFilter.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/filter/CacheRequestBodyFilter.java deleted file mode 100644 index 84a1e9a..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/filter/CacheRequestBodyFilter.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.iailab.framework.web.core.filter; - -import com.iailab.framework.common.util.servlet.ServletUtils; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * Request Body 缓存 Filter,实现它的可重复读取 - * - * @author iailab - */ -public class CacheRequestBodyFilter extends OncePerRequestFilter { - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws IOException, ServletException { - filterChain.doFilter(new CacheRequestBodyWrapper(request), response); - } - - @Override - protected boolean shouldNotFilter(HttpServletRequest request) { - // 只处理 json 请求内容 - return !ServletUtils.isJsonRequest(request); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/filter/CacheRequestBodyWrapper.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/filter/CacheRequestBodyWrapper.java deleted file mode 100644 index 18a5aa1..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/filter/CacheRequestBodyWrapper.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.iailab.framework.web.core.filter; - -import com.iailab.framework.common.util.servlet.ServletUtils; - -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; - -/** - * Request Body 缓存 Wrapper - * - * @author iailab - */ -public class CacheRequestBodyWrapper extends HttpServletRequestWrapper { - - /** - * 缓存的内容 - */ - private final byte[] body; - - public CacheRequestBodyWrapper(HttpServletRequest request) { - super(request); - body = ServletUtils.getBodyBytes(request); - } - - @Override - public BufferedReader getReader() throws IOException { - return new BufferedReader(new InputStreamReader(this.getInputStream())); - } - - @Override - public ServletInputStream getInputStream() throws IOException { - final ByteArrayInputStream inputStream = new ByteArrayInputStream(body); - // 返回 ServletInputStream - return new ServletInputStream() { - - @Override - public int read() { - return inputStream.read(); - } - - @Override - public boolean isFinished() { - return false; - } - - @Override - public boolean isReady() { - return false; - } - - @Override - public void setReadListener(ReadListener readListener) {} - - @Override - public int available() { - return body.length; - } - - }; - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/filter/DemoFilter.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/filter/DemoFilter.java deleted file mode 100644 index 43bd5cb..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/filter/DemoFilter.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.iailab.framework.web.core.filter; - -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.util.servlet.ServletUtils; -import com.iailab.framework.web.core.util.WebFrameworkUtils; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import static com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants.DEMO_DENY; - -/** - * 演示 Filter,禁止用户发起写操作,避免影响测试数据 - * - * @author iailab - */ -public class DemoFilter extends OncePerRequestFilter { - - @Override - protected boolean shouldNotFilter(HttpServletRequest request) { - String method = request.getMethod(); - return !StrUtil.equalsAnyIgnoreCase(method, "POST", "PUT", "DELETE") // 写操作时,不进行过滤率 - || WebFrameworkUtils.getLoginUserId(request) == null; // 非登录用户时,不进行过滤 - } - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { - // 直接返回 DEMO_DENY 的结果。即,请求不继续 - ServletUtils.writeJSON(response, CommonResult.error(DEMO_DENY)); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/handler/GlobalExceptionHandler.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/handler/GlobalExceptionHandler.java deleted file mode 100644 index 79930b8..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/handler/GlobalExceptionHandler.java +++ /dev/null @@ -1,356 +0,0 @@ -package com.iailab.framework.web.core.handler; - -import cn.hutool.core.exceptions.ExceptionUtil; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.apilog.core.service.ApiErrorLogFrameworkService; -import com.iailab.framework.common.exception.ServiceException; -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.util.collection.SetUtils; -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.framework.common.util.monitor.TracerUtils; -import com.iailab.framework.common.util.servlet.ServletUtils; -import com.iailab.framework.web.core.util.WebFrameworkUtils; -import com.iailab.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.util.Assert; -import org.springframework.validation.BindException; -import org.springframework.validation.FieldError; -import org.springframework.web.HttpRequestMethodNotSupportedException; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.MissingServletRequestParameterException; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; -import org.springframework.web.servlet.NoHandlerFoundException; -import org.springframework.dao.DuplicateKeyException; - -import javax.servlet.http.HttpServletRequest; -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; -import javax.validation.ValidationException; -import java.time.LocalDateTime; -import java.util.Map; -import java.util.Set; - -import static com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants.*; - -/** - * 全局异常处理器,将 Exception 翻译成 CommonResult + 对应的异常编号 - * - * @author iailab - */ -@RestControllerAdvice -@AllArgsConstructor -@Slf4j -public class GlobalExceptionHandler { - - /** - * 忽略的 ServiceException 错误提示,避免打印过多 logger - */ - public static final Set<String> IGNORE_ERROR_MESSAGES = SetUtils.asSet("无效的刷新令牌"); - - @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") - private final String applicationName; - - private final ApiErrorLogFrameworkService apiErrorLogFrameworkService; - - /** - * 处理所有异常,主要是提供给 Filter 使用 - * 因为 Filter 不走 SpringMVC 的流程,但是我们又需要兜底处理异常,所以这里提供一个全量的异常处理过程,保持逻辑统一。 - * - * @param request 请求 - * @param ex 异常 - * @return 通用返回 - */ - public CommonResult<?> allExceptionHandler(HttpServletRequest request, Throwable ex) { - if (ex instanceof MissingServletRequestParameterException) { - return missingServletRequestParameterExceptionHandler((MissingServletRequestParameterException) ex); - } - if (ex instanceof MethodArgumentTypeMismatchException) { - return methodArgumentTypeMismatchExceptionHandler((MethodArgumentTypeMismatchException) ex); - } - if (ex instanceof MethodArgumentNotValidException) { - return methodArgumentNotValidExceptionExceptionHandler((MethodArgumentNotValidException) ex); - } - if (ex instanceof BindException) { - return bindExceptionHandler((BindException) ex); - } - if (ex instanceof ConstraintViolationException) { - return constraintViolationExceptionHandler((ConstraintViolationException) ex); - } - if (ex instanceof ValidationException) { - return validationException((ValidationException) ex); - } - if (ex instanceof NoHandlerFoundException) { - return noHandlerFoundExceptionHandler((NoHandlerFoundException) ex); - } - if (ex instanceof HttpRequestMethodNotSupportedException) { - return httpRequestMethodNotSupportedExceptionHandler((HttpRequestMethodNotSupportedException) ex); - } - if (ex instanceof ServiceException) { - return serviceExceptionHandler((ServiceException) ex); - } - if (ex instanceof AccessDeniedException) { - return accessDeniedExceptionHandler(request, (AccessDeniedException) ex); - } - if (ex instanceof DuplicateKeyException) { - return duplicateKeyExceptionHandler((DuplicateKeyException) ex); - } - return defaultExceptionHandler(request, ex); - } - - /** - * 处理 SpringMVC 请求参数缺失 - * - * 例如说,接口上设置了 @RequestParam("xx") 参数,结果并未传递 xx 参数 - */ - @ExceptionHandler(value = MissingServletRequestParameterException.class) - public CommonResult<?> missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException ex) { - log.warn("[missingServletRequestParameterExceptionHandler]", ex); - return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数缺失:%s", ex.getParameterName())); - } - - /** - * 处理 SpringMVC 请求参数类型错误 - * - * 例如说,接口上设置了 @RequestParam("xx") 参数为 Integer,结果传递 xx 参数类型为 String - */ - @ExceptionHandler(MethodArgumentTypeMismatchException.class) - public CommonResult<?> methodArgumentTypeMismatchExceptionHandler(MethodArgumentTypeMismatchException ex) { - log.warn("[missingServletRequestParameterExceptionHandler]", ex); - return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数类型错误:%s", ex.getMessage())); - } - - /** - * 处理 SpringMVC 参数校验不正确 - */ - @ExceptionHandler(MethodArgumentNotValidException.class) - public CommonResult<?> methodArgumentNotValidExceptionExceptionHandler(MethodArgumentNotValidException ex) { - log.warn("[methodArgumentNotValidExceptionExceptionHandler]", ex); - FieldError fieldError = ex.getBindingResult().getFieldError(); - assert fieldError != null; // 断言,避免告警 - return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数不正确:%s", fieldError.getDefaultMessage())); - } - - /** - * 处理 SpringMVC 参数绑定不正确,本质上也是通过 Validator 校验 - */ - @ExceptionHandler(BindException.class) - public CommonResult<?> bindExceptionHandler(BindException ex) { - log.warn("[handleBindException]", ex); - FieldError fieldError = ex.getFieldError(); - assert fieldError != null; // 断言,避免告警 - return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数不正确:%s", fieldError.getDefaultMessage())); - } - - /** - * 处理 Validator 校验不通过产生的异常 - */ - @ExceptionHandler(value = ConstraintViolationException.class) - public CommonResult<?> constraintViolationExceptionHandler(ConstraintViolationException ex) { - log.warn("[constraintViolationExceptionHandler]", ex); - ConstraintViolation<?> constraintViolation = ex.getConstraintViolations().iterator().next(); - return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数不正确:%s", constraintViolation.getMessage())); - } - - /** - * 处理 Dubbo Consumer 本地参数校验时,抛出的 ValidationException 异常 - */ - @ExceptionHandler(value = ValidationException.class) - public CommonResult<?> validationException(ValidationException ex) { - log.warn("[constraintViolationExceptionHandler]", ex); - // 无法拼接明细的错误信息,因为 Dubbo Consumer 抛出 ValidationException 异常时,是直接的字符串信息,且人类不可读 - return CommonResult.error(BAD_REQUEST); - } - - /** - * 处理 SpringMVC 请求地址不存在 - * - * 注意,它需要设置如下两个配置项: - * 1. spring.mvc.throw-exception-if-no-handler-found 为 true - * 2. spring.mvc.static-path-pattern 为 /statics/** - */ - @ExceptionHandler(NoHandlerFoundException.class) - public CommonResult<?> noHandlerFoundExceptionHandler(NoHandlerFoundException ex) { - log.warn("[noHandlerFoundExceptionHandler]", ex); - return CommonResult.error(NOT_FOUND.getCode(), String.format("请求地址不存在:%s", ex.getRequestURL())); - } - - /** - * 处理 SpringMVC 请求方法不正确 - * - * 例如说,A 接口的方法为 GET 方式,结果请求方法为 POST 方式,导致不匹配 - */ - @ExceptionHandler(HttpRequestMethodNotSupportedException.class) - public CommonResult<?> httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException ex) { - log.warn("[httpRequestMethodNotSupportedExceptionHandler]", ex); - return CommonResult.error(METHOD_NOT_ALLOWED.getCode(), String.format("请求方法不正确:%s", ex.getMessage())); - } - - /** - * 处理 Spring Security 权限不足的异常 - * - * 来源是,使用 @PreAuthorize 注解,AOP 进行权限拦截 - */ - @ExceptionHandler(value = AccessDeniedException.class) - public CommonResult<?> accessDeniedExceptionHandler(HttpServletRequest req, AccessDeniedException ex) { - log.warn("[accessDeniedExceptionHandler][userId({}) 无法访问 url({})]", WebFrameworkUtils.getLoginUserId(req), - req.getRequestURL(), ex); - return CommonResult.error(FORBIDDEN); - } - - /** - * 处理业务异常 SQLIntegrityConstraintViolationException - * - * 数据库存在重复数据 - */ - @ExceptionHandler(value = DuplicateKeyException.class) - public CommonResult<?> duplicateKeyExceptionHandler(DuplicateKeyException ex) { - log.warn("[duplicateKeyExceptionHandler]", ex); - return CommonResult.error(DATA_REPETITION.getCode(), DATA_REPETITION.getMsg()); - } - - /** - * 处理业务异常 ServiceException - * - * 例如说,商品库存不足,用户手机号已存在。 - */ - @ExceptionHandler(value = ServiceException.class) - public CommonResult<?> serviceExceptionHandler(ServiceException ex) { - // 不包含的时候,才进行打印,避免 ex 堆栈过多 - if (!IGNORE_ERROR_MESSAGES.contains(ex.getMessage())) { - // 即使打印,也只打印第一层 StackTraceElement,并且使用 warn 在控制台输出,更容易看到 - StackTraceElement[] stackTrace = ex.getStackTrace(); - log.warn("[serviceExceptionHandler]\n\t{}", stackTrace[0]); - } - return CommonResult.error(ex.getCode(), ex.getMessage()); - } - - /** - * 处理系统异常,兜底处理所有的一切 - */ - @ExceptionHandler(value = Exception.class) - public CommonResult<?> defaultExceptionHandler(HttpServletRequest req, Throwable ex) { - // 情况一:处理表不存在的异常 - CommonResult<?> tableNotExistsResult = handleTableNotExists(ex); - if (tableNotExistsResult != null) { - return tableNotExistsResult; - } - - // 情况二:处理异常 - log.error("[defaultExceptionHandler]", ex); - // 插入异常日志 - createExceptionLog(req, ex); - // 返回 ERROR CommonResult - return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); - } - - private void createExceptionLog(HttpServletRequest req, Throwable e) { - // 插入错误日志 - ApiErrorLogCreateReqDTO errorLog = new ApiErrorLogCreateReqDTO(); - try { - // 初始化 errorLog - buildExceptionLog(errorLog, req, e); - // 执行插入 errorLog - apiErrorLogFrameworkService.createApiErrorLog(errorLog); - } catch (Throwable th) { - log.error("[createExceptionLog][url({}) log({}) 发生异常]", req.getRequestURI(), JsonUtils.toJsonString(errorLog), th); - } - } - - private void buildExceptionLog(ApiErrorLogCreateReqDTO errorLog, HttpServletRequest request, Throwable e) { - // 处理用户信息 - errorLog.setUserId(WebFrameworkUtils.getLoginUserId(request)); - errorLog.setUserType(WebFrameworkUtils.getLoginUserType(request)); - // 设置异常字段 - errorLog.setExceptionName(e.getClass().getName()); - errorLog.setExceptionMessage(ExceptionUtil.getMessage(e)); - errorLog.setExceptionRootCauseMessage(ExceptionUtil.getRootCauseMessage(e)); - errorLog.setExceptionStackTrace(ExceptionUtil.stacktraceToString(e)); - StackTraceElement[] stackTraceElements = e.getStackTrace(); - Assert.notEmpty(stackTraceElements, "异常 stackTraceElements 不能为空"); - StackTraceElement stackTraceElement = stackTraceElements[0]; - errorLog.setExceptionClassName(stackTraceElement.getClassName()); - errorLog.setExceptionFileName(stackTraceElement.getFileName()); - errorLog.setExceptionMethodName(stackTraceElement.getMethodName()); - errorLog.setExceptionLineNumber(stackTraceElement.getLineNumber()); - // 设置其它字段 - errorLog.setTraceId(TracerUtils.getTraceId()); - errorLog.setApplicationName(applicationName); - errorLog.setRequestUrl(request.getRequestURI()); - Map<String, Object> requestParams = MapUtil.<String, Object>builder() - .put("query", ServletUtils.getParamMap(request)) - .put("body", ServletUtils.getBody(request)).build(); - errorLog.setRequestParams(JsonUtils.toJsonString(requestParams)); - errorLog.setRequestMethod(request.getMethod()); - errorLog.setUserAgent(ServletUtils.getUserAgent(request)); - errorLog.setUserIp(ServletUtils.getClientIP(request)); - errorLog.setExceptionTime(LocalDateTime.now()); - } - - /** - * 处理 Table 不存在的异常情况 - * - * @param ex 异常 - * @return 如果是 Table 不存在的异常,则返回对应的 CommonResult - */ - private CommonResult<?> handleTableNotExists(Throwable ex) { - String message = ExceptionUtil.getRootCauseMessage(ex); - if (!message.contains("doesn't exist")) { - return null; - } - // 1. 数据报表 - if (message.contains("report_")) { - log.error("[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]"); - return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]"); - } - // 2. 工作流 - if (message.contains("bpm_")) { - log.error("[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]"); - return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]"); - } - // 3. 微信公众号 - if (message.contains("mp_")) { - log.error("[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]"); - return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]"); - } - // 4. 商城系统 - if (StrUtil.containsAny(message, "product_", "promotion_", "trade_")) { - log.error("[商城系统 yudao-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]"); - return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[商城系统 yudao-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]"); - } - // 5. ERP 系统 - if (message.contains("erp_")) { - log.error("[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]"); - return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]"); - } - // 6. CRM 系统 - if (message.contains("crm_")) { - log.error("[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]"); - return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]"); - } - // 7. 支付平台 - if (message.contains("pay_")) { - log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]"); - return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]"); - } - // 8. AI 大模型 - if (message.contains("ai_")) { - log.error("[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]"); - return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]"); - } - return null; - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/handler/GlobalResponseBodyHandler.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/handler/GlobalResponseBodyHandler.java deleted file mode 100644 index 36b3aca..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/handler/GlobalResponseBodyHandler.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.iailab.framework.web.core.handler; - -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.web.core.util.WebFrameworkUtils; -import org.springframework.core.MethodParameter; -import org.springframework.http.MediaType; -import org.springframework.http.server.ServerHttpRequest; -import org.springframework.http.server.ServerHttpResponse; -import org.springframework.http.server.ServletServerHttpRequest; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; - -/** - * 全局响应结果(ResponseBody)处理器 - * - * 不同于在网上看到的很多文章,会选择自动将 Controller 返回结果包上 {@link CommonResult}, - * 在 onemall 中,是 Controller 在返回时,主动自己包上 {@link CommonResult}。 - * 原因是,GlobalResponseBodyHandler 本质上是 AOP,它不应该改变 Controller 返回的数据结构 - * - * 目前,GlobalResponseBodyHandler 的主要作用是,记录 Controller 的返回结果, - * 方便 {@link com.iailab.framework.apilog.core.filter.ApiAccessLogFilter} 记录访问日志 - */ -@ControllerAdvice -public class GlobalResponseBodyHandler implements ResponseBodyAdvice { - - @Override - @SuppressWarnings("NullableProblems") // 避免 IDEA 警告 - public boolean supports(MethodParameter returnType, Class converterType) { - if (returnType.getMethod() == null) { - return false; - } - // 只拦截返回结果为 CommonResult 类型 - return returnType.getMethod().getReturnType() == CommonResult.class; - } - - @Override - @SuppressWarnings("NullableProblems") // 避免 IDEA 警告 - public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, - ServerHttpRequest request, ServerHttpResponse response) { - // 记录 Controller 结果 - WebFrameworkUtils.setCommonResult(((ServletServerHttpRequest) request).getServletRequest(), (CommonResult<?>) body); - return body; - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/util/WebFrameworkUtils.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/util/WebFrameworkUtils.java deleted file mode 100644 index 08cc572..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/util/WebFrameworkUtils.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.iailab.framework.web.core.util; - -import cn.hutool.core.util.NumberUtil; -import cn.hutool.extra.servlet.ServletUtil; -import com.iailab.framework.common.enums.RpcConstants; -import com.iailab.framework.common.enums.TerminalEnum; -import com.iailab.framework.common.enums.UserTypeEnum; -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.util.servlet.ServletUtils; -import com.iailab.framework.web.config.WebProperties; -import org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import javax.servlet.ServletRequest; -import javax.servlet.http.HttpServletRequest; - -/** - * 专属于 web 包的工具类 - * - * @author iailab - */ -public class WebFrameworkUtils { - - private static final String REQUEST_ATTRIBUTE_LOGIN_USER_ID = "login_user_id"; - private static final String REQUEST_ATTRIBUTE_LOGIN_USER_TYPE = "login_user_type"; - - private static final String REQUEST_ATTRIBUTE_COMMON_RESULT = "common_result"; - - public static final String HEADER_TENANT_ID = "tenant-id"; - - /** - * 终端的 Header - * - * @see com.iailab.framework.common.enums.TerminalEnum - */ - public static final String HEADER_TERMINAL = "terminal"; - - private static WebProperties properties; - - public WebFrameworkUtils(WebProperties webProperties) { - WebFrameworkUtils.properties = webProperties; - } - - /** - * 获得租户编号,从 header 中 - * 考虑到其它 framework 组件也会使用到租户编号,所以不得不放在 WebFrameworkUtils 统一提供 - * - * @param request 请求 - * @return 租户编号 - */ - public static Long getTenantId(HttpServletRequest request) { - String tenantId = request.getHeader(HEADER_TENANT_ID); - return NumberUtil.isNumber(tenantId) ? Long.valueOf(tenantId) : null; - } - - public static void setLoginUserId(ServletRequest request, Long userId) { - request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_ID, userId); - } - - /** - * 设置用户类型 - * - * @param request 请求 - * @param userType 用户类型 - */ - public static void setLoginUserType(ServletRequest request, Integer userType) { - request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE, userType); - } - - /** - * 获得当前用户的编号,从请求中 - * 注意:该方法仅限于 framework 框架使用!!! - * - * @param request 请求 - * @return 用户编号 - */ - public static Long getLoginUserId(HttpServletRequest request) { - if (request == null) { - return null; - } - return (Long) request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_ID); - } - - /** - * 获得当前用户的类型 - * 注意:该方法仅限于 web 相关的 framework 组件使用!!! - * - * @param request 请求 - * @return 用户编号 - */ - public static Integer getLoginUserType(HttpServletRequest request) { - if (request == null) { - return null; - } - // 1. 优先,从 Attribute 中获取 - Integer userType = (Integer) request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE); - if (userType != null) { - return userType; - } - // 2. 其次,基于 URL 前缀的约定 - if (request.getServletPath().startsWith(properties.getAdminApi().getPrefix())) { - return UserTypeEnum.ADMIN.getValue(); - } - if (request.getServletPath().startsWith(properties.getAppApi().getPrefix())) { - return UserTypeEnum.MEMBER.getValue(); - } - return null; - } - - public static Integer getLoginUserType() { - HttpServletRequest request = getRequest(); - return getLoginUserType(request); - } - - public static Long getLoginUserId() { - HttpServletRequest request = getRequest(); - return getLoginUserId(request); - } - - public static Integer getTerminal() { - HttpServletRequest request = getRequest(); - if (request == null) { - return TerminalEnum.UNKNOWN.getTerminal(); - } - String terminalValue = request.getHeader(HEADER_TERMINAL); - return NumberUtil.parseInt(terminalValue, TerminalEnum.UNKNOWN.getTerminal()); - } - - public static void setCommonResult(ServletRequest request, CommonResult<?> result) { - request.setAttribute(REQUEST_ATTRIBUTE_COMMON_RESULT, result); - } - - public static CommonResult<?> getCommonResult(ServletRequest request) { - return (CommonResult<?>) request.getAttribute(REQUEST_ATTRIBUTE_COMMON_RESULT); - } - - public static HttpServletRequest getRequest() { - RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); - if (!(requestAttributes instanceof ServletRequestAttributes)) { - return null; - } - ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes; - return servletRequestAttributes.getRequest(); - } - - /** - * 判断是否为 RPC 请求 - * - * @param request 请求 - * @return 是否为 RPC 请求 - */ - public static boolean isRpcRequest(HttpServletRequest request) { - return request.getRequestURI().startsWith(RpcConstants.RPC_API_PREFIX); - } - - /** - * 判断是否为 RPC 请求 - * - * 约定大于配置,只要以 Api 结尾,都认为是 RPC 接口 - * - * @param className 类名 - * @return 是否为 RPC 请求 - */ - public static boolean isRpcRequest(String className) { - return className.endsWith("Api"); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/package-info.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/package-info.java deleted file mode 100644 index 53e1115..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 针对 SpringMVC 的基础封装 - */ -package com.iailab.framework.web; diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/config/IailabXssAutoConfiguration.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/config/IailabXssAutoConfiguration.java deleted file mode 100644 index ed97017..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/config/IailabXssAutoConfiguration.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.iailab.framework.xss.config; - -import com.iailab.framework.common.enums.WebFilterOrderEnum; -import com.iailab.framework.xss.core.clean.JsoupXssCleaner; -import com.iailab.framework.xss.core.clean.XssCleaner; -import com.iailab.framework.xss.core.filter.XssFilter; -import com.iailab.framework.xss.core.json.XssStringJsonDeserializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.util.PathMatcher; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import static com.iailab.framework.web.config.IailabWebAutoConfiguration.createFilterBean; - -@AutoConfiguration -@EnableConfigurationProperties(XssProperties.class) -@ConditionalOnProperty(prefix = "iailab.xss", name = "enable", havingValue = "true", matchIfMissing = true) // 设置为 false 时,禁用 -public class IailabXssAutoConfiguration implements WebMvcConfigurer { - - /** - * Xss 清理者 - * - * @return XssCleaner - */ - @Bean - @ConditionalOnMissingBean(XssCleaner.class) - public XssCleaner xssCleaner() { - return new JsoupXssCleaner(); - } - - /** - * 注册 Jackson 的序列化器,用于处理 json 类型参数的 xss 过滤 - * - * @return Jackson2ObjectMapperBuilderCustomizer - */ - @Bean - @ConditionalOnMissingBean(name = "xssJacksonCustomizer") - @ConditionalOnBean(ObjectMapper.class) - @ConditionalOnProperty(value = "iailab.xss.enable", havingValue = "true") - public Jackson2ObjectMapperBuilderCustomizer xssJacksonCustomizer(XssProperties properties, - PathMatcher pathMatcher, - XssCleaner xssCleaner) { - // 在反序列化时进行 xss 过滤,可以替换使用 XssStringJsonSerializer,在序列化时进行处理 - return builder -> builder.deserializerByType(String.class, new XssStringJsonDeserializer(properties, pathMatcher, xssCleaner)); - } - - /** - * 创建 XssFilter Bean,解决 Xss 安全问题 - */ - @Bean - @ConditionalOnBean(XssCleaner.class) - public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher, XssCleaner xssCleaner) { - return createFilterBean(new XssFilter(properties, pathMatcher, xssCleaner), WebFilterOrderEnum.XSS_FILTER); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/config/XssProperties.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/config/XssProperties.java deleted file mode 100644 index 1c4d970..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/config/XssProperties.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.iailab.framework.xss.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.validation.annotation.Validated; - -import java.util.Collections; -import java.util.List; - -/** - * Xss 配置属性 - * - * @author iailab - */ -@ConfigurationProperties(prefix = "iailab.xss") -@Validated -@Data -public class XssProperties { - - /** - * 是否开启,默认为 true - */ - private boolean enable = true; - /** - * 需要排除的 URL,默认为空 - */ - private List<String> excludeUrls = Collections.emptyList(); - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/clean/JsoupXssCleaner.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/clean/JsoupXssCleaner.java deleted file mode 100644 index 4636994..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/clean/JsoupXssCleaner.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.iailab.framework.xss.core.clean; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.safety.Safelist; - -/** - * 基于 JSONP 实现 XSS 过滤字符串 - */ -public class JsoupXssCleaner implements XssCleaner { - - private final Safelist safelist; - - /** - * 用于在 src 属性使用相对路径时,强制转换为绝对路径。 为空时不处理,值应为绝对路径的前缀(包含协议部分) - */ - private final String baseUri; - - /** - * 无参构造,默认使用 {@link JsoupXssCleaner#buildSafelist} 方法构建一个安全列表 - */ - public JsoupXssCleaner() { - this.safelist = buildSafelist(); - this.baseUri = ""; - } - - /** - * 构建一个 Xss 清理的 Safelist 规则。 - * 基于 Safelist#relaxed() 的基础上: - * 1. 扩展支持了 style 和 class 属性 - * 2. a 标签额外支持了 target 属性 - * 3. img 标签额外支持了 data 协议,便于支持 base64 - * - * @return Safelist - */ - private Safelist buildSafelist() { - // 使用 jsoup 提供的默认的 - Safelist relaxedSafelist = Safelist.relaxed(); - // 富文本编辑时一些样式是使用 style 来进行实现的 - // 比如红色字体 style="color:red;", 所以需要给所有标签添加 style 属性 - // 注意:style 属性会有注入风险 <img STYLE="background-image:url(javascript:alert('XSS'))"> - relaxedSafelist.addAttributes(":all", "style", "class"); - // 保留 a 标签的 target 属性 - relaxedSafelist.addAttributes("a", "target"); - // 支持img 为base64 - relaxedSafelist.addProtocols("img", "src", "data"); - - // 保留相对路径, 保留相对路径时,必须提供对应的 baseUri 属性,否则依然会被删除 - // WHITELIST.preserveRelativeLinks(false); - - // 移除 a 标签和 img 标签的一些协议限制,这会导致 xss 防注入失效,如 <img src=javascript:alert("xss")> - // 虽然可以重写 WhiteList#isSafeAttribute 来处理,但是有隐患,所以暂时不支持相对路径 - // WHITELIST.removeProtocols("a", "href", "ftp", "http", "https", "mailto"); - // WHITELIST.removeProtocols("img", "src", "http", "https"); - return relaxedSafelist; - } - - @Override - public String clean(String html) { - return Jsoup.clean(html, baseUri, safelist, new Document.OutputSettings().prettyPrint(false)); - } - -} - diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/clean/XssCleaner.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/clean/XssCleaner.java deleted file mode 100644 index d15b77e..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/clean/XssCleaner.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.iailab.framework.xss.core.clean; - -/** - * 对 html 文本中的有 Xss 风险的数据进行清理 - */ -public interface XssCleaner { - - /** - * 清理有 Xss 风险的文本 - * - * @param html 原 html - * @return 清理后的 html - */ - String clean(String html); - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/filter/XssFilter.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/filter/XssFilter.java deleted file mode 100644 index 59c9347..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/filter/XssFilter.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.iailab.framework.xss.core.filter; - -import com.iailab.framework.xss.config.XssProperties; -import com.iailab.framework.xss.core.clean.XssCleaner; -import lombok.AllArgsConstructor; -import org.springframework.util.PathMatcher; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * Xss 过滤器 - * - * @author iailab - */ -@AllArgsConstructor -public class XssFilter extends OncePerRequestFilter { - - /** - * 属性 - */ - private final XssProperties properties; - /** - * 路径匹配器 - */ - private final PathMatcher pathMatcher; - - private final XssCleaner xssCleaner; - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws IOException, ServletException { - filterChain.doFilter(new XssRequestWrapper(request, xssCleaner), response); - } - - @Override - protected boolean shouldNotFilter(HttpServletRequest request) { - // 如果关闭,则不过滤 - if (!properties.isEnable()) { - return true; - } - - // 如果匹配到无需过滤,则不过滤 - String uri = request.getRequestURI(); - return properties.getExcludeUrls().stream().anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, uri)); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/filter/XssRequestWrapper.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/filter/XssRequestWrapper.java deleted file mode 100644 index 04acb35..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/filter/XssRequestWrapper.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.iailab.framework.xss.core.filter; - -import com.iailab.framework.xss.core.clean.XssCleaner; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Xss 请求 Wrapper - * - * @author iailab - */ -public class XssRequestWrapper extends HttpServletRequestWrapper { - - private final XssCleaner xssCleaner; - - public XssRequestWrapper(HttpServletRequest request, XssCleaner xssCleaner) { - super(request); - this.xssCleaner = xssCleaner; - } - - // ============================ parameter ============================ - @Override - public Map<String, String[]> getParameterMap() { - Map<String, String[]> map = new LinkedHashMap<>(); - Map<String, String[]> parameters = super.getParameterMap(); - for (Map.Entry<String, String[]> entry : parameters.entrySet()) { - String[] values = entry.getValue(); - for (int i = 0; i < values.length; i++) { - values[i] = xssCleaner.clean(values[i]); - } - map.put(entry.getKey(), values); - } - return map; - } - - @Override - public String[] getParameterValues(String name) { - String[] values = super.getParameterValues(name); - if (values == null) { - return null; - } - int count = values.length; - String[] encodedValues = new String[count]; - for (int i = 0; i < count; i++) { - encodedValues[i] = xssCleaner.clean(values[i]); - } - return encodedValues; - } - - @Override - public String getParameter(String name) { - String value = super.getParameter(name); - if (value == null) { - return null; - } - return xssCleaner.clean(value); - } - - // ============================ attribute ============================ - @Override - public Object getAttribute(String name) { - Object value = super.getAttribute(name); - if (value instanceof String) { - return xssCleaner.clean((String) value); - } - return value; - } - - // ============================ header ============================ - @Override - public String getHeader(String name) { - String value = super.getHeader(name); - if (value == null) { - return null; - } - return xssCleaner.clean(value); - } - - // ============================ queryString ============================ - @Override - public String getQueryString() { - String value = super.getQueryString(); - if (value == null) { - return null; - } - return xssCleaner.clean(value); - } - -} diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/json/XssStringJsonDeserializer.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/json/XssStringJsonDeserializer.java deleted file mode 100644 index 83f6f94..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/core/json/XssStringJsonDeserializer.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.iailab.framework.xss.core.json; - -import com.iailab.framework.common.util.servlet.ServletUtils; -import com.iailab.framework.xss.config.XssProperties; -import com.iailab.framework.xss.core.clean.XssCleaner; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.deser.std.StringDeserializer; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.PathMatcher; - -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; - -/** - * XSS 过滤 jackson 反序列化器。 - * 在反序列化的过程中,会对字符串进行 XSS 过滤。 - * - * @author Hccake - */ -@Slf4j -@AllArgsConstructor -public class XssStringJsonDeserializer extends StringDeserializer { - - /** - * 属性 - */ - private final XssProperties properties; - /** - * 路径匹配器 - */ - private final PathMatcher pathMatcher; - - private final XssCleaner xssCleaner; - - @Override - public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - // 1. 白名单 URL 的处理 - HttpServletRequest request = ServletUtils.getRequest(); - if (request != null) { - String uri = ServletUtils.getRequest().getRequestURI(); - if (properties.getExcludeUrls().stream().anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, uri))) { - return p.getText(); - } - } - - // 2. 真正使用 xssCleaner 进行过滤 - if (p.hasToken(JsonToken.VALUE_STRING)) { - return xssCleaner.clean(p.getText()); - } - JsonToken t = p.currentToken(); - // [databind#381] - if (t == JsonToken.START_ARRAY) { - return _deserializeFromArray(p, ctxt); - } - // need to gracefully handle byte[] data, as base64 - if (t == JsonToken.VALUE_EMBEDDED_OBJECT) { - Object ob = p.getEmbeddedObject(); - if (ob == null) { - return null; - } - if (ob instanceof byte[]) { - return ctxt.getBase64Variant().encode((byte[]) ob, false); - } - // otherwise, try conversion using toString()... - return ob.toString(); - } - // 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML) - if (t == JsonToken.START_OBJECT) { - return ctxt.extractScalarFromObject(p, this, _valueClass); - } - - if (t.isScalarValue()) { - String text = p.getValueAsString(); - return xssCleaner.clean(text); - } - return (String) ctxt.handleUnexpectedToken(_valueClass, p); - } -} - diff --git a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/package-info.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/package-info.java deleted file mode 100644 index 8e6ce67..0000000 --- a/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/xss/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 针对 XSS 的基础封装 - * - * XSS 说明:https://tech.meituan.com/2018/09/27/fe-security.html - */ -package com.iailab.framework.xss; diff --git a/iailab-framework/iailab-common-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/iailab-framework/iailab-common-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index e7ad084..0000000 --- a/iailab-framework/iailab-common-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,6 +0,0 @@ -com.iailab.framework.apilog.config.IailabApiLogAutoConfiguration -com.iailab.framework.jackson.config.IailabJacksonAutoConfiguration -com.iailab.framework.swagger.config.IailabSwaggerAutoConfiguration -com.iailab.framework.web.config.IailabWebAutoConfiguration -com.iailab.framework.apilog.config.IailabApiLogRpcAutoConfiguration -com.iailab.framework.banner.config.IailabBannerAutoConfiguration \ No newline at end of file diff --git a/iailab-framework/iailab-common-web/src/main/resources/banner.txt b/iailab-framework/iailab-common-web/src/main/resources/banner.txt deleted file mode 100644 index 65b7f42..0000000 --- a/iailab-framework/iailab-common-web/src/main/resources/banner.txt +++ /dev/null @@ -1,12 +0,0 @@ -iailab -Application Version: ${iailab.info.version} -Spring Boot Version: ${spring-boot.version} - - ██ ██ ██ ██ ██ ██ ████ -░░ ░░ ░██ ░██ ██████ ░██ ░██ ░██░ - ██ ██████ ██ ░██ ██████ ░██ ░██░░░██ ░██ ██████ ██████ ██████ ██████ ██████ ██████████ -░██ ░░░░░░██ ░██ ░██ ░░░░░░██ ░██████ ░██ ░██ ░██ ░░░░░░██ ░░░██░ ░░░██░ ██░░░░██░░██░░█░░██░░██░░██ -░██ ███████ ░██ ░██ ███████ ░██░░░██ ░██████ ░██ ███████ ░██ ░██ ░██ ░██ ░██ ░ ░██ ░██ ░██ -░██ ██░░░░██ ░██ ░██ ██░░░░██ ░██ ░██ ░██░░░ ░██ ██░░░░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ -░██░░████████░██ ███░░████████░██████ ░██ ███░░████████ ░░██ ░██ ░░██████ ░███ ███ ░██ ░██ -░░ ░░░░░░░░ ░░ ░░░ ░░░░░░░░ ░░░░░ ░░ ░░░ ░░░░░░░░ ░░ ░░ ░░░░░░ ░░░ ░░░ ░░ ░░ diff --git a/iailab-framework/iailab-common-web/src/test/java/com/iailab/framework/desensitize/core/DesensitizeTest.java b/iailab-framework/iailab-common-web/src/test/java/com/iailab/framework/desensitize/core/DesensitizeTest.java deleted file mode 100644 index bb7a62a..0000000 --- a/iailab-framework/iailab-common-web/src/test/java/com/iailab/framework/desensitize/core/DesensitizeTest.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.iailab.framework.desensitize.core; - -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.framework.desensitize.core.regex.annotation.EmailDesensitize; -import com.iailab.framework.desensitize.core.regex.annotation.RegexDesensitize; -import com.iailab.framework.desensitize.core.annotation.Address; -import com.iailab.framework.desensitize.core.slider.annotation.BankCardDesensitize; -import com.iailab.framework.desensitize.core.slider.annotation.CarLicenseDesensitize; -import com.iailab.framework.desensitize.core.slider.annotation.ChineseNameDesensitize; -import com.iailab.framework.desensitize.core.slider.annotation.FixedPhoneDesensitize; -import com.iailab.framework.desensitize.core.slider.annotation.IdCardDesensitize; -import com.iailab.framework.desensitize.core.slider.annotation.PasswordDesensitize; -import com.iailab.framework.desensitize.core.slider.annotation.MobileDesensitize; -import com.iailab.framework.desensitize.core.slider.annotation.SliderDesensitize; -import lombok.Data; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link DesensitizeTest} 的单元测试 - */ -@ExtendWith(MockitoExtension.class) -public class DesensitizeTest { - - @Test - public void test() { - // 准备参数 - DesensitizeDemo desensitizeDemo = new DesensitizeDemo(); - desensitizeDemo.setNickname("iailab"); - desensitizeDemo.setBankCard("9988002866797031"); - desensitizeDemo.setCarLicense("粤A66666"); - desensitizeDemo.setFixedPhone("01086551122"); - desensitizeDemo.setIdCard("530321199204074611"); - desensitizeDemo.setPassword("123456"); - desensitizeDemo.setPhoneNumber("13248765917"); - desensitizeDemo.setSlider1("ABCDEFG"); - desensitizeDemo.setSlider2("ABCDEFG"); - desensitizeDemo.setSlider3("ABCDEFG"); - desensitizeDemo.setEmail("1@email.com"); - desensitizeDemo.setRegex("你好,我是iailab"); - desensitizeDemo.setAddress("北京市海淀区上地十街10号"); - desensitizeDemo.setOrigin("iailab"); - - // 调用 - DesensitizeDemo d = JsonUtils.parseObject(JsonUtils.toJsonString(desensitizeDemo), DesensitizeDemo.class); - // 断言 - assertNotNull(d); - assertEquals("芋***", d.getNickname()); - assertEquals("998800********31", d.getBankCard()); - assertEquals("粤A6***6", d.getCarLicense()); - assertEquals("0108*****22", d.getFixedPhone()); - assertEquals("530321**********11", d.getIdCard()); - assertEquals("******", d.getPassword()); - assertEquals("132****5917", d.getPhoneNumber()); - assertEquals("#######", d.getSlider1()); - assertEquals("ABC*EFG", d.getSlider2()); - assertEquals("*******", d.getSlider3()); - assertEquals("1****@email.com", d.getEmail()); - assertEquals("你好,我是*", d.getRegex()); - assertEquals("北京市海淀区上地十街10号*", d.getAddress()); - assertEquals("iailab", d.getOrigin()); - } - - @Data - public static class DesensitizeDemo { - - @ChineseNameDesensitize - private String nickname; - @BankCardDesensitize - private String bankCard; - @CarLicenseDesensitize - private String carLicense; - @FixedPhoneDesensitize - private String fixedPhone; - @IdCardDesensitize - private String idCard; - @PasswordDesensitize - private String password; - @MobileDesensitize - private String phoneNumber; - @SliderDesensitize(prefixKeep = 6, suffixKeep = 1, replacer = "#") - private String slider1; - @SliderDesensitize(prefixKeep = 3, suffixKeep = 3) - private String slider2; - @SliderDesensitize(prefixKeep = 10) - private String slider3; - @EmailDesensitize - private String email; - @RegexDesensitize(regex = "iailab", replacer = "*") - private String regex; - @Address - private String address; - private String origin; - - } - -} diff --git a/iailab-framework/iailab-common-web/src/test/java/com/iailab/framework/desensitize/core/annotation/Address.java b/iailab-framework/iailab-common-web/src/test/java/com/iailab/framework/desensitize/core/annotation/Address.java deleted file mode 100644 index 01826e4..0000000 --- a/iailab-framework/iailab-common-web/src/test/java/com/iailab/framework/desensitize/core/annotation/Address.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.iailab.framework.desensitize.core.annotation; - -import com.iailab.framework.desensitize.core.DesensitizeTest; -import com.iailab.framework.desensitize.core.base.annotation.DesensitizeBy; -import com.iailab.framework.desensitize.core.handler.AddressHandler; -import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 地址 - * - * 用于 {@link DesensitizeTest} 测试使用 - * - * @author gaibu - */ -@Documented -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@JacksonAnnotationsInside -@DesensitizeBy(handler = AddressHandler.class) -public @interface Address { - - String replacer() default "*"; - -} diff --git a/iailab-framework/iailab-common-web/src/test/java/com/iailab/framework/desensitize/core/handler/AddressHandler.java b/iailab-framework/iailab-common-web/src/test/java/com/iailab/framework/desensitize/core/handler/AddressHandler.java deleted file mode 100644 index 6172c24..0000000 --- a/iailab-framework/iailab-common-web/src/test/java/com/iailab/framework/desensitize/core/handler/AddressHandler.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.iailab.framework.desensitize.core.handler; - -import com.iailab.framework.desensitize.core.DesensitizeTest; -import com.iailab.framework.desensitize.core.base.handler.DesensitizationHandler; -import com.iailab.framework.desensitize.core.annotation.Address; - -/** - * {@link Address} 的脱敏处理器 - * - * 用于 {@link DesensitizeTest} 测试使用 - */ -public class AddressHandler implements DesensitizationHandler<Address> { - - @Override - public String desensitize(String origin, Address annotation) { - return origin + annotation.replacer(); - } - -} diff --git a/iailab-framework/iailab-common-websocket/pom.xml b/iailab-framework/iailab-common-websocket/pom.xml deleted file mode 100644 index a566d80..0000000 --- a/iailab-framework/iailab-common-websocket/pom.xml +++ /dev/null @@ -1,73 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>com.iailab</groupId> - <artifactId>iailab-framework</artifactId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common-websocket</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description>WebSocket 框架,支持多节点的广播</description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - - <dependencies> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common</artifactId> - </dependency> - - <!-- Web 相关 --> - <dependency> - <!-- 为什么是 websocket 依赖 security 呢?而不是 security 拓展 websocket 呢? - 因为 websocket 和 LoginUser 当前登录的用户有一定的相关性,具体可见 WebSocketSessionManagerImpl 逻辑。 - 如果让 security 拓展 websocket 的话,会导致 websocket 组件的封装很散,进而增大理解成本。 - --> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-security</artifactId> - <scope>provided</scope> - </dependency> - - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-websocket</artifactId> - </dependency> - - <!-- 消息队列相关 --> - <dependency> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-mq</artifactId> - </dependency> -<!-- <dependency>--> -<!-- <groupId>org.springframework.kafka</groupId>--> -<!-- <artifactId>spring-kafka</artifactId>--> -<!-- <optional>true</optional>--> -<!-- </dependency>--> -<!-- <dependency>--> -<!-- <groupId>org.springframework.amqp</groupId>--> -<!-- <artifactId>spring-rabbit</artifactId>--> -<!-- <optional>true</optional>--> -<!-- </dependency>--> -<!-- <dependency>--> -<!-- <groupId>org.apache.rocketmq</groupId>--> -<!-- <artifactId>rocketmq-spring-boot-starter</artifactId>--> -<!-- <optional>true</optional>--> -<!-- </dependency>--> - - <!-- 业务组件 --> - <dependency> - <!-- 为什么要依赖 tenant 组件? - 因为广播某个类型的用户时候,需要根据租户过滤下,避免广播到别的租户! - --> - <groupId>com.iailab</groupId> - <artifactId>iailab-common-biz-tenant</artifactId> - <scope>provided</scope> - </dependency> - </dependencies> - -</project> \ No newline at end of file diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/config/IailabWebSocketAutoConfiguration.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/config/IailabWebSocketAutoConfiguration.java deleted file mode 100644 index 459a5b9..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/config/IailabWebSocketAutoConfiguration.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.iailab.framework.websocket.config; - -import com.iailab.framework.mq.redis.config.IailabRedisMQConsumerAutoConfiguration; -import com.iailab.framework.mq.redis.core.RedisMQTemplate; -import com.iailab.framework.websocket.core.handler.JsonWebSocketMessageHandler; -import com.iailab.framework.websocket.core.listener.WebSocketMessageListener; -import com.iailab.framework.websocket.core.security.LoginUserHandshakeInterceptor; -import com.iailab.framework.websocket.core.sender.kafka.KafkaWebSocketMessageConsumer; -import com.iailab.framework.websocket.core.sender.kafka.KafkaWebSocketMessageSender; -import com.iailab.framework.websocket.core.sender.local.LocalWebSocketMessageSender; -import com.iailab.framework.websocket.core.sender.rabbitmq.RabbitMQWebSocketMessageConsumer; -import com.iailab.framework.websocket.core.sender.rabbitmq.RabbitMQWebSocketMessageSender; -import com.iailab.framework.websocket.core.sender.redis.RedisWebSocketMessageConsumer; -import com.iailab.framework.websocket.core.sender.redis.RedisWebSocketMessageSender; -import com.iailab.framework.websocket.core.sender.rocketmq.RocketMQWebSocketMessageConsumer; -import com.iailab.framework.websocket.core.sender.rocketmq.RocketMQWebSocketMessageSender; -import com.iailab.framework.websocket.core.session.WebSocketSessionHandlerDecorator; -import com.iailab.framework.websocket.core.session.WebSocketSessionManager; -import com.iailab.framework.websocket.core.session.WebSocketSessionManagerImpl; -import org.apache.rocketmq.spring.core.RocketMQTemplate; -import org.springframework.amqp.core.TopicExchange; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.AutoConfiguration; -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.Configuration; -import org.springframework.kafka.core.KafkaTemplate; -import org.springframework.web.socket.WebSocketHandler; -import org.springframework.web.socket.config.annotation.EnableWebSocket; -import org.springframework.web.socket.config.annotation.WebSocketConfigurer; -import org.springframework.web.socket.server.HandshakeInterceptor; - -import java.util.List; - -/** - * WebSocket 自动配置 - * - * @author xingyu4j - */ -@AutoConfiguration(before = IailabRedisMQConsumerAutoConfiguration.class) // before IailabRedisMQConsumerAutoConfiguration 的原因是,需要保证 RedisWebSocketMessageConsumer 先创建,才能创建 RedisMessageListenerContainer -@EnableWebSocket // 开启 websocket -@ConditionalOnProperty(prefix = "iailab.websocket", value = "enable", matchIfMissing = true) // 允许使用 iailab.websocket.enable=false 禁用 websocket -@EnableConfigurationProperties(WebSocketProperties.class) -public class IailabWebSocketAutoConfiguration { - - @Bean - public WebSocketConfigurer webSocketConfigurer(HandshakeInterceptor[] handshakeInterceptors, - WebSocketHandler webSocketHandler, - WebSocketProperties webSocketProperties) { - return registry -> registry - // 添加 WebSocketHandler - .addHandler(webSocketHandler, webSocketProperties.getPath()) - .addInterceptors(handshakeInterceptors) - // 允许跨域,否则前端连接会直接断开 - .setAllowedOriginPatterns("*"); - } - - @Bean - public HandshakeInterceptor handshakeInterceptor() { - return new LoginUserHandshakeInterceptor(); - } - - @Bean - public WebSocketHandler webSocketHandler(WebSocketSessionManager sessionManager, - List<? extends WebSocketMessageListener<?>> messageListeners) { - // 1. 创建 JsonWebSocketMessageHandler 对象,处理消息 - JsonWebSocketMessageHandler messageHandler = new JsonWebSocketMessageHandler(messageListeners); - // 2. 创建 WebSocketSessionHandlerDecorator 对象,处理连接 - return new WebSocketSessionHandlerDecorator(messageHandler, sessionManager); - } - - @Bean - public WebSocketSessionManager webSocketSessionManager() { - return new WebSocketSessionManagerImpl(); - } - - // ==================== Sender 相关 ==================== - - @Configuration - @ConditionalOnProperty(prefix = "iailab.websocket", name = "sender-type", havingValue = "local", matchIfMissing = true) - public class LocalWebSocketMessageSenderConfiguration { - - @Bean - public LocalWebSocketMessageSender localWebSocketMessageSender(WebSocketSessionManager sessionManager) { - return new LocalWebSocketMessageSender(sessionManager); - } - - } - - @Configuration - @ConditionalOnProperty(prefix = "iailab.websocket", name = "sender-type", havingValue = "redis", matchIfMissing = true) - public class RedisWebSocketMessageSenderConfiguration { - - @Bean - public RedisWebSocketMessageSender redisWebSocketMessageSender(WebSocketSessionManager sessionManager, - RedisMQTemplate redisMQTemplate) { - return new RedisWebSocketMessageSender(sessionManager, redisMQTemplate); - } - - @Bean - public RedisWebSocketMessageConsumer redisWebSocketMessageConsumer( - RedisWebSocketMessageSender redisWebSocketMessageSender) { - return new RedisWebSocketMessageConsumer(redisWebSocketMessageSender); - } - - } - - @Configuration - @ConditionalOnProperty(prefix = "iailab.websocket", name = "sender-type", havingValue = "rocketmq", matchIfMissing = true) - public class RocketMQWebSocketMessageSenderConfiguration { - - @Bean - public RocketMQWebSocketMessageSender rocketMQWebSocketMessageSender( - WebSocketSessionManager sessionManager, RocketMQTemplate rocketMQTemplate, - @Value("${iailab.websocket.sender-rocketmq.topic}") String topic) { - return new RocketMQWebSocketMessageSender(sessionManager, rocketMQTemplate, topic); - } - - @Bean - public RocketMQWebSocketMessageConsumer rocketMQWebSocketMessageConsumer( - RocketMQWebSocketMessageSender rocketMQWebSocketMessageSender) { - return new RocketMQWebSocketMessageConsumer(rocketMQWebSocketMessageSender); - } - - } - - @Configuration - @ConditionalOnProperty(prefix = "iailab.websocket", name = "sender-type", havingValue = "rabbitmq", matchIfMissing = true) - public class RabbitMQWebSocketMessageSenderConfiguration { - - @Bean - public RabbitMQWebSocketMessageSender rabbitMQWebSocketMessageSender( - WebSocketSessionManager sessionManager, RabbitTemplate rabbitTemplate, - TopicExchange websocketTopicExchange) { - return new RabbitMQWebSocketMessageSender(sessionManager, rabbitTemplate, websocketTopicExchange); - } - - @Bean - public RabbitMQWebSocketMessageConsumer rabbitMQWebSocketMessageConsumer( - RabbitMQWebSocketMessageSender rabbitMQWebSocketMessageSender) { - return new RabbitMQWebSocketMessageConsumer(rabbitMQWebSocketMessageSender); - } - - /** - * 创建 Topic Exchange - */ - @Bean - public TopicExchange websocketTopicExchange(@Value("${iailab.websocket.sender-rabbitmq.exchange}") String exchange) { - return new TopicExchange(exchange, - true, // durable: 是否持久化 - false); // exclusive: 是否排它 - } - - } - - @Configuration - @ConditionalOnProperty(prefix = "iailab.websocket", name = "sender-type", havingValue = "kafka", matchIfMissing = true) - public class KafkaWebSocketMessageSenderConfiguration { - - @Bean - public KafkaWebSocketMessageSender kafkaWebSocketMessageSender( - WebSocketSessionManager sessionManager, KafkaTemplate<Object, Object> kafkaTemplate, - @Value("${iailab.websocket.sender-kafka.topic}") String topic) { - return new KafkaWebSocketMessageSender(sessionManager, kafkaTemplate, topic); - } - - @Bean - public KafkaWebSocketMessageConsumer kafkaWebSocketMessageConsumer( - KafkaWebSocketMessageSender kafkaWebSocketMessageSender) { - return new KafkaWebSocketMessageConsumer(kafkaWebSocketMessageSender); - } - - } - -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/config/WebSocketProperties.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/config/WebSocketProperties.java deleted file mode 100644 index 75e10ca..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/config/WebSocketProperties.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.iailab.framework.websocket.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.validation.annotation.Validated; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -/** - * WebSocket 配置项 - * - * @author xingyu4j - */ -@ConfigurationProperties("iailab.websocket") -@Data -@Validated -public class WebSocketProperties { - - /** - * WebSocket 的连接路径 - */ - @NotEmpty(message = "WebSocket 的连接路径不能为空") - private String path = "/ws"; - - /** - * 消息发送器的类型 - * - * 可选值:local、redis、rocketmq、kafka、rabbitmq - */ - @NotNull(message = "WebSocket 的消息发送者不能为空") - private String senderType = "local"; - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/handler/JsonWebSocketMessageHandler.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/handler/JsonWebSocketMessageHandler.java deleted file mode 100644 index 699e282..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/handler/JsonWebSocketMessageHandler.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.iailab.framework.websocket.core.handler; - -import cn.hutool.core.util.StrUtil; -import cn.hutool.core.util.TypeUtil; -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.framework.tenant.core.util.TenantUtils; -import com.iailab.framework.websocket.core.listener.WebSocketMessageListener; -import com.iailab.framework.websocket.core.message.JsonWebSocketMessage; -import com.iailab.framework.websocket.core.util.WebSocketFrameworkUtils; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.socket.TextMessage; -import org.springframework.web.socket.WebSocketHandler; -import org.springframework.web.socket.WebSocketSession; -import org.springframework.web.socket.handler.TextWebSocketHandler; - -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.Consumer; - -/** - * JSON 格式 {@link WebSocketHandler} 实现类 - * - * 基于 {@link JsonWebSocketMessage#getType()} 消息类型,调度到对应的 {@link WebSocketMessageListener} 监听器。 - * - * @author iailab - */ -@Slf4j -public class JsonWebSocketMessageHandler extends TextWebSocketHandler { - - /** - * type 与 WebSocketMessageListener 的映射 - */ - private final Map<String, WebSocketMessageListener<Object>> listeners = new HashMap<>(); - - @SuppressWarnings({"rawtypes", "unchecked"}) - public JsonWebSocketMessageHandler(List<? extends WebSocketMessageListener> listenersList) { - listenersList.forEach((Consumer<WebSocketMessageListener>) - listener -> listeners.put(listener.getType(), listener)); - } - - @Override - protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { - // 1.1 空消息,跳过 - if (message.getPayloadLength() == 0) { - return; - } - // 1.2 ping 心跳消息,直接返回 pong 消息。 - if (message.getPayloadLength() == 4 && Objects.equals(message.getPayload(), "ping")) { - session.sendMessage(new TextMessage("pong")); - return; - } - - // 2.1 解析消息 - try { - JsonWebSocketMessage jsonMessage = JsonUtils.parseObject(message.getPayload(), JsonWebSocketMessage.class); - if (jsonMessage == null) { - log.error("[handleTextMessage][session({}) message({}) 解析为空]", session.getId(), message.getPayload()); - return; - } - if (StrUtil.isEmpty(jsonMessage.getType())) { - log.error("[handleTextMessage][session({}) message({}) 类型为空]", session.getId(), message.getPayload()); - return; - } - // 2.2 获得对应的 WebSocketMessageListener - WebSocketMessageListener<Object> messageListener = listeners.get(jsonMessage.getType()); - if (messageListener == null) { - log.error("[handleTextMessage][session({}) message({}) 监听器为空]", session.getId(), message.getPayload()); - return; - } - // 2.3 处理消息 - Type type = TypeUtil.getTypeArgument(messageListener.getClass(), 0); - Object messageObj = JsonUtils.parseObject(jsonMessage.getContent(), type); - Long tenantId = WebSocketFrameworkUtils.getTenantId(session); - TenantUtils.execute(tenantId, () -> messageListener.onMessage(session, messageObj)); - } catch (Throwable ex) { - log.error("[handleTextMessage][session({}) message({}) 处理异常]", session.getId(), message.getPayload()); - } - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/listener/WebSocketMessageListener.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/listener/WebSocketMessageListener.java deleted file mode 100644 index 9b55317..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/listener/WebSocketMessageListener.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.iailab.framework.websocket.core.listener; - -import com.iailab.framework.websocket.core.message.JsonWebSocketMessage; -import org.springframework.web.socket.WebSocketSession; - -/** - * WebSocket 消息监听器接口 - * - * 目的:前端发送消息给后端后,处理对应 {@link #getType()} 类型的消息 - * - * @param <T> 泛型,消息类型 - */ -public interface WebSocketMessageListener<T> { - - /** - * 处理消息 - * - * @param session Session - * @param message 消息 - */ - void onMessage(WebSocketSession session, T message); - - /** - * 获得消息类型 - * - * @see JsonWebSocketMessage#getType() - * @return 消息类型 - */ - String getType(); - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/message/JsonWebSocketMessage.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/message/JsonWebSocketMessage.java deleted file mode 100644 index e9c598f..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/message/JsonWebSocketMessage.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.iailab.framework.websocket.core.message; - -import com.iailab.framework.websocket.core.listener.WebSocketMessageListener; -import lombok.Data; - -import java.io.Serializable; - -/** - * JSON 格式的 WebSocket 消息帧 - * - * @author iailab - */ -@Data -public class JsonWebSocketMessage implements Serializable { - - /** - * 消息类型 - * - * 目的:用于分发到对应的 {@link WebSocketMessageListener} 实现类 - */ - private String type; - /** - * 消息内容 - * - * 要求 JSON 对象 - */ - private String content; - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/security/LoginUserHandshakeInterceptor.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/security/LoginUserHandshakeInterceptor.java deleted file mode 100644 index d1092f5..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/security/LoginUserHandshakeInterceptor.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.iailab.framework.websocket.core.security; - -import com.iailab.framework.security.core.LoginUser; -import com.iailab.framework.security.core.filter.TokenAuthenticationFilter; -import com.iailab.framework.security.core.util.SecurityFrameworkUtils; -import com.iailab.framework.websocket.core.util.WebSocketFrameworkUtils; -import org.springframework.http.server.ServerHttpRequest; -import org.springframework.http.server.ServerHttpResponse; -import org.springframework.web.socket.WebSocketHandler; -import org.springframework.web.socket.WebSocketSession; -import org.springframework.web.socket.server.HandshakeInterceptor; - -import java.util.Map; - -/** - * 登录用户的 {@link HandshakeInterceptor} 实现类 - * - * 流程如下: - * 1. 前端连接 websocket 时,会通过拼接 ?token={token} 到 ws:// 连接后,这样它可以被 {@link TokenAuthenticationFilter} 所认证通过 - * 2. {@link LoginUserHandshakeInterceptor} 负责把 {@link LoginUser} 添加到 {@link WebSocketSession} 中 - * - * @author iailab - */ -public class LoginUserHandshakeInterceptor implements HandshakeInterceptor { - - @Override - public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, - WebSocketHandler wsHandler, Map<String, Object> attributes) { - LoginUser loginUser = SecurityFrameworkUtils.getLoginUser(); - if (loginUser != null) { - WebSocketFrameworkUtils.setLoginUser(loginUser, attributes); - } - return true; - } - - @Override - public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, - WebSocketHandler wsHandler, Exception exception) { - // do nothing - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/security/WebSocketAuthorizeRequestsCustomizer.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/security/WebSocketAuthorizeRequestsCustomizer.java deleted file mode 100644 index bd760f5..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/security/WebSocketAuthorizeRequestsCustomizer.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.iailab.framework.websocket.core.security; - -import com.iailab.framework.security.config.AuthorizeRequestsCustomizer; -import com.iailab.framework.websocket.config.WebSocketProperties; -import lombok.RequiredArgsConstructor; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; - -/** - * WebSocket 的权限自定义 - * - * @author iailab - */ -@RequiredArgsConstructor -public class WebSocketAuthorizeRequestsCustomizer extends AuthorizeRequestsCustomizer { - - private final WebSocketProperties webSocketProperties; - - @Override - public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) { - registry.requestMatchers(webSocketProperties.getPath()).permitAll(); - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/AbstractWebSocketMessageSender.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/AbstractWebSocketMessageSender.java deleted file mode 100644 index ce676f0..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/AbstractWebSocketMessageSender.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.iailab.framework.websocket.core.sender; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.framework.websocket.core.message.JsonWebSocketMessage; -import com.iailab.framework.websocket.core.session.WebSocketSessionManager; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.socket.TextMessage; -import org.springframework.web.socket.WebSocketSession; - -import java.io.IOException; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * WebSocketMessageSender 实现类 - * - * @author iailab - */ -@Slf4j -@RequiredArgsConstructor -public abstract class AbstractWebSocketMessageSender implements WebSocketMessageSender { - - private final WebSocketSessionManager sessionManager; - - @Override - public void send(Integer userType, Long userId, String messageType, String messageContent) { - send(null, userType, userId, messageType, messageContent); - } - - @Override - public void send(Integer userType, String messageType, String messageContent) { - send(null, userType, null, messageType, messageContent); - } - - @Override - public void send(String sessionId, String messageType, String messageContent) { - send(sessionId, null, null, messageType, messageContent); - } - - /** - * 发送消息 - * - * @param sessionId Session 编号 - * @param userType 用户类型 - * @param userId 用户编号 - * @param messageType 消息类型 - * @param messageContent 消息内容 - */ - public void send(String sessionId, Integer userType, Long userId, String messageType, String messageContent) { - // 1. 获得 Session 列表 - List<WebSocketSession> sessions = Collections.emptyList(); - if (StrUtil.isNotEmpty(sessionId)) { - WebSocketSession session = sessionManager.getSession(sessionId); - if (session != null) { - sessions = Collections.singletonList(session); - } - } else if (userType != null && userId != null) { - sessions = (List<WebSocketSession>) sessionManager.getSessionList(userType, userId); - } else if (userType != null) { - sessions = (List<WebSocketSession>) sessionManager.getSessionList(userType); - } - if (CollUtil.isEmpty(sessions)) { - log.info("[send][sessionId({}) userType({}) userId({}) messageType({}) messageContent({}) 未匹配到会话]", - sessionId, userType, userId, messageType, messageContent); - } - // 2. 执行发送 - doSend(sessions, messageType, messageContent); - } - - /** - * 发送消息的具体实现 - * - * @param sessions Session 列表 - * @param messageType 消息类型 - * @param messageContent 消息内容 - */ - public void doSend(Collection<WebSocketSession> sessions, String messageType, String messageContent) { - JsonWebSocketMessage message = new JsonWebSocketMessage().setType(messageType).setContent(messageContent); - String payload = JsonUtils.toJsonString(message); // 关键,使用 JSON 序列化 - sessions.forEach(session -> { - // 1. 各种校验,保证 Session 可以被发送 - if (session == null) { - log.error("[doSend][session 为空, message({})]", message); - return; - } - if (!session.isOpen()) { - log.error("[doSend][session({}) 已关闭, message({})]", session.getId(), message); - return; - } - // 2. 执行发送 - try { - session.sendMessage(new TextMessage(payload)); - log.info("[doSend][session({}) 发送消息成功,message({})]", session.getId(), message); - } catch (IOException ex) { - log.error("[doSend][session({}) 发送消息失败,message({})]", session.getId(), message, ex); - } - }); - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/WebSocketMessageSender.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/WebSocketMessageSender.java deleted file mode 100644 index df5f111..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/WebSocketMessageSender.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.iailab.framework.websocket.core.sender; - -import com.iailab.framework.common.util.json.JsonUtils; - -/** - * WebSocket 消息的发送器接口 - * - * @author iailab - */ -public interface WebSocketMessageSender { - - /** - * 发送消息给指定用户 - * - * @param userType 用户类型 - * @param userId 用户编号 - * @param messageType 消息类型 - * @param messageContent 消息内容,JSON 格式 - */ - void send(Integer userType, Long userId, String messageType, String messageContent); - - /** - * 发送消息给指定用户类型 - * - * @param userType 用户类型 - * @param messageType 消息类型 - * @param messageContent 消息内容,JSON 格式 - */ - void send(Integer userType, String messageType, String messageContent); - - /** - * 发送消息给指定 Session - * - * @param sessionId Session 编号 - * @param messageType 消息类型 - * @param messageContent 消息内容,JSON 格式 - */ - void send(String sessionId, String messageType, String messageContent); - - default void sendObject(Integer userType, Long userId, String messageType, Object messageContent) { - send(userType, userId, messageType, JsonUtils.toJsonString(messageContent)); - } - - default void sendObject(Integer userType, String messageType, Object messageContent) { - send(userType, messageType, JsonUtils.toJsonString(messageContent)); - } - - default void sendObject(String sessionId, String messageType, Object messageContent) { - send(sessionId, messageType, JsonUtils.toJsonString(messageContent)); - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/kafka/KafkaWebSocketMessage.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/kafka/KafkaWebSocketMessage.java deleted file mode 100644 index a61a6c9..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/kafka/KafkaWebSocketMessage.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.iailab.framework.websocket.core.sender.kafka; - -import lombok.Data; - -/** - * Kafka 广播 WebSocket 的消息 - * - * @author iailab - */ -@Data -public class KafkaWebSocketMessage { - - /** - * Session 编号 - */ - private String sessionId; - /** - * 用户类型 - */ - private Integer userType; - /** - * 用户编号 - */ - private Long userId; - - /** - * 消息类型 - */ - private String messageType; - /** - * 消息内容 - */ - private String messageContent; - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/kafka/KafkaWebSocketMessageConsumer.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/kafka/KafkaWebSocketMessageConsumer.java deleted file mode 100644 index ffad2f8..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/kafka/KafkaWebSocketMessageConsumer.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.framework.websocket.core.sender.kafka; - -import lombok.RequiredArgsConstructor; -import org.springframework.amqp.rabbit.annotation.RabbitHandler; -import org.springframework.kafka.annotation.KafkaListener; - -/** - * {@link KafkaWebSocketMessage} 广播消息的消费者,真正把消息发送出去 - * - * @author iailab - */ -@RequiredArgsConstructor -public class KafkaWebSocketMessageConsumer { - - private final KafkaWebSocketMessageSender rabbitMQWebSocketMessageSender; - - @RabbitHandler - @KafkaListener( - topics = "${iailab.websocket.sender-kafka.topic}", - // 在 Group 上,使用 UUID 生成其后缀。这样,启动的 Consumer 的 Group 不同,以达到广播消费的目的 - groupId = "${iailab.websocket.sender-kafka.consumer-group}" + "-" + "#{T(java.util.UUID).randomUUID()}") - public void onMessage(KafkaWebSocketMessage message) { - rabbitMQWebSocketMessageSender.send(message.getSessionId(), - message.getUserType(), message.getUserId(), - message.getMessageType(), message.getMessageContent()); - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/kafka/KafkaWebSocketMessageSender.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/kafka/KafkaWebSocketMessageSender.java deleted file mode 100644 index 8801a55..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/kafka/KafkaWebSocketMessageSender.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.iailab.framework.websocket.core.sender.kafka; - -import com.iailab.framework.websocket.core.sender.AbstractWebSocketMessageSender; -import com.iailab.framework.websocket.core.sender.WebSocketMessageSender; -import com.iailab.framework.websocket.core.session.WebSocketSessionManager; -import lombok.extern.slf4j.Slf4j; -import org.springframework.kafka.core.KafkaTemplate; - -import java.util.concurrent.ExecutionException; - -/** - * 基于 Kafka 的 {@link WebSocketMessageSender} 实现类 - * - * @author iailab - */ -@Slf4j -public class KafkaWebSocketMessageSender extends AbstractWebSocketMessageSender { - - private final KafkaTemplate<Object, Object> kafkaTemplate; - - private final String topic; - - public KafkaWebSocketMessageSender(WebSocketSessionManager sessionManager, - KafkaTemplate<Object, Object> kafkaTemplate, - String topic) { - super(sessionManager); - this.kafkaTemplate = kafkaTemplate; - this.topic = topic; - } - - @Override - public void send(Integer userType, Long userId, String messageType, String messageContent) { - sendKafkaMessage(null, userId, userType, messageType, messageContent); - } - - @Override - public void send(Integer userType, String messageType, String messageContent) { - sendKafkaMessage(null, null, userType, messageType, messageContent); - } - - @Override - public void send(String sessionId, String messageType, String messageContent) { - sendKafkaMessage(sessionId, null, null, messageType, messageContent); - } - - /** - * 通过 Kafka 广播消息 - * - * @param sessionId Session 编号 - * @param userId 用户编号 - * @param userType 用户类型 - * @param messageType 消息类型 - * @param messageContent 消息内容 - */ - private void sendKafkaMessage(String sessionId, Long userId, Integer userType, - String messageType, String messageContent) { - KafkaWebSocketMessage mqMessage = new KafkaWebSocketMessage() - .setSessionId(sessionId).setUserId(userId).setUserType(userType) - .setMessageType(messageType).setMessageContent(messageContent); - try { - kafkaTemplate.send(topic, mqMessage).get(); - } catch (InterruptedException | ExecutionException e) { - log.error("[sendKafkaMessage][发送消息({}) 到 Kafka 失败]", mqMessage, e); - } - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/local/LocalWebSocketMessageSender.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/local/LocalWebSocketMessageSender.java deleted file mode 100644 index 87eaa3c..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/local/LocalWebSocketMessageSender.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.iailab.framework.websocket.core.sender.local; - -import com.iailab.framework.websocket.core.sender.AbstractWebSocketMessageSender; -import com.iailab.framework.websocket.core.sender.WebSocketMessageSender; -import com.iailab.framework.websocket.core.session.WebSocketSessionManager; - -/** - * 本地的 {@link WebSocketMessageSender} 实现类 - * - * 注意:仅仅适合单机场景!!! - * - * @author iailab - */ -public class LocalWebSocketMessageSender extends AbstractWebSocketMessageSender { - - public LocalWebSocketMessageSender(WebSocketSessionManager sessionManager) { - super(sessionManager); - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rabbitmq/RabbitMQWebSocketMessage.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rabbitmq/RabbitMQWebSocketMessage.java deleted file mode 100644 index 02abb91..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rabbitmq/RabbitMQWebSocketMessage.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.iailab.framework.websocket.core.sender.rabbitmq; - -import lombok.Data; - -import java.io.Serializable; - -/** - * RabbitMQ 广播 WebSocket 的消息 - * - * @author iailab - */ -@Data -public class RabbitMQWebSocketMessage implements Serializable { - - /** - * Session 编号 - */ - private String sessionId; - /** - * 用户类型 - */ - private Integer userType; - /** - * 用户编号 - */ - private Long userId; - - /** - * 消息类型 - */ - private String messageType; - /** - * 消息内容 - */ - private String messageContent; - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rabbitmq/RabbitMQWebSocketMessageConsumer.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rabbitmq/RabbitMQWebSocketMessageConsumer.java deleted file mode 100644 index 9505bfb..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rabbitmq/RabbitMQWebSocketMessageConsumer.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.iailab.framework.websocket.core.sender.rabbitmq; - -import lombok.RequiredArgsConstructor; -import org.springframework.amqp.core.ExchangeTypes; -import org.springframework.amqp.rabbit.annotation.*; - -/** - * {@link RabbitMQWebSocketMessage} 广播消息的消费者,真正把消息发送出去 - * - * @author iailab - */ -@RabbitListener( - bindings = @QueueBinding( - value = @Queue( - // 在 Queue 的名字上,使用 UUID 生成其后缀。这样,启动的 Consumer 的 Queue 不同,以达到广播消费的目的 - name = "${iailab.websocket.sender-rabbitmq.queue}" + "-" + "#{T(java.util.UUID).randomUUID()}", - // Consumer 关闭时,该队列就可以被自动删除了 - autoDelete = "true" - ), - exchange = @Exchange( - name = "${iailab.websocket.sender-rabbitmq.exchange}", - type = ExchangeTypes.TOPIC, - declare = "false" - ) - ) -) -@RequiredArgsConstructor -public class RabbitMQWebSocketMessageConsumer { - - private final RabbitMQWebSocketMessageSender rabbitMQWebSocketMessageSender; - - @RabbitHandler - public void onMessage(RabbitMQWebSocketMessage message) { - rabbitMQWebSocketMessageSender.send(message.getSessionId(), - message.getUserType(), message.getUserId(), - message.getMessageType(), message.getMessageContent()); - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rabbitmq/RabbitMQWebSocketMessageSender.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rabbitmq/RabbitMQWebSocketMessageSender.java deleted file mode 100644 index f3e1409..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rabbitmq/RabbitMQWebSocketMessageSender.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.iailab.framework.websocket.core.sender.rabbitmq; - -import com.iailab.framework.websocket.core.sender.AbstractWebSocketMessageSender; -import com.iailab.framework.websocket.core.sender.WebSocketMessageSender; -import com.iailab.framework.websocket.core.session.WebSocketSessionManager; -import lombok.extern.slf4j.Slf4j; -import org.springframework.amqp.core.TopicExchange; -import org.springframework.amqp.rabbit.core.RabbitTemplate; - -/** - * 基于 RabbitMQ 的 {@link WebSocketMessageSender} 实现类 - * - * @author iailab - */ -@Slf4j -public class RabbitMQWebSocketMessageSender extends AbstractWebSocketMessageSender { - - private final RabbitTemplate rabbitTemplate; - - private final TopicExchange topicExchange; - - public RabbitMQWebSocketMessageSender(WebSocketSessionManager sessionManager, - RabbitTemplate rabbitTemplate, - TopicExchange topicExchange) { - super(sessionManager); - this.rabbitTemplate = rabbitTemplate; - this.topicExchange = topicExchange; - } - - @Override - public void send(Integer userType, Long userId, String messageType, String messageContent) { - sendRabbitMQMessage(null, userId, userType, messageType, messageContent); - } - - @Override - public void send(Integer userType, String messageType, String messageContent) { - sendRabbitMQMessage(null, null, userType, messageType, messageContent); - } - - @Override - public void send(String sessionId, String messageType, String messageContent) { - sendRabbitMQMessage(sessionId, null, null, messageType, messageContent); - } - - /** - * 通过 RabbitMQ 广播消息 - * - * @param sessionId Session 编号 - * @param userId 用户编号 - * @param userType 用户类型 - * @param messageType 消息类型 - * @param messageContent 消息内容 - */ - private void sendRabbitMQMessage(String sessionId, Long userId, Integer userType, - String messageType, String messageContent) { - RabbitMQWebSocketMessage mqMessage = new RabbitMQWebSocketMessage() - .setSessionId(sessionId).setUserId(userId).setUserType(userType) - .setMessageType(messageType).setMessageContent(messageContent); - rabbitTemplate.convertAndSend(topicExchange.getName(), null, mqMessage); - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/redis/RedisWebSocketMessage.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/redis/RedisWebSocketMessage.java deleted file mode 100644 index dddf1ef..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/redis/RedisWebSocketMessage.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.iailab.framework.websocket.core.sender.redis; - -import com.iailab.framework.mq.redis.core.pubsub.AbstractRedisChannelMessage; -import lombok.Data; - -/** - * Redis 广播 WebSocket 的消息 - */ -@Data -public class RedisWebSocketMessage extends AbstractRedisChannelMessage { - - /** - * Session 编号 - */ - private String sessionId; - /** - * 用户类型 - */ - private Integer userType; - /** - * 用户编号 - */ - private Long userId; - - /** - * 消息类型 - */ - private String messageType; - /** - * 消息内容 - */ - private String messageContent; - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/redis/RedisWebSocketMessageConsumer.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/redis/RedisWebSocketMessageConsumer.java deleted file mode 100644 index 228964c..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/redis/RedisWebSocketMessageConsumer.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.iailab.framework.websocket.core.sender.redis; - -import com.iailab.framework.mq.redis.core.pubsub.AbstractRedisChannelMessageListener; -import lombok.RequiredArgsConstructor; - -/** - * {@link RedisWebSocketMessage} 广播消息的消费者,真正把消息发送出去 - * - * @author iailab - */ -@RequiredArgsConstructor -public class RedisWebSocketMessageConsumer extends AbstractRedisChannelMessageListener<RedisWebSocketMessage> { - - private final RedisWebSocketMessageSender redisWebSocketMessageSender; - - @Override - public void onMessage(RedisWebSocketMessage message) { - redisWebSocketMessageSender.send(message.getSessionId(), - message.getUserType(), message.getUserId(), - message.getMessageType(), message.getMessageContent()); - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/redis/RedisWebSocketMessageSender.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/redis/RedisWebSocketMessageSender.java deleted file mode 100644 index 5d400a1..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/redis/RedisWebSocketMessageSender.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.iailab.framework.websocket.core.sender.redis; - -import com.iailab.framework.mq.redis.core.RedisMQTemplate; -import com.iailab.framework.websocket.core.sender.AbstractWebSocketMessageSender; -import com.iailab.framework.websocket.core.sender.WebSocketMessageSender; -import com.iailab.framework.websocket.core.session.WebSocketSessionManager; -import lombok.extern.slf4j.Slf4j; - -/** - * 基于 Redis 的 {@link WebSocketMessageSender} 实现类 - * - * @author iailab - */ -@Slf4j -public class RedisWebSocketMessageSender extends AbstractWebSocketMessageSender { - - private final RedisMQTemplate redisMQTemplate; - - public RedisWebSocketMessageSender(WebSocketSessionManager sessionManager, - RedisMQTemplate redisMQTemplate) { - super(sessionManager); - this.redisMQTemplate = redisMQTemplate; - } - - @Override - public void send(Integer userType, Long userId, String messageType, String messageContent) { - sendRedisMessage(null, userId, userType, messageType, messageContent); - } - - @Override - public void send(Integer userType, String messageType, String messageContent) { - sendRedisMessage(null, null, userType, messageType, messageContent); - } - - @Override - public void send(String sessionId, String messageType, String messageContent) { - sendRedisMessage(sessionId, null, null, messageType, messageContent); - } - - /** - * 通过 Redis 广播消息 - * - * @param sessionId Session 编号 - * @param userId 用户编号 - * @param userType 用户类型 - * @param messageType 消息类型 - * @param messageContent 消息内容 - */ - private void sendRedisMessage(String sessionId, Long userId, Integer userType, - String messageType, String messageContent) { - RedisWebSocketMessage mqMessage = new RedisWebSocketMessage() - .setSessionId(sessionId).setUserId(userId).setUserType(userType) - .setMessageType(messageType).setMessageContent(messageContent); - redisMQTemplate.send(mqMessage); - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rocketmq/RocketMQWebSocketMessage.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rocketmq/RocketMQWebSocketMessage.java deleted file mode 100644 index 92dc515..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rocketmq/RocketMQWebSocketMessage.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.iailab.framework.websocket.core.sender.rocketmq; - -import lombok.Data; - -/** - * RocketMQ 广播 WebSocket 的消息 - * - * @author iailab - */ -@Data -public class RocketMQWebSocketMessage { - - /** - * Session 编号 - */ - private String sessionId; - /** - * 用户类型 - */ - private Integer userType; - /** - * 用户编号 - */ - private Long userId; - - /** - * 消息类型 - */ - private String messageType; - /** - * 消息内容 - */ - private String messageContent; - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rocketmq/RocketMQWebSocketMessageConsumer.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rocketmq/RocketMQWebSocketMessageConsumer.java deleted file mode 100644 index 38b7836..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rocketmq/RocketMQWebSocketMessageConsumer.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.iailab.framework.websocket.core.sender.rocketmq; - -import lombok.RequiredArgsConstructor; -import org.apache.rocketmq.spring.annotation.MessageModel; -import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; -import org.apache.rocketmq.spring.core.RocketMQListener; - -/** - * {@link RocketMQWebSocketMessage} 广播消息的消费者,真正把消息发送出去 - * - * @author iailab - */ -@RocketMQMessageListener( // 重点:添加 @RocketMQMessageListener 注解,声明消费的 topic - topic = "${iailab.websocket.sender-rocketmq.topic}", - consumerGroup = "${iailab.websocket.sender-rocketmq.consumer-group}", - messageModel = MessageModel.BROADCASTING // 设置为广播模式,保证每个实例都能收到消息 -) -@RequiredArgsConstructor -public class RocketMQWebSocketMessageConsumer implements RocketMQListener<RocketMQWebSocketMessage> { - - private final RocketMQWebSocketMessageSender rocketMQWebSocketMessageSender; - - @Override - public void onMessage(RocketMQWebSocketMessage message) { - rocketMQWebSocketMessageSender.send(message.getSessionId(), - message.getUserType(), message.getUserId(), - message.getMessageType(), message.getMessageContent()); - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rocketmq/RocketMQWebSocketMessageSender.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rocketmq/RocketMQWebSocketMessageSender.java deleted file mode 100644 index 2241b13..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/sender/rocketmq/RocketMQWebSocketMessageSender.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.iailab.framework.websocket.core.sender.rocketmq; - -import com.iailab.framework.websocket.core.sender.AbstractWebSocketMessageSender; -import com.iailab.framework.websocket.core.sender.WebSocketMessageSender; -import com.iailab.framework.websocket.core.session.WebSocketSessionManager; -import lombok.extern.slf4j.Slf4j; -import org.apache.rocketmq.spring.core.RocketMQTemplate; - -/** - * 基于 RocketMQ 的 {@link WebSocketMessageSender} 实现类 - * - * @author iailab - */ -@Slf4j -public class RocketMQWebSocketMessageSender extends AbstractWebSocketMessageSender { - - private final RocketMQTemplate rocketMQTemplate; - - private final String topic; - - public RocketMQWebSocketMessageSender(WebSocketSessionManager sessionManager, - RocketMQTemplate rocketMQTemplate, - String topic) { - super(sessionManager); - this.rocketMQTemplate = rocketMQTemplate; - this.topic = topic; - } - - @Override - public void send(Integer userType, Long userId, String messageType, String messageContent) { - sendRocketMQMessage(null, userId, userType, messageType, messageContent); - } - - @Override - public void send(Integer userType, String messageType, String messageContent) { - sendRocketMQMessage(null, null, userType, messageType, messageContent); - } - - @Override - public void send(String sessionId, String messageType, String messageContent) { - sendRocketMQMessage(sessionId, null, null, messageType, messageContent); - } - - /** - * 通过 RocketMQ 广播消息 - * - * @param sessionId Session 编号 - * @param userId 用户编号 - * @param userType 用户类型 - * @param messageType 消息类型 - * @param messageContent 消息内容 - */ - private void sendRocketMQMessage(String sessionId, Long userId, Integer userType, - String messageType, String messageContent) { - RocketMQWebSocketMessage mqMessage = new RocketMQWebSocketMessage() - .setSessionId(sessionId).setUserId(userId).setUserType(userType) - .setMessageType(messageType).setMessageContent(messageContent); - rocketMQTemplate.syncSend(topic, mqMessage); - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/session/WebSocketSessionHandlerDecorator.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/session/WebSocketSessionHandlerDecorator.java deleted file mode 100644 index 9836e56..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/session/WebSocketSessionHandlerDecorator.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.iailab.framework.websocket.core.session; - -import org.springframework.web.socket.CloseStatus; -import org.springframework.web.socket.WebSocketHandler; -import org.springframework.web.socket.WebSocketSession; -import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator; -import org.springframework.web.socket.handler.WebSocketHandlerDecorator; - -/** - * {@link WebSocketHandler} 的装饰类,实现了以下功能: - * - * 1. {@link WebSocketSession} 连接或关闭时,使用 {@link #sessionManager} 进行管理 - * 2. 封装 {@link WebSocketSession} 支持并发操作 - * - * @author iailab - */ -public class WebSocketSessionHandlerDecorator extends WebSocketHandlerDecorator { - - /** - * 发送时间的限制,单位:毫秒 - */ - private static final Integer SEND_TIME_LIMIT = 1000 * 5; - /** - * 发送消息缓冲上线,单位:bytes - */ - private static final Integer BUFFER_SIZE_LIMIT = 1024 * 100; - - private final WebSocketSessionManager sessionManager; - - public WebSocketSessionHandlerDecorator(WebSocketHandler delegate, - WebSocketSessionManager sessionManager) { - super(delegate); - this.sessionManager = sessionManager; - } - - @Override - public void afterConnectionEstablished(WebSocketSession session) { - // 实现 session 支持并发,可参考 https://blog.csdn.net/abu935009066/article/details/131218149 - session = new ConcurrentWebSocketSessionDecorator(session, SEND_TIME_LIMIT, BUFFER_SIZE_LIMIT); - // 添加到 WebSocketSessionManager 中 - sessionManager.addSession(session); - } - - @Override - public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) { - sessionManager.removeSession(session); - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/session/WebSocketSessionManager.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/session/WebSocketSessionManager.java deleted file mode 100644 index ad2608a..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/session/WebSocketSessionManager.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.iailab.framework.websocket.core.session; - -import org.springframework.web.socket.WebSocketSession; - -import java.util.Collection; - -/** - * {@link WebSocketSession} 管理器的接口 - * - * @author iailab - */ -public interface WebSocketSessionManager { - - /** - * 添加 Session - * - * @param session Session - */ - void addSession(WebSocketSession session); - - /** - * 移除 Session - * - * @param session Session - */ - void removeSession(WebSocketSession session); - - /** - * 获得指定编号的 Session - * - * @param id Session 编号 - * @return Session - */ - WebSocketSession getSession(String id); - - /** - * 获得指定用户类型的 Session 列表 - * - * @param userType 用户类型 - * @return Session 列表 - */ - Collection<WebSocketSession> getSessionList(Integer userType); - - /** - * 获得指定用户编号的 Session 列表 - * - * @param userType 用户类型 - * @param userId 用户编号 - * @return Session 列表 - */ - Collection<WebSocketSession> getSessionList(Integer userType, Long userId); - -} \ No newline at end of file diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/session/WebSocketSessionManagerImpl.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/session/WebSocketSessionManagerImpl.java deleted file mode 100644 index 418ec88..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/session/WebSocketSessionManagerImpl.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.iailab.framework.websocket.core.session; - -import cn.hutool.core.collection.CollUtil; -import com.iailab.framework.security.core.LoginUser; -import com.iailab.framework.tenant.core.context.TenantContextHolder; -import com.iailab.framework.websocket.core.util.WebSocketFrameworkUtils; -import org.springframework.web.socket.WebSocketSession; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * 默认的 {@link WebSocketSessionManager} 实现类 - * - * @author iailab - */ -public class WebSocketSessionManagerImpl implements WebSocketSessionManager { - - /** - * id 与 WebSocketSession 映射 - * - * key:Session 编号 - */ - private final ConcurrentMap<String, WebSocketSession> idSessions = new ConcurrentHashMap<>(); - - /** - * user 与 WebSocketSession 映射 - * - * key1:用户类型 - * key2:用户编号 - */ - private final ConcurrentMap<Integer, ConcurrentMap<Long, CopyOnWriteArrayList<WebSocketSession>>> userSessions - = new ConcurrentHashMap<>(); - - @Override - public void addSession(WebSocketSession session) { - // 添加到 idSessions 中 - idSessions.put(session.getId(), session); - // 添加到 userSessions 中 - LoginUser user = WebSocketFrameworkUtils.getLoginUser(session); - if (user == null) { - return; - } - ConcurrentMap<Long, CopyOnWriteArrayList<WebSocketSession>> userSessionsMap = userSessions.get(user.getUserType()); - if (userSessionsMap == null) { - userSessionsMap = new ConcurrentHashMap<>(); - if (userSessions.putIfAbsent(user.getUserType(), userSessionsMap) != null) { - userSessionsMap = userSessions.get(user.getUserType()); - } - } - CopyOnWriteArrayList<WebSocketSession> sessions = userSessionsMap.get(user.getId()); - if (sessions == null) { - sessions = new CopyOnWriteArrayList<>(); - if (userSessionsMap.putIfAbsent(user.getId(), sessions) != null) { - sessions = userSessionsMap.get(user.getId()); - } - } - sessions.add(session); - } - - @Override - public void removeSession(WebSocketSession session) { - // 移除从 idSessions 中 - idSessions.remove(session.getId()); - // 移除从 idSessions 中 - LoginUser user = WebSocketFrameworkUtils.getLoginUser(session); - if (user == null) { - return; - } - ConcurrentMap<Long, CopyOnWriteArrayList<WebSocketSession>> userSessionsMap = userSessions.get(user.getUserType()); - if (userSessionsMap == null) { - return; - } - CopyOnWriteArrayList<WebSocketSession> sessions = userSessionsMap.get(user.getId()); - sessions.removeIf(session0 -> session0.getId().equals(session.getId())); - if (CollUtil.isEmpty(sessions)) { - userSessionsMap.remove(user.getId(), sessions); - } - } - - @Override - public WebSocketSession getSession(String id) { - return idSessions.get(id); - } - - @Override - public Collection<WebSocketSession> getSessionList(Integer userType) { - ConcurrentMap<Long, CopyOnWriteArrayList<WebSocketSession>> userSessionsMap = userSessions.get(userType); - if (CollUtil.isEmpty(userSessionsMap)) { - return new ArrayList<>(); - } - LinkedList<WebSocketSession> result = new LinkedList<>(); // 避免扩容 - Long contextTenantId = TenantContextHolder.getTenantId(); - for (List<WebSocketSession> sessions : userSessionsMap.values()) { - if (CollUtil.isEmpty(sessions)) { - continue; - } - // 特殊:如果租户不匹配,则直接排除 - if (contextTenantId != null) { - Long userTenantId = WebSocketFrameworkUtils.getTenantId(sessions.get(0)); - if (!contextTenantId.equals(userTenantId)) { - continue; - } - } - result.addAll(sessions); - } - return result; - } - - @Override - public Collection<WebSocketSession> getSessionList(Integer userType, Long userId) { - ConcurrentMap<Long, CopyOnWriteArrayList<WebSocketSession>> userSessionsMap = userSessions.get(userType); - if (CollUtil.isEmpty(userSessionsMap)) { - return new ArrayList<>(); - } - CopyOnWriteArrayList<WebSocketSession> sessions = userSessionsMap.get(userId); - return CollUtil.isNotEmpty(sessions) ? new ArrayList<>(sessions) : new ArrayList<>(); - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/util/WebSocketFrameworkUtils.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/util/WebSocketFrameworkUtils.java deleted file mode 100644 index 96a6c45..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/core/util/WebSocketFrameworkUtils.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.iailab.framework.websocket.core.util; - -import com.iailab.framework.security.core.LoginUser; -import org.springframework.web.socket.WebSocketSession; - -import java.util.Map; - -/** - * 专属于 web 包的工具类 - * - * @author iailab - */ -public class WebSocketFrameworkUtils { - - public static final String ATTRIBUTE_LOGIN_USER = "LOGIN_USER"; - - /** - * 设置当前用户 - * - * @param loginUser 登录用户 - * @param attributes Session - */ - public static void setLoginUser(LoginUser loginUser, Map<String, Object> attributes) { - attributes.put(ATTRIBUTE_LOGIN_USER, loginUser); - } - - /** - * 获取当前用户 - * - * @return 当前用户 - */ - public static LoginUser getLoginUser(WebSocketSession session) { - return (LoginUser) session.getAttributes().get(ATTRIBUTE_LOGIN_USER); - } - - /** - * 获得当前用户的编号 - * - * @return 用户编号 - */ - public static Long getLoginUserId(WebSocketSession session) { - LoginUser loginUser = getLoginUser(session); - return loginUser != null ? loginUser.getId() : null; - } - - /** - * 获得当前用户的类型 - * - * @return 用户编号 - */ - public static Integer getLoginUserType(WebSocketSession session) { - LoginUser loginUser = getLoginUser(session); - return loginUser != null ? loginUser.getUserType() : null; - } - - /** - * 获得当前用户的租户编号 - * - * @param session Session - * @return 租户编号 - */ - public static Long getTenantId(WebSocketSession session) { - LoginUser loginUser = getLoginUser(session); - return loginUser != null ? loginUser.getTenantId() : null; - } - -} diff --git a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/package-info.java b/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/package-info.java deleted file mode 100644 index e10f7ce..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/java/com/iailab/framework/websocket/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * WebSocket 框架,支持多节点的广播 - */ -package com.iailab.framework.websocket; diff --git a/iailab-framework/iailab-common-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/iailab-framework/iailab-common-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 08c8131..0000000 --- a/iailab-framework/iailab-common-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -com.iailab.framework.websocket.config.IailabWebSocketAutoConfiguration \ No newline at end of file diff --git a/iailab-framework/iailab-common/pom.xml b/iailab-framework/iailab-common/pom.xml deleted file mode 100644 index ad63de0..0000000 --- a/iailab-framework/iailab-common/pom.xml +++ /dev/null @@ -1,159 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>com.iailab</groupId> - <artifactId>iailab-framework</artifactId> - <version>${revision}</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>iailab-common</artifactId> - <packaging>jar</packaging> - - <name>${project.artifactId}</name> - <description>定义基础 pojo 类、枚举、工具类等等</description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <dependencies> - <!-- Spring 核心 --> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-core</artifactId> -<!-- <scope>provided</scope> <!– 设置为 provided,只有工具类需要使用到 –>--> - </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-expression</artifactId> -<!-- <scope>provided</scope> <!– 设置为 provided,只有工具类需要使用到 –>--> - </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-aop</artifactId> -<!-- <scope>provided</scope> <!– 设置为 provided,只有工具类需要使用到 –>--> - </dependency> - <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjweaver</artifactId> -<!-- <scope>provided</scope> <!– 设置为 provided,只有工具类需要使用到 –>--> - </dependency> - - <dependency> - <!-- 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 --> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-configuration-processor</artifactId> - <optional>true</optional> - </dependency> - - <!-- Web 相关 --> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-web</artifactId> -<!-- <scope>provided</scope> <!– 设置为 provided,只有工具类需要使用到 –>--> - </dependency> - - <dependency> - <groupId>jakarta.servlet</groupId> - <artifactId>jakarta.servlet-api</artifactId> -<!-- <scope>provided</scope> <!– 设置为 provided,只有工具类需要使用到 –>--> - </dependency> - - <dependency> - <groupId>org.springdoc</groupId> - <artifactId>springdoc-openapi-ui</artifactId> - <scope>provided</scope> - </dependency> - - <!-- 监控相关 --> - <dependency> - <groupId>org.apache.skywalking</groupId> - <artifactId>apm-toolkit-trace</artifactId> - </dependency> - - <!-- 工具类相关 --> - <dependency> - <groupId>org.projectlombok</groupId> - <artifactId>lombok</artifactId> - </dependency> - - <dependency> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct</artifactId> - </dependency> - <dependency> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct-jdk8</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher --> - </dependency> - <dependency> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct-processor</artifactId> - </dependency> - - <dependency> - <groupId>com.google.guava</groupId> - <artifactId>guava</artifactId> -<!-- <scope>provided</scope> <!– 设置为 provided,只有工具类需要使用到 –>--> - </dependency> - - <dependency> - <groupId>com.fasterxml.jackson.core</groupId> - <artifactId>jackson-databind</artifactId> -<!-- <scope>provided</scope> <!– 设置为 provided,只有工具类需要使用到 –>--> - </dependency> - <dependency> - <groupId>com.fasterxml.jackson.core</groupId> - <artifactId>jackson-core</artifactId> -<!-- <scope>provided</scope> <!– 设置为 provided,只有工具类需要使用到 –>--> - </dependency> - <dependency> - <groupId>com.fasterxml.jackson.datatype</groupId> - <artifactId>jackson-datatype-jsr310</artifactId> -<!-- <scope>provided</scope> <!– 设置为 provided,只有工具类需要使用到 –>--> - </dependency> - - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>slf4j-api</artifactId> -<!-- <scope>provided</scope> <!– 设置为 provided,只有工具类需要使用到 –>--> - </dependency> - - <dependency> - <groupId>jakarta.validation</groupId> - <artifactId>jakarta.validation-api</artifactId> -<!-- <scope>provided</scope> <!– 设置为 provided,主要是 PageParam 使用到 –>--> - </dependency> - - <dependency> - <groupId>cn.hutool</groupId> - <artifactId>hutool-all</artifactId> - </dependency> - - <dependency> - <groupId>joda-time</groupId> - <artifactId>joda-time</artifactId> - </dependency> - - <dependency> - <groupId>com.alibaba</groupId> - <artifactId>transmittable-thread-local</artifactId> - </dependency> - - <dependency> - <groupId>org.jsoup</groupId> - <artifactId>jsoup</artifactId> - </dependency> - - <dependency> - <groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 --> - <artifactId>easy-trans-anno</artifactId> <!-- 默认引入的原因,方便 xxx-module-api 包使用 --> - </dependency> - - <!-- Test 测试相关 --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-test</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - -</project> diff --git a/iailab-framework/iailab-common/src/main/java/com/fhs/trans/service/AutoTransable.java b/iailab-framework/iailab-common/src/main/java/com/fhs/trans/service/AutoTransable.java deleted file mode 100644 index b00d45e..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/fhs/trans/service/AutoTransable.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.fhs.trans.service; - -import com.fhs.core.trans.vo.VO; - -import java.util.ArrayList; -import java.util.List; - -/** - * 只有实现了这个接口的才能自动翻译 - * - * 为什么要赋值粘贴到 iailab-common 包下? - * 因为 AutoTransable 属于 easy-trans-service 下,无法方便的在 iailab-module-xxx-api 模块下使用 - * - * @author jackwang - * @since 2020-05-19 10:26:15 - */ -public interface AutoTransable<V extends VO> { - - /** - * 根据 ids 查询数据列表 - * - * 改方法已过期啦,请使用 selectByIds - * - * @param ids 编号数组 - * @return 数据列表 - */ - @Deprecated - default List<V> findByIds(List<? extends Object> ids){ - return new ArrayList<>(); - } - - /** - * 根据 ids 查询 - * - * @param ids 编号数组 - * @return 数据列表 - */ - default List<V> selectByIds(List<? extends Object> ids){ - return this.findByIds(ids); - } - - /** - * 获取 db 中所有的数据 - * - * @return db 中所有的数据 - */ - default List<V> select(){ - return new ArrayList<>(); - } - - /** - * 根据 id 获取 vo - * - * @param primaryValue id - * @return vo - */ - V selectById(Object primaryValue); - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/AutoBpm.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/AutoBpm.java deleted file mode 100644 index 33abe2f..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/AutoBpm.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.iailab.framework.common.annotation; - -import java.lang.annotation.*; - -/** - * 业务流程 - * - * @author PanZhibao - * @Description - * @createTime 2022年12月20日 16:10:00 - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface AutoBpm { -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/AutoDict.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/AutoDict.java deleted file mode 100644 index 7205fb9..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/AutoDict.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.iailab.framework.common.annotation; - -import java.lang.annotation.*; - -/** - * @author PanZhibao - * @Description - * @createTime 2022年05月21日 10:59:00 - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface AutoDict { - - /** - * 暂时无用 - * @return - */ - String value() default ""; -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/AutoUser.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/AutoUser.java deleted file mode 100644 index c1c0a36..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/AutoUser.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.iailab.framework.common.annotation; - -import java.lang.annotation.*; - -/** - * @author PanZhibao - * @Description - * @createTime 2023年06月07日 11:35:00 - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface AutoUser { - - /** - * 暂时无用 - * @return - */ - String value() default ""; -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/BpmProcess.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/BpmProcess.java deleted file mode 100644 index 8453bef..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/BpmProcess.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.common.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 业务流程ID - * - * @author PanZhibao - * @Description - * @createTime 2022年12月20日 16:17:00 - */ -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -public @interface BpmProcess { - - /** - * 业务ID - * - * @return - */ - String businessKey(); -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/BpmStatus.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/BpmStatus.java deleted file mode 100644 index 213f0a7..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/BpmStatus.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.common.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 业务流程状态 - * - * @author PanZhibao - * @Description - * @createTime 2022年12月20日 16:14:00 - */ -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -public @interface BpmStatus { - - /** - * 业务ID - * - * @return - */ - String businessKey(); -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/Dict.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/Dict.java deleted file mode 100644 index dd01bb5..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/Dict.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.iailab.framework.common.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 字典注解 - * - * @author PanZhibao - * @Description - * @createTime 2022年05月20日 17:36:00 - */ -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -public @interface Dict { - - /** - * 数据code - * - * @return - */ - String dicCode(); - - /** - * 数据itemValue - * - * @return - */ - String itemValue(); -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/UserRealName.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/UserRealName.java deleted file mode 100644 index a142543..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/annotation/UserRealName.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.iailab.framework.common.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author PanZhibao - * @Description - * @createTime 2023年06月07日 11:37:00 - */ -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -public @interface UserRealName { - - /** - * 用户ID - * - * @return - */ - String userid() default ""; - - /** - * 用户账号 - * - * @return - */ - String username() default ""; -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/constant/CacheConstant.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/constant/CacheConstant.java deleted file mode 100644 index 36e9ba2..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/constant/CacheConstant.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.iailab.framework.common.constant; - -/** - * @author: huangxutao - * @date: 2019-06-14 - * @description: 缓存常量 - */ -public interface CacheConstant { - - /** - * 字典信息缓存(含禁用的字典项) - */ - public static final String SYS_DICT_CACHE = "sys:cache:dict"; - - /** - * 字典信息缓存 status为有效的 - */ - public static final String SYS_ENABLE_DICT_CACHE = "sys:cache:dictEnable"; - /** - * 表字典信息缓存 - */ - public static final String SYS_DICT_TABLE_CACHE = "sys:cache:dictTable"; - public static final String SYS_DICT_TABLE_BY_KEYS_CACHE = SYS_DICT_TABLE_CACHE + "ByKeys"; - - /** - * 数据权限配置缓存 - */ - public static final String SYS_DATA_PERMISSIONS_CACHE = "sys:cache:permission:datarules"; - - /** - * 缓存用户信息 - */ - public static final String SYS_USERS_CACHE = "sys:cache:user"; - - /** - * 全部部门信息缓存 - */ - public static final String SYS_DEPARTS_CACHE = "sys:cache:depart:alldata"; - - - /** - * 全部部门ids缓存 - */ - public static final String SYS_DEPART_IDS_CACHE = "sys:cache:depart:allids"; - - - /** - * 测试缓存key - */ - public static final String TEST_DEMO_CACHE = "test:demo"; - - /** - * 字典信息缓存 - */ - public static final String SYS_DYNAMICDB_CACHE = "sys:cache:dbconnect:dynamic:"; - - /** - * gateway路由缓存 - */ - public static final String GATEWAY_ROUTES = "sys:cache:cloud:gateway_routes"; - - - /** - * gatewayAPI缓存 - */ - public static final String GATEWAY_APIS = "sys:cache:cloud:gateway_apis"; - - /** - * gateway路由 reload key - */ - public static final String ROUTE_JVM_RELOAD_TOPIC = "gateway_jvm_route_reload_topic"; - - /** - * TODO 冗余代码 待删除 - *插件商城排行榜 - */ - public static final String PLUGIN_MALL_RANKING = "pluginMall::rankingList"; - /** - * TODO 冗余代码 待删除 - *插件商城排行榜 - */ - public static final String PLUGIN_MALL_PAGE_LIST = "pluginMall::queryPageList"; - - - /** - * online列表页配置信息缓存key - */ - public static final String ONLINE_LIST = "sys:cache:online:list"; - - /** - * online表单页配置信息缓存key - */ - public static final String ONLINE_FORM = "sys:cache:online:form"; - - /** - * online报表 - */ - public static final String ONLINE_RP = "sys:cache:online:rp"; - - /** - * online图表 - */ - public static final String ONLINE_GRAPH = "sys:cache:online:graph"; - /** - * 拖拽页面信息缓存 - */ - public static final String DRAG_PAGE_CACHE = "drag:cache:param"; -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/constant/CommonConstant.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/constant/CommonConstant.java deleted file mode 100644 index 5fb8fca..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/constant/CommonConstant.java +++ /dev/null @@ -1,406 +0,0 @@ -package com.iailab.framework.common.constant; - -import java.math.BigDecimal; - -/** - * @Description: 通用常量 - */ -public interface CommonConstant { - - BigDecimal BAD_VALUE = new BigDecimal("-2"); - - BigDecimal ZERO_VALUE = new BigDecimal("0"); - - /** - * 正常状态 - */ - public static final Integer STATUS_NORMAL = 0; - - /** - * 禁用状态 - */ - public static final Integer STATUS_DISABLE = -1; - - /** - * 删除标志 - */ - public static final Integer DEL_FLAG_1 = 1; - - /** - * 未删除 - */ - public static final Integer DEL_FLAG_0 = 0; - - /** - * 未提交 - */ - public static final Integer SUBMINT_STATUS_0 = 0; - - /** - * 系统日志类型: 登录 - */ - public static final int LOG_TYPE_1 = 1; - - /** - * 系统日志类型: 操作 - */ - public static final int LOG_TYPE_2 = 2; - - /** - * 操作日志类型: 查询 - */ - public static final int OPERATE_TYPE_1 = 1; - - /** - * 操作日志类型: 添加 - */ - public static final int OPERATE_TYPE_2 = 2; - - /** - * 操作日志类型: 更新 - */ - public static final int OPERATE_TYPE_3 = 3; - - /** - * 操作日志类型: 删除 - */ - public static final int OPERATE_TYPE_4 = 4; - - /** - * 操作日志类型: 倒入 - */ - public static final int OPERATE_TYPE_5 = 5; - - /** - * 操作日志类型: 导出 - */ - public static final int OPERATE_TYPE_6 = 6; - - /** - * 提交 - */ - public static final int SUBMIT_FLAG_1 = 1; - - /** - * 提交 - */ - public static final int SUBMIT_FLAG_0 = 0; - - /** - * 启用 - */ - public static final int IS_ENABLE = 1; - - /** - * 常量点类型 - */ - public static final String POINT_TYPE_NAME_CONSTANT = "CONSTANT"; - - - /** {@code 500 Server Error} (HTTP/1.0 - RFC 1945) */ - public static final Integer SC_INTERNAL_SERVER_ERROR_500 = 500; - /** {@code 200 OK} (HTTP/1.0 - RFC 1945) */ - public static final Integer SC_OK_200 = 200; - - /**访问权限认证未通过 510*/ - public static final Integer SC_JEECG_NO_AUTHZ=510; - - /** 登录用户Shiro权限缓存KEY前缀 */ - public static String PREFIX_USER_SHIRO_CACHE = "shiro:cache:org.jeecg.config.shiro.ShiroRealm.authorizationCache:"; - /** 登录用户Token令牌缓存KEY前缀 */ - public static final String PREFIX_USER_TOKEN = "prefix_user_token_"; -// /** Token缓存时间:3600秒即一小时 */ -// public static final int TOKEN_EXPIRE_TIME = 3600; - - /** 登录二维码 */ - public static final String LOGIN_QRCODE_PRE = "QRCODELOGIN:"; - public static final String LOGIN_QRCODE = "LQ:"; - /** 登录二维码token */ - public static final String LOGIN_QRCODE_TOKEN = "LQT:"; - - - /** - * 0:一级菜单 - */ - public static final Integer MENU_TYPE_0 = 0; - /** - * 1:子菜单 - */ - public static final Integer MENU_TYPE_1 = 1; - /** - * 2:按钮权限 - */ - public static final Integer MENU_TYPE_2 = 2; - - /**通告对象类型(USER:指定用户,ALL:全体用户)*/ - public static final String MSG_TYPE_UESR = "USER"; - public static final String MSG_TYPE_ALL = "ALL"; - - /**发布状态(0未发布,1已发布,2已撤销)*/ - public static final String NO_SEND = "0"; - public static final String HAS_SEND = "1"; - public static final String HAS_CANCLE = "2"; - - /**阅读状态(0未读,1已读)*/ - public static final String HAS_READ_FLAG = "1"; - public static final String NO_READ_FLAG = "0"; - - /**优先级(L低,M中,H高)*/ - public static final String PRIORITY_L = "L"; - public static final String PRIORITY_M = "M"; - public static final String PRIORITY_H = "H"; - - /** - * 短信模板方式 0 .登录模板、1.注册模板、2.忘记密码模板 - */ - public static final String SMS_TPL_TYPE_0 = "0"; - public static final String SMS_TPL_TYPE_1 = "1"; - public static final String SMS_TPL_TYPE_2 = "2"; - - /** - * 状态(0无效1有效) - */ - public static final String STATUS_0 = "0"; - public static final String STATUS_1 = "1"; - - /** - * 同步工作流引擎1同步0不同步 - */ - public static final Integer ACT_SYNC_1 = 1; - public static final Integer ACT_SYNC_0 = 0; - - /** - * 消息类型1:通知公告2:系统消息 - */ - public static final String MSG_CATEGORY_1 = "1"; - public static final String MSG_CATEGORY_2 = "2"; - - /** - * 是否配置菜单的数据权限 1是0否 - */ - public static final Integer RULE_FLAG_0 = 0; - public static final Integer RULE_FLAG_1 = 1; - - /** - * 是否用户已被冻结 1正常(解冻) 2冻结 - */ - public static final Integer USER_UNFREEZE = 1; - public static final Integer USER_FREEZE = 2; - - /**字典翻译文本后缀*/ - public static final String DICT_TEXT_SUFFIX = "_dictText"; - - /** - * 表单设计器主表类型 - */ - public static final Integer DESIGN_FORM_TYPE_MAIN = 1; - - /** - * 表单设计器子表表类型 - */ - public static final Integer DESIGN_FORM_TYPE_SUB = 2; - - /** - * 表单设计器URL授权通过 - */ - public static final Integer DESIGN_FORM_URL_STATUS_PASSED = 1; - - /** - * 表单设计器URL授权未通过 - */ - public static final Integer DESIGN_FORM_URL_STATUS_NOT_PASSED = 2; - - /** - * 表单设计器新增 Flag - */ - public static final String DESIGN_FORM_URL_TYPE_ADD = "add"; - /** - * 表单设计器修改 Flag - */ - public static final String DESIGN_FORM_URL_TYPE_EDIT = "edit"; - /** - * 表单设计器详情 Flag - */ - public static final String DESIGN_FORM_URL_TYPE_DETAIL = "detail"; - /** - * 表单设计器复用数据 Flag - */ - public static final String DESIGN_FORM_URL_TYPE_REUSE = "reuse"; - /** - * 表单设计器编辑 Flag (已弃用) - */ - public static final String DESIGN_FORM_URL_TYPE_VIEW = "view"; - - /** - * online参数值设置(是:Y, 否:N) - */ - public static final String ONLINE_PARAM_VAL_IS_TURE = "Y"; - public static final String ONLINE_PARAM_VAL_IS_FALSE = "N"; - - /** - * 文件上传类型(本地:local,Minio:minio,阿里云:alioss) - */ - public static final String UPLOAD_TYPE_LOCAL = "local"; - public static final String UPLOAD_TYPE_MINIO = "minio"; - public static final String UPLOAD_TYPE_OSS = "alioss"; - - /** - * 文档上传自定义桶名称 - */ - public static final String UPLOAD_CUSTOM_BUCKET = "eoafile"; - /** - * 文档上传自定义路径 - */ - public static final String UPLOAD_CUSTOM_PATH = "eoafile"; - /** - * 文件外链接有效天数 - */ - public static final Integer UPLOAD_EFFECTIVE_DAYS = 1; - - /** - * 员工身份 (1:普通员工 2:上级) - */ - public static final Integer USER_IDENTITY_1 = 1; - public static final Integer USER_IDENTITY_2 = 2; - - /** sys_user 表 username 唯一键索引 */ - public static final String SQL_INDEX_UNIQ_SYS_USER_USERNAME = "uniq_sys_user_username"; - /** sys_user 表 work_no 唯一键索引 */ - public static final String SQL_INDEX_UNIQ_SYS_USER_WORK_NO = "uniq_sys_user_work_no"; - /** sys_user 表 phone 唯一键索引 */ - public static final String SQL_INDEX_UNIQ_SYS_USER_PHONE = "uniq_sys_user_phone"; - /** 达梦数据库升提示。违反表[SYS_USER]唯一性约束 */ - public static final String SQL_INDEX_UNIQ_SYS_USER = "唯一性约束"; - - /** sys_user 表 email 唯一键索引 */ - public static final String SQL_INDEX_UNIQ_SYS_USER_EMAIL = "uniq_sys_user_email"; - /** sys_quartz_job 表 job_class_name 唯一键索引 */ - public static final String SQL_INDEX_UNIQ_JOB_CLASS_NAME = "uniq_job_class_name"; - /** sys_position 表 code 唯一键索引 */ - public static final String SQL_INDEX_UNIQ_CODE = "uniq_code"; - /** sys_role 表 code 唯一键索引 */ - public static final String SQL_INDEX_UNIQ_SYS_ROLE_CODE = "uniq_sys_role_role_code"; - /** sys_depart 表 code 唯一键索引 */ - public static final String SQL_INDEX_UNIQ_DEPART_ORG_CODE = "uniq_depart_org_code"; - /** sys_category 表 code 唯一键索引 */ - public static final String SQL_INDEX_UNIQ_CATEGORY_CODE = "idx_sc_code"; - /** - * 在线聊天 是否为默认分组 - */ - public static final String IM_DEFAULT_GROUP = "1"; - /** - * 在线聊天 图片文件保存路径 - */ - public static final String IM_UPLOAD_CUSTOM_PATH = "imfile"; - /** - * 在线聊天 用户状态 - */ - public static final String IM_STATUS_ONLINE = "online"; - - /** - * 在线聊天 SOCKET消息类型 - */ - public static final String IM_SOCKET_TYPE = "chatMessage"; - - /** - * 在线聊天 是否开启默认添加好友 1是 0否 - */ - public static final String IM_DEFAULT_ADD_FRIEND = "1"; - - /** - * 在线聊天 用户好友缓存前缀 - */ - public static final String IM_PREFIX_USER_FRIEND_CACHE = "sys:cache:im:im_prefix_user_friend_"; - - /** - * 考勤补卡业务状态 (1:同意 2:不同意) - */ - public static final String SIGN_PATCH_BIZ_STATUS_1 = "1"; - public static final String SIGN_PATCH_BIZ_STATUS_2 = "2"; - - /** - * 公文文档上传自定义路径 - */ - public static final String UPLOAD_CUSTOM_PATH_OFFICIAL = "officialdoc"; - /** - * 公文文档下载自定义路径 - */ - public static final String DOWNLOAD_CUSTOM_PATH_OFFICIAL = "officaldown"; - - /** - * WPS存储值类别(1 code文号 2 text(WPS模板还是公文发文模板)) - */ - public static final String WPS_TYPE_1="1"; - public static final String WPS_TYPE_2="2"; - - - public final static String X_ACCESS_TOKEN = "X-Access-Token"; - public final static String X_SIGN = "X-Sign"; - public final static String X_TIMESTAMP = "X-TIMESTAMP"; - public final static String TOKEN_IS_INVALID_MSG = "Token失效,请重新登录!"; - - /** - * 多租户 请求头 - */ - public final static String TENANT_ID = "tenant-id"; - - /** - * 微服务读取配置文件属性 服务地址 - */ - public final static String CLOUD_SERVER_KEY = "spring.cloud.nacos.discovery.server-addr"; - - /** - * 第三方登录 验证密码/创建用户 都需要设置一个操作码 防止被恶意调用 - */ - public final static String THIRD_LOGIN_CODE = "third_login_code"; - - /** - * 第三方APP同步方向:本地 --> 第三方APP - */ - String THIRD_SYNC_TO_APP = "SYNC_TO_APP"; - /** - * 第三方APP同步方向:第三方APP --> 本地 - */ - String THIRD_SYNC_TO_LOCAL = "SYNC_TO_LOCAL"; - - /** 系统通告消息状态:0=未发布 */ - String ANNOUNCEMENT_SEND_STATUS_0 = "0"; - /** 系统通告消息状态:1=已发布 */ - String ANNOUNCEMENT_SEND_STATUS_1 = "1"; - /** 系统通告消息状态:2=已撤销 */ - String ANNOUNCEMENT_SEND_STATUS_2 = "2"; - - /**ONLINE 报表权限用 从request中获取地址栏后的参数*/ - String ONL_REP_URL_PARAM_STR="onlRepUrlParamStr"; - - /**POST请求*/ - String HTTP_POST = "POST"; - - /**PUT请求*/ - String HTTP_PUT = "PUT"; - - /**PATCH请求*/ - String HTTP_PATCH = "PATCH"; - - /**未知的*/ - String UNKNOWN = "unknown"; - - /**字符串http*/ - String STR_HTTP = "http"; - - /**String 类型的空值*/ - String STRING_NULL = "null"; - - /**java.util.Date 包*/ - String JAVA_UTIL_DATE = "java.util.Date"; - - /**.do*/ - String SPOT_DO = ".do"; - - - /**前端vue版本标识*/ - String VERSION="X-Version"; - - /**前端vue版本*/ - String VERSION_VUE3="vue3"; -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/constant/Constant.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/constant/Constant.java deleted file mode 100644 index 0d530d5..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/constant/Constant.java +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.constant; - -/** - * 常量 - * - * @author Mark sunlightcs@gmail.com - */ -public interface Constant { - /** - * 成功 - */ - int SUCCESS = 1; - /** - * 失败 - */ - int FAIL = 0; - /** - * 菜单根节点标识 - */ - Long MENU_ROOT = 0L; - /** - * 部门根节点标识 - */ - Long DEPT_ROOT = 0L; - /** - * 升序 - */ - String ASC = "asc"; - /** - * 降序 - */ - String DESC = "desc"; - /** - * 创建时间字段名 - */ - String CREATE_DATE = "create_date"; - - String CREATE_TIME = "create_time"; - - /** - * 数据权限过滤 - */ - String SQL_FILTER = "sqlFilter"; - /** - * 当前页码 - */ - String PAGE = "page"; - /** - * 每页显示记录数 - */ - String LIMIT = "limit"; - /** - * 排序字段 - */ - String ORDER_FIELD = "orderField"; - /** - * 排序方式 - */ - String ORDER = "order"; - /** - * token header - */ - String TOKEN_HEADER = "authorization"; - - /** - * tenantCode - */ - String TENANT_CODE = "tenantCode"; - - /** - * tenantId - */ - String TENANT_ID = "tenantId"; - - /** - * tenantId - */ - String HEAD_TENANT_ID = "tenant-id"; - - /** - * 云存储配置KEY - */ - String CLOUD_STORAGE_CONFIG_KEY = "CLOUD_STORAGE_CONFIG_KEY"; - - Integer DEL_FLAG_0 = 0; - - enum EnableStatus { - DISABLED(0), - NORMAL(1); - - private int value; - - private EnableStatus(int value) { - this.value = value; - } - - public int getValue() { - return this.value; - } - } - - /** - * 定时任务状态 - */ - enum ScheduleStatus { - /** - * 暂停 - */ - PAUSE(0), - /** - * 正常 - */ - NORMAL(1); - - private int value; - - ScheduleStatus(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - /** - * 云服务商 - */ - enum CloudService { - /** - * 七牛云 - */ - QINIU(1), - /** - * 阿里云 - */ - ALIYUN(2), - /** - * 腾讯云 - */ - QCLOUD(3); - - private int value; - - CloudService(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } -} \ No newline at end of file diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/constant/GlobalConstants.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/constant/GlobalConstants.java deleted file mode 100644 index 52bdc03..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/constant/GlobalConstants.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.iailab.framework.common.constant; - -/** -* @Description: GlobalConstants -* @author: scott -* @date: 2020/01/01 16:01 -*/ -public class GlobalConstants { - - /** - * 业务处理器beanName传递参数 - */ - public static final String HANDLER_NAME = "handlerName"; - - /** - * 路由刷新触发器 - */ - public static final String LODER_ROUDER_HANDLER = "loderRouderHandler"; - - /** - * API刷新触发器 - */ - public static final String LODER_API_HANDLER = "loderApiHandler"; - - /** - * redis消息通道名称 - */ - public static final String REDIS_TOPIC_NAME="redis_topic"; -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/core/IntArrayValuable.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/core/IntArrayValuable.java deleted file mode 100644 index a4ff210..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/core/IntArrayValuable.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.iailab.framework.common.core; - -/** - * 可生成 Int 数组的接口 - * - * @author iailab - */ -public interface IntArrayValuable { - - /** - * @return int 数组 - */ - int[] array(); - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/core/KeyValue.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/core/KeyValue.java deleted file mode 100644 index a0e885c..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/core/KeyValue.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iailab.framework.common.core; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -/** - * Key Value 的键值对 - * - * @author iailab - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class KeyValue<K, V> implements Serializable { - - private K key; - private V value; - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/dto/TreeLabelDTO.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/dto/TreeLabelDTO.java deleted file mode 100644 index cfa0e22..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/dto/TreeLabelDTO.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.iailab.framework.common.dto; - -import lombok.Data; - -import java.util.List; - -/** - * @author PanZhibao - * @Description - * @createTime 2024年09月23日 - */ -@Data -public class TreeLabelDTO { - - private String value; - - private String label; - - private List<TreeLabelDTO> children; -} \ No newline at end of file diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/CommonStatusEnum.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/CommonStatusEnum.java deleted file mode 100644 index a611b8b..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/CommonStatusEnum.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.iailab.framework.common.enums; - -import cn.hutool.core.util.ObjUtil; -import com.iailab.framework.common.core.IntArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * 通用状态枚举 - * - * @author iailab - */ -@Getter -@AllArgsConstructor -public enum CommonStatusEnum implements IntArrayValuable { - - ENABLE(0, "开启"), - DISABLE(1, "关闭"); - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CommonStatusEnum::getStatus).toArray(); - - /** - * 状态值 - */ - private final Integer status; - /** - * 状态名 - */ - private final String name; - - @Override - public int[] array() { - return ARRAYS; - } - - public static boolean isEnable(Integer status) { - return ObjUtil.equal(ENABLE.status, status); - } - - public static boolean isDisable(Integer status) { - return ObjUtil.equal(DISABLE.status, status); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/DateIntervalEnum.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/DateIntervalEnum.java deleted file mode 100644 index af55211..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/DateIntervalEnum.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.iailab.framework.common.enums; - -import cn.hutool.core.util.ArrayUtil; -import com.iailab.framework.common.core.IntArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * 时间间隔的枚举 - * - * @author dhb52 - */ -@Getter -@AllArgsConstructor -public enum DateIntervalEnum implements IntArrayValuable { - - DAY(1, "天"), - WEEK(2, "周"), - MONTH(3, "月"), - QUARTER(4, "季度"), - YEAR(5, "年") - ; - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getInterval).toArray(); - - /** - * 类型 - */ - private final Integer interval; - /** - * 名称 - */ - private final String name; - - @Override - public int[] array() { - return ARRAYS; - } - - public static DateIntervalEnum valueOf(Integer interval) { - return ArrayUtil.firstMatch(item -> item.getInterval().equals(interval), DateIntervalEnum.values()); - } - -} \ No newline at end of file diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/DocumentEnum.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/DocumentEnum.java deleted file mode 100644 index 01f8ef4..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/DocumentEnum.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.iailab.framework.common.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 文档地址 - * - * @author iailab - */ -@Getter -@AllArgsConstructor -public enum DocumentEnum { - - REDIS_INSTALL("https://iailab.cn", "Redis 安装文档"), - TENANT("https://iailab.cn", "SaaS 多租户文档"); - - private final String url; - private final String memo; - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/ErrorCode.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/ErrorCode.java deleted file mode 100644 index 6228b18..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/ErrorCode.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.enums; - -/** - * 错误编码,由5位数字组成,前2位为模块编码,后3位为业务编码 - * <p> - * 如:10001(10代表系统模块,001代表业务代码) - * </p> - * - * @author Mark sunlightcs@gmail.com - * @since 1.0.0 - */ -public interface ErrorCode { - int INTERNAL_SERVER_ERROR = 500; - int UNAUTHORIZED = 401; - - int NOT_NULL = 10001; - int DB_RECORD_EXISTS = 10002; - int PARAMS_GET_ERROR = 10003; - int ACCOUNT_PASSWORD_ERROR = 10004; - int ACCOUNT_DISABLE = 10005; - int IDENTIFIER_NOT_NULL = 10006; - int CAPTCHA_ERROR = 10007; - int SUB_MENU_EXIST = 10008; - int PASSWORD_ERROR = 10009; - int SUPERIOR_DEPT_ERROR = 10011; - int SUPERIOR_MENU_ERROR = 10012; - int DATA_SCOPE_PARAMS_ERROR = 10013; - int DEPT_SUB_DELETE_ERROR = 10014; - int DEPT_USER_DELETE_ERROR = 10015; - int UPLOAD_FILE_EMPTY = 10019; - int TOKEN_NOT_EMPTY = 10020; - int TOKEN_INVALID = 10021; - int ACCOUNT_LOCK = 10022; - int OSS_UPLOAD_FILE_ERROR = 10024; - int REDIS_ERROR = 10027; - int JOB_ERROR = 10028; - int INVALID_SYMBOL = 10029; -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/RpcConstants.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/RpcConstants.java deleted file mode 100644 index b29ee71..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/RpcConstants.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.iailab.framework.common.enums; - -/** - * RPC 相关的枚举 - * - * 虽然放在 iailab-common-rpc 会相对合适,但是每个 API 模块需要使用到,所以暂时只好放在此处 - * - * @author iailab - */ -public class RpcConstants { - - /** - * RPC API 的前缀 - */ - public static final String RPC_API_PREFIX = "/rpc-api"; - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/TerminalEnum.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/TerminalEnum.java deleted file mode 100644 index c49ae51..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/TerminalEnum.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.iailab.framework.common.enums; - -import com.iailab.framework.common.core.IntArrayValuable; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.Arrays; - -/** - * 终端的枚举 - * - * @author iailab - */ -@RequiredArgsConstructor -@Getter -public enum TerminalEnum implements IntArrayValuable { - - UNKNOWN(0, "未知"), // 目的:在无法解析到 terminal 时,使用它 - WECHAT_MINI_PROGRAM(10, "微信小程序"), - WECHAT_WAP(11, "微信公众号"), - H5(20, "H5 网页"), - APP(31, "手机 App"), - ; - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TerminalEnum::getTerminal).toArray(); - - /** - * 终端 - */ - private final Integer terminal; - /** - * 终端名 - */ - private final String name; - - @Override - public int[] array() { - return ARRAYS; - } -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/UserTypeEnum.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/UserTypeEnum.java deleted file mode 100644 index 560e02c..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/UserTypeEnum.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.iailab.framework.common.enums; - -import cn.hutool.core.util.ArrayUtil; -import com.iailab.framework.common.core.IntArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * 全局用户类型枚举 - */ -@AllArgsConstructor -@Getter -public enum UserTypeEnum implements IntArrayValuable { - - MEMBER(1, "会员"), // 面向 c 端,普通用户 - ADMIN(2, "管理员"); // 面向 b 端,管理后台 - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(UserTypeEnum::getValue).toArray(); - - /** - * 类型 - */ - private final Integer value; - /** - * 类型名 - */ - private final String name; - - public static UserTypeEnum valueOf(Integer value) { - return ArrayUtil.firstMatch(userType -> userType.getValue().equals(value), UserTypeEnum.values()); - } - - @Override - public int[] array() { - return ARRAYS; - } -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/WebFilterOrderEnum.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/WebFilterOrderEnum.java deleted file mode 100644 index ea7e7f7..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/WebFilterOrderEnum.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.iailab.framework.common.enums; - -/** - * Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期 - * - * 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 enum 包下 - * - * @author iailab - */ -public interface WebFilterOrderEnum { - - int CORS_FILTER = Integer.MIN_VALUE; - - int TRACE_FILTER = CORS_FILTER + 1; - - int ENV_TAG_FILTER = TRACE_FILTER + 1; - - int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500; - - // OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等 - - int TENANT_CONTEXT_FILTER = - 104; // 需要保证在 ApiAccessLogFilter 前面 - - int API_ACCESS_LOG_FILTER = -103; // 需要保证在 RequestBodyCacheFilter 后面 - - int XSS_FILTER = -102; // 需要保证在 RequestBodyCacheFilter 后面 - - // Spring Security Filter 默认为 -100,可见 org.springframework.boot.autoconfigure.security.SecurityProperties 配置属性类 - - int TENANT_SECURITY_FILTER = -99; // 需要保证在 Spring Security 过滤器后面 - - int FLOWABLE_FILTER = -98; // 需要保证在 Spring Security 过滤后面 - - int DEMO_FILTER = Integer.MAX_VALUE; - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/ErrorCode.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/ErrorCode.java deleted file mode 100644 index af64f8f..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/ErrorCode.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.iailab.framework.common.exception; - -import com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants; -import com.iailab.framework.common.exception.enums.ServiceErrorCodeRange; -import lombok.Data; - -/** - * 错误码对象 - * - * 全局错误码,占用 [0, 999], 参见 {@link GlobalErrorCodeConstants} - * 业务异常错误码,占用 [1 000 000 000, +∞),参见 {@link ServiceErrorCodeRange} - * - * TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备 - */ -@Data -public class ErrorCode { - - /** - * 错误码 - */ - private final Integer code; - /** - * 错误提示 - */ - private final String msg; - - public ErrorCode(Integer code, String message) { - this.code = code; - this.msg = message; - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/ExceptionUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/ExceptionUtils.java deleted file mode 100644 index b4bd8e0..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/ExceptionUtils.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.exception; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; - -/** - * Exception工具类 - * - * @author Mark sunlightcs@gmail.com - */ -public class ExceptionUtils { - - /** - * 获取异常信息 - * @param ex 异常 - * @return 返回异常信息 - */ - public static String getErrorStackTrace(Exception ex){ - StringWriter sw = null; - PrintWriter pw = null; - try { - sw = new StringWriter(); - pw = new PrintWriter(sw, true); - ex.printStackTrace(pw); - }finally { - try { - if(pw != null) { - pw.close(); - } - } catch (Exception e) { - - } - try { - if(sw != null) { - sw.close(); - } - } catch (IOException e) { - - } - } - - return sw.toString(); - } -} \ No newline at end of file diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/ServerException.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/ServerException.java deleted file mode 100644 index 9e7f0f4..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/ServerException.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.iailab.framework.common.exception; - -import com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 服务器异常 Exception - */ -@Data -@EqualsAndHashCode(callSuper = true) -public final class ServerException extends RuntimeException { - - /** - * 全局错误码 - * - * @see GlobalErrorCodeConstants - */ - private Integer code; - /** - * 错误提示 - */ - private String message; - - /** - * 空构造方法,避免反序列化问题 - */ - public ServerException() { - } - - public ServerException(ErrorCode errorCode) { - this.code = errorCode.getCode(); - this.message = errorCode.getMsg(); - } - - public ServerException(Integer code, String message) { - this.code = code; - this.message = message; - } - - public Integer getCode() { - return code; - } - - public ServerException setCode(Integer code) { - this.code = code; - return this; - } - - @Override - public String getMessage() { - return message; - } - - public ServerException setMessage(String message) { - this.message = message; - return this; - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/ServiceException.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/ServiceException.java deleted file mode 100644 index 536bb40..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/ServiceException.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.iailab.framework.common.exception; - -import com.iailab.framework.common.exception.enums.ServiceErrorCodeRange; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 业务逻辑异常 Exception - */ -@Data -@EqualsAndHashCode(callSuper = true) -public final class ServiceException extends RuntimeException { - - /** - * 业务错误码 - * - * @see ServiceErrorCodeRange - */ - private Integer code; - /** - * 错误提示 - */ - private String message; - - /** - * 空构造方法,避免反序列化问题 - */ - public ServiceException() { - } - - public ServiceException(ErrorCode errorCode) { - this.code = errorCode.getCode(); - this.message = errorCode.getMsg(); - } - - public ServiceException(Integer code, String message) { - this.code = code; - this.message = message; - } - - public Integer getCode() { - return code; - } - - public ServiceException setCode(Integer code) { - this.code = code; - return this; - } - - @Override - public String getMessage() { - return message; - } - - public ServiceException setMessage(String message) { - this.message = message; - return this; - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/enums/GlobalErrorCodeConstants.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/enums/GlobalErrorCodeConstants.java deleted file mode 100644 index bf1e415..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/enums/GlobalErrorCodeConstants.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.iailab.framework.common.exception.enums; - -import com.iailab.framework.common.exception.ErrorCode; - -/** - * 全局错误码枚举 - * 0-999 系统异常编码保留 - * - * 一般情况下,使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status - * 虽然说,HTTP 响应状态码作为业务使用表达能力偏弱,但是使用在系统层面还是非常不错的 - * 比较特殊的是,因为之前一直使用 0 作为成功,就不使用 200 啦。 - * - * @author iailab - */ -public interface GlobalErrorCodeConstants { - - ErrorCode SUCCESS = new ErrorCode(0, "成功"); - - // ========== 客户端错误段 ========== - - ErrorCode BAD_REQUEST = new ErrorCode(400, "请求参数不正确"); - ErrorCode UNAUTHORIZED = new ErrorCode(401, "账号未登录"); - ErrorCode FORBIDDEN = new ErrorCode(403, "没有该操作权限"); - ErrorCode NOT_FOUND = new ErrorCode(404, "请求未找到"); - ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确"); - ErrorCode DATA_REPETITION = new ErrorCode(406, "数据库存在重复数据"); - ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许 - ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "请求过于频繁,请稍后重试"); - - // ========== 服务端错误段 ========== - - ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常"); - ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, "功能未实现/未开启"); - ErrorCode ERROR_CONFIGURATION = new ErrorCode(502, "错误的配置项"); - - // ========== 自定义错误段 ========== - ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求 - ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作"); - - ErrorCode UNKNOWN = new ErrorCode(999, "未知错误"); - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/enums/ServiceErrorCodeRange.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/enums/ServiceErrorCodeRange.java deleted file mode 100644 index 237b926..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/enums/ServiceErrorCodeRange.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.iailab.framework.common.exception.enums; - -/** - * 业务异常的错误码区间,解决:解决各模块错误码定义,避免重复,在此只声明不做实际使用 - * - * 一共 10 位,分成四段 - * - * 第一段,1 位,类型 - * 1 - 业务级别异常 - * x - 预留 - * 第二段,3 位,系统类型 - * 001 - 用户系统 - * 002 - 商品系统 - * 003 - 订单系统 - * 004 - 支付系统 - * 005 - 优惠劵系统 - * ... - ... - * 第三段,3 位,模块 - * 不限制规则。 - * 一般建议,每个系统里面,可能有多个模块,可以再去做分段。以用户系统为例子: - * 001 - OAuth2 模块 - * 002 - User 模块 - * 003 - MobileCode 模块 - * 第四段,3 位,错误码 - * 不限制规则。 - * 一般建议,每个模块自增。 - * - * @author iailab - */ -public class ServiceErrorCodeRange { - - // 模块 infra 错误码区间 [1-001-000-000 ~ 1-002-000-000) - // 模块 system 错误码区间 [1-002-000-000 ~ 1-003-000-000) - // 模块 report 错误码区间 [1-003-000-000 ~ 1-004-000-000) - // 模块 member 错误码区间 [1-004-000-000 ~ 1-005-000-000) - // 模块 mp 错误码区间 [1-006-000-000 ~ 1-007-000-000) - // 模块 pay 错误码区间 [1-007-000-000 ~ 1-008-000-000) - // 模块 bpm 错误码区间 [1-009-000-000 ~ 1-010-000-000) - - // 模块 product 错误码区间 [1-008-000-000 ~ 1-009-000-000) - // 模块 trade 错误码区间 [1-011-000-000 ~ 1-012-000-000) - // 模块 promotion 错误码区间 [1-013-000-000 ~ 1-014-000-000) - - // 模块 crm 错误码区间 [1-020-000-000 ~ 1-021-000-000) - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/util/ServiceExceptionUtil.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/util/ServiceExceptionUtil.java deleted file mode 100644 index da298fb..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/exception/util/ServiceExceptionUtil.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.iailab.framework.common.exception.util; - -import com.iailab.framework.common.exception.ErrorCode; -import com.iailab.framework.common.exception.ServiceException; -import com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants; -import com.google.common.annotations.VisibleForTesting; -import lombok.extern.slf4j.Slf4j; - -/** - * {@link ServiceException} 工具类 - * - * 目的在于,格式化异常信息提示。 - * 考虑到 String.format 在参数不正确时会报错,因此使用 {} 作为占位符,并使用 {@link #doFormat(int, String, Object...)} 方法来格式化 - * - */ -@Slf4j -public class ServiceExceptionUtil { - - // ========== 和 ServiceException 的集成 ========== - - public static ServiceException exception(ErrorCode errorCode) { - return exception0(errorCode.getCode(), errorCode.getMsg()); - } - - public static ServiceException exception(ErrorCode errorCode, Object... params) { - return exception0(errorCode.getCode(), errorCode.getMsg(), params); - } - - public static ServiceException exception0(Integer code, String messagePattern, Object... params) { - String message = doFormat(code, messagePattern, params); - return new ServiceException(code, message); - } - - public static ServiceException invalidParamException(String messagePattern, Object... params) { - return exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), messagePattern, params); - } - - // ========== 格式化方法 ========== - - /** - * 将错误编号对应的消息使用 params 进行格式化。 - * - * @param code 错误编号 - * @param messagePattern 消息模版 - * @param params 参数 - * @return 格式化后的提示 - */ - @VisibleForTesting - public static String doFormat(int code, String messagePattern, Object... params) { - StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50); - int i = 0; - int j; - int l; - for (l = 0; l < params.length; l++) { - j = messagePattern.indexOf("{}", i); - if (j == -1) { - log.error("[doFormat][参数过多:错误码({})|错误内容({})|参数({})", code, messagePattern, params); - if (i == 0) { - return messagePattern; - } else { - sbuf.append(messagePattern.substring(i)); - return sbuf.toString(); - } - } else { - sbuf.append(messagePattern, i, j); - sbuf.append(params[l]); - i = j + 2; - } - } - if (messagePattern.indexOf("{}", i) != -1) { - log.error("[doFormat][参数过少:错误码({})|错误内容({})|参数({})", code, messagePattern, params); - } - sbuf.append(messagePattern.substring(i)); - return sbuf.toString(); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/package-info.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/package-info.java deleted file mode 100644 index 6c99aa5..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 基础的通用类,和框架无关 - * - * 例如说,CommonResult 为通用返回 - */ -package com.iailab.framework.common; diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/CommonResult.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/CommonResult.java deleted file mode 100644 index 147e44d..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/CommonResult.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.iailab.framework.common.pojo; - -import cn.hutool.core.lang.Assert; -import com.iailab.framework.common.exception.ErrorCode; -import com.iailab.framework.common.exception.ServiceException; -import com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.iailab.framework.common.exception.util.ServiceExceptionUtil; -import lombok.Data; - -import java.io.Serializable; -import java.util.Objects; - -/** - * 通用返回 - * - * @param <T> 数据泛型 - */ -@Data -public class CommonResult<T> implements Serializable { - - /** - * 错误码 - * - * @see ErrorCode#getCode() - */ - private Integer code; - /** - * 返回数据 - */ - private T data; - /** - * 错误提示,用户可阅读 - * - * @see ErrorCode#getMsg() () - */ - private String msg; - - /** - * 将传入的 result 对象,转换成另外一个泛型结果的对象 - * - * 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。 - * - * @param result 传入的 result 对象 - * @param <T> 返回的泛型 - * @return 新的 CommonResult 对象 - */ - public static <T> CommonResult<T> error(CommonResult<?> result) { - return error(result.getCode(), result.getMsg()); - } - - public static <T> CommonResult<T> error(Integer code, String message) { - cn.hutool.core.lang.Assert.notEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), code, "code 必须是错误的!"); - CommonResult<T> result = new CommonResult<>(); - result.code = code; - result.msg = message; - return result; - } - - public static <T> CommonResult<T> error(ErrorCode errorCode, Object... params) { - Assert.notEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), errorCode.getCode(), "code 必须是错误的!"); - CommonResult<T> result = new CommonResult<>(); - result.code = errorCode.getCode(); - result.msg = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMsg(), params); - return result; - } - - public static <T> CommonResult<T> error(ErrorCode errorCode) { - return error(errorCode.getCode(), errorCode.getMsg()); - } - - public static <T> CommonResult<T> success(T data) { - CommonResult<T> result = new CommonResult<>(); - result.code = GlobalErrorCodeConstants.SUCCESS.getCode(); - result.data = data; - result.msg = ""; - return result; - } - - public static CommonResult<String> success() { - CommonResult<String> result = new CommonResult<>(); - result.code = GlobalErrorCodeConstants.SUCCESS.getCode(); - result.msg = "success"; - return result; - } - - public static boolean isSuccess(Integer code) { - return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.getCode()); - } - - @JsonIgnore // 避免 jackson 序列化 - public boolean isSuccess() { - return isSuccess(code); - } - - @JsonIgnore // 避免 jackson 序列化 - public boolean isError() { - return !isSuccess(); - } - - // ========= 和 Exception 异常体系集成 ========= - - /** - * 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常 - */ - public void checkError() throws ServiceException { - if (isSuccess()) { - return; - } - // 业务异常 - throw new ServiceException(code, msg); - } - - /** - * 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常 - * 如果没有,则返回 {@link #data} 数据 - */ - @JsonIgnore // 避免 jackson 序列化 - public T getCheckedData() { - checkError(); - return data; - } - - public static <T> CommonResult<T> error(ServiceException serviceException) { - return error(serviceException.getCode(), serviceException.getMessage()); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/PageParam.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/PageParam.java deleted file mode 100644 index 6d7df45..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/PageParam.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.iailab.framework.common.pojo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.Min; -import javax.validation.constraints.Max; -import javax.validation.constraints.NotNull; -import java.io.Serializable; - -@Schema(description="分页参数") -@Data -public class PageParam implements Serializable { - - private static final Integer PAGE_NO = 1; - private static final Integer PAGE_SIZE = 10; - - /** - * 每页条数 - 不分页 - * - * 例如说,导出接口,可以设置 {@link #pageSize} 为 -1 不分页,查询所有数据。 - */ - public static final Integer PAGE_SIZE_NONE = -1; - - @Schema(description = "页码,从 1 开始", requiredMode = Schema.RequiredMode.REQUIRED,example = "1") - @NotNull(message = "页码不能为空") - @Min(value = 1, message = "页码最小值为 1") - private Integer pageNo = PAGE_NO; - - @Schema(description = "每页条数,最大值为 100", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @NotNull(message = "每页条数不能为空") - @Min(value = 1, message = "每页条数最小值为 1") - @Max(value = 100, message = "每页条数最大值为 100") - private Integer pageSize = PAGE_SIZE; - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/PageResult.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/PageResult.java deleted file mode 100644 index bee40ce..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/PageResult.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.iailab.framework.common.pojo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -@Schema(description = "分页结果") -@Data -public final class PageResult<T> implements Serializable { - - @Schema(description = "数据", requiredMode = Schema.RequiredMode.REQUIRED) - private List<T> list; - - @Schema(description = "总量", requiredMode = Schema.RequiredMode.REQUIRED) - private Long total; - - public PageResult() { - } - - public PageResult(List<T> list, Long total) { - this.list = list; - this.total = total; - } - - public PageResult(Long total) { - this.list = new ArrayList<>(); - this.total = total; - } - - public static <T> PageResult<T> empty() { - return new PageResult<>(0L); - } - - public static <T> PageResult<T> empty(Long total) { - return new PageResult<>(total); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/SortablePageParam.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/SortablePageParam.java deleted file mode 100644 index 668725f..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/SortablePageParam.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.iailab.framework.common.pojo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -import java.util.List; - -@Schema(description = "可排序的分页参数") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SortablePageParam extends PageParam { - - @Schema(description = "排序字段") - private List<SortingField> sortingFields; - -} \ No newline at end of file diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/SortingField.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/SortingField.java deleted file mode 100644 index 3063446..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/pojo/SortingField.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.iailab.framework.common.pojo; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -/** - * 排序字段 DTO - * - * 类名加了 ing 的原因是,避免和 ES SortField 重名。 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class SortingField implements Serializable { - - /** - * 顺序 - 升序 - */ - public static final String ORDER_ASC = "asc"; - /** - * 顺序 - 降序 - */ - public static final String ORDER_DESC = "desc"; - - /** - * 字段 - */ - private String field; - /** - * 顺序 - */ - private String order; - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/cache/CacheUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/cache/CacheUtils.java deleted file mode 100644 index 528af27..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/cache/CacheUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.iailab.framework.common.util.cache; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; - -import java.time.Duration; -import java.util.concurrent.Executors; - -/** - * Cache 工具类 - * - * @author iailab - */ -public class CacheUtils { - - /** - * 构建异步刷新的 LoadingCache 对象 - * - * 注意:如果你的缓存和 ThreadLocal 有关系,要么自己处理 ThreadLocal 的传递,要么使用 {@link #buildCache(Duration, CacheLoader)} 方法 - * - * 或者简单理解: - * 1、和“人”相关的,使用 {@link #buildCache(Duration, CacheLoader)} 方法 - * 2、和“全局”、“系统”相关的,使用当前缓存方法 - * - * @param duration 过期时间 - * @param loader CacheLoader 对象 - * @return LoadingCache 对象 - */ - public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) { - return CacheBuilder.newBuilder() - // 只阻塞当前数据加载线程,其他线程返回旧值 - .refreshAfterWrite(duration) - // 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程 - .build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO iailab:可能要思考下,未来要不要做成可配置 - } - - /** - * 构建同步刷新的 LoadingCache 对象 - * - * @param duration 过期时间 - * @param loader CacheLoader 对象 - * @return LoadingCache 对象 - */ - public static <K, V> LoadingCache<K, V> buildCache(Duration duration, CacheLoader<K, V> loader) { - return CacheBuilder.newBuilder().refreshAfterWrite(duration).build(loader); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/collection/ArrayUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/collection/ArrayUtils.java deleted file mode 100644 index 4fba745..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/collection/ArrayUtils.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.iailab.framework.common.util.collection; - -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.collection.IterUtil; -import cn.hutool.core.util.ArrayUtil; - -import java.util.Collection; -import java.util.function.Consumer; -import java.util.function.Function; - -import static com.iailab.framework.common.util.collection.CollectionUtils.convertList; - -/** - * Array 工具类 - * - * @author iailab - */ -public class ArrayUtils { - - /** - * 将 object 和 newElements 合并成一个数组 - * - * @param object 对象 - * @param newElements 数组 - * @param <T> 泛型 - * @return 结果数组 - */ - @SafeVarargs - public static <T> Consumer<T>[] append(Consumer<T> object, Consumer<T>... newElements) { - if (object == null) { - return newElements; - } - Consumer<T>[] result = ArrayUtil.newArray(Consumer.class, 1 + newElements.length); - result[0] = object; - System.arraycopy(newElements, 0, result, 1, newElements.length); - return result; - } - - public static <T, V> V[] toArray(Collection<T> from, Function<T, V> mapper) { - return toArray(convertList(from, mapper)); - } - - @SuppressWarnings("unchecked") - public static <T> T[] toArray(Collection<T> from) { - if (CollectionUtil.isEmpty(from)) { - return (T[]) (new Object[0]); - } - return ArrayUtil.toArray(from, (Class<T>) IterUtil.getElementType(from.iterator())); - } - - public static <T> T get(T[] array, int index) { - if (null == array || index >= array.length) { - return null; - } - return array[index]; - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/collection/CollectionUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/collection/CollectionUtils.java deleted file mode 100644 index 9b972c9..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/collection/CollectionUtils.java +++ /dev/null @@ -1,338 +0,0 @@ -package com.iailab.framework.common.util.collection; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.util.ArrayUtil; -import com.google.common.collect.ImmutableMap; -import com.iailab.framework.common.pojo.PageResult; - -import java.util.*; -import java.util.function.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static java.util.Arrays.asList; - -/** - * Collection 工具类 - * - * @author iailab - */ -public class CollectionUtils { - - public static boolean containsAny(Object source, Object... targets) { - return asList(targets).contains(source); - } - - public static boolean isAnyEmpty(Collection<?>... collections) { - return Arrays.stream(collections).anyMatch(CollectionUtil::isEmpty); - } - - public static <T> boolean anyMatch(Collection<T> from, Predicate<T> predicate) { - return from.stream().anyMatch(predicate); - } - - public static <T> List<T> filterList(Collection<T> from, Predicate<T> predicate) { - if (CollUtil.isEmpty(from)) { - return new ArrayList<>(); - } - return from.stream().filter(predicate).collect(Collectors.toList()); - } - - public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper) { - if (CollUtil.isEmpty(from)) { - return new ArrayList<>(); - } - return distinct(from, keyMapper, (t1, t2) -> t1); - } - - public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper, BinaryOperator<T> cover) { - if (CollUtil.isEmpty(from)) { - return new ArrayList<>(); - } - return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values()); - } - - public static <T, U> List<U> convertList(T[] from, Function<T, U> func) { - if (ArrayUtil.isEmpty(from)) { - return new ArrayList<>(); - } - return convertList(Arrays.asList(from), func); - } - - public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) { - if (CollUtil.isEmpty(from)) { - return new ArrayList<>(); - } - return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList()); - } - - public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func, Predicate<T> filter) { - if (CollUtil.isEmpty(from)) { - return new ArrayList<>(); - } - return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList()); - } - - public static <T, U> PageResult<U> convertPage(PageResult<T> from, Function<T, U> func) { - if (ArrayUtil.isEmpty(from)) { - return new PageResult<>(from.getTotal()); - } - return new PageResult<>(convertList(from.getList(), func), from.getTotal()); - } - - public static <T, U> List<U> convertListByFlatMap(Collection<T> from, - Function<T, ? extends Stream<? extends U>> func) { - if (CollUtil.isEmpty(from)) { - return new ArrayList<>(); - } - return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); - } - - public static <T, U, R> List<R> convertListByFlatMap(Collection<T> from, - Function<? super T, ? extends U> mapper, - Function<U, ? extends Stream<? extends R>> func) { - if (CollUtil.isEmpty(from)) { - return new ArrayList<>(); - } - return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); - } - - public static <K, V> List<V> mergeValuesFromMap(Map<K, List<V>> map) { - return map.values() - .stream() - .flatMap(List::stream) - .collect(Collectors.toList()); - } - - public static <T> Set<T> convertSet(Collection<T> from) { - return convertSet(from, v -> v); - } - - public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) { - if (CollUtil.isEmpty(from)) { - return new HashSet<>(); - } - return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet()); - } - - public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func, Predicate<T> filter) { - if (CollUtil.isEmpty(from)) { - return new HashSet<>(); - } - return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet()); - } - - public static <T, K> Map<K, T> convertMapByFilter(Collection<T> from, Predicate<T> filter, Function<T, K> keyFunc) { - if (CollUtil.isEmpty(from)) { - return new HashMap<>(); - } - return from.stream().filter(filter).collect(Collectors.toMap(keyFunc, v -> v)); - } - - public static <T, U> Set<U> convertSetByFlatMap(Collection<T> from, - Function<T, ? extends Stream<? extends U>> func) { - if (CollUtil.isEmpty(from)) { - return new HashSet<>(); - } - return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); - } - - public static <T, U, R> Set<R> convertSetByFlatMap(Collection<T> from, - Function<? super T, ? extends U> mapper, - Function<U, ? extends Stream<? extends R>> func) { - if (CollUtil.isEmpty(from)) { - return new HashSet<>(); - } - return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); - } - - public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) { - if (CollUtil.isEmpty(from)) { - return new HashMap<>(); - } - return convertMap(from, keyFunc, Function.identity()); - } - - public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc, Supplier<? extends Map<K, T>> supplier) { - if (CollUtil.isEmpty(from)) { - return supplier.get(); - } - return convertMap(from, keyFunc, Function.identity(), supplier); - } - - public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) { - if (CollUtil.isEmpty(from)) { - return new HashMap<>(); - } - return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1); - } - - public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction) { - if (CollUtil.isEmpty(from)) { - return new HashMap<>(); - } - return convertMap(from, keyFunc, valueFunc, mergeFunction, HashMap::new); - } - - public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, Supplier<? extends Map<K, V>> supplier) { - if (CollUtil.isEmpty(from)) { - return supplier.get(); - } - return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1, supplier); - } - - public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction, Supplier<? extends Map<K, V>> supplier) { - if (CollUtil.isEmpty(from)) { - return new HashMap<>(); - } - return from.stream().collect(Collectors.toMap(keyFunc, valueFunc, mergeFunction, supplier)); - } - - public static <T, K> Map<K, List<T>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc) { - if (CollUtil.isEmpty(from)) { - return new HashMap<>(); - } - return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(t -> t, Collectors.toList()))); - } - - public static <T, K, V> Map<K, List<V>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) { - if (CollUtil.isEmpty(from)) { - return new HashMap<>(); - } - return from.stream() - .collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toList()))); - } - - // 暂时没想好名字,先以 2 结尾噶 - public static <T, K, V> Map<K, Set<V>> convertMultiMap2(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) { - if (CollUtil.isEmpty(from)) { - return new HashMap<>(); - } - return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet()))); - } - - public static <T, K> Map<K, T> convertImmutableMap(Collection<T> from, Function<T, K> keyFunc) { - if (CollUtil.isEmpty(from)) { - return Collections.emptyMap(); - } - ImmutableMap.Builder<K, T> builder = ImmutableMap.builder(); - from.forEach(item -> builder.put(keyFunc.apply(item), item)); - return builder.build(); - } - - /** - * 对比老、新两个列表,找出新增、修改、删除的数据 - * - * @param oldList 老列表 - * @param newList 新列表 - * @param sameFunc 对比函数,返回 true 表示相同,返回 false 表示不同 - * 注意,same 是通过每个元素的“标识”,判断它们是不是同一个数据 - * @return [新增列表、修改列表、删除列表] - */ - public static <T> List<List<T>> diffList(Collection<T> oldList, Collection<T> newList, - BiFunction<T, T, Boolean> sameFunc) { - List<T> createList = new LinkedList<>(newList); // 默认都认为是新增的,后续会进行移除 - List<T> updateList = new ArrayList<>(); - List<T> deleteList = new ArrayList<>(); - - // 通过以 oldList 为主遍历,找出 updateList 和 deleteList - for (T oldObj : oldList) { - // 1. 寻找是否有匹配的 - T foundObj = null; - for (Iterator<T> iterator = createList.iterator(); iterator.hasNext(); ) { - T newObj = iterator.next(); - // 1.1 不匹配,则直接跳过 - if (!sameFunc.apply(oldObj, newObj)) { - continue; - } - // 1.2 匹配,则移除,并结束寻找 - iterator.remove(); - foundObj = newObj; - break; - } - // 2. 匹配添加到 updateList;不匹配则添加到 deleteList 中 - if (foundObj != null) { - updateList.add(foundObj); - } else { - deleteList.add(oldObj); - } - } - return asList(createList, updateList, deleteList); - } - - public static boolean containsAny(Collection<?> source, Collection<?> candidates) { - return org.springframework.util.CollectionUtils.containsAny(source, candidates); - } - - public static <T> T getFirst(List<T> from) { - return !CollectionUtil.isEmpty(from) ? from.get(0) : null; - } - - public static <T> T findFirst(Collection<T> from, Predicate<T> predicate) { - return findFirst(from, predicate, Function.identity()); - } - - public static <T, U> U findFirst(Collection<T> from, Predicate<T> predicate, Function<T, U> func) { - if (CollUtil.isEmpty(from)) { - return null; - } - return from.stream().filter(predicate).findFirst().map(func).orElse(null); - } - - public static <T, V extends Comparable<? super V>> V getMaxValue(Collection<T> from, Function<T, V> valueFunc) { - if (CollUtil.isEmpty(from)) { - return null; - } - assert !from.isEmpty(); // 断言,避免告警 - T t = from.stream().max(Comparator.comparing(valueFunc)).get(); - return valueFunc.apply(t); - } - - public static <T, V extends Comparable<? super V>> V getMinValue(List<T> from, Function<T, V> valueFunc) { - if (CollUtil.isEmpty(from)) { - return null; - } - assert from.size() > 0; // 断言,避免告警 - T t = from.stream().min(Comparator.comparing(valueFunc)).get(); - return valueFunc.apply(t); - } - - public static <T, V extends Comparable<? super V>> T getMinObject(List<T> from, Function<T, V> valueFunc) { - if (CollUtil.isEmpty(from)) { - return null; - } - assert from.size() > 0; // 断言,避免告警 - return from.stream().min(Comparator.comparing(valueFunc)).get(); - } - - public static <T, V extends Comparable<? super V>> V getSumValue(Collection<T> from, Function<T, V> valueFunc, - BinaryOperator<V> accumulator) { - return getSumValue(from, valueFunc, accumulator, null); - } - - public static <T, V extends Comparable<? super V>> V getSumValue(Collection<T> from, Function<T, V> valueFunc, - BinaryOperator<V> accumulator, V defaultValue) { - if (CollUtil.isEmpty(from)) { - return defaultValue; - } - assert !from.isEmpty(); // 断言,避免告警 - return from.stream().map(valueFunc).filter(Objects::nonNull).reduce(accumulator).orElse(defaultValue); - } - - public static <T> void addIfNotNull(Collection<T> coll, T item) { - if (item == null) { - return; - } - coll.add(item); - } - - public static <T> Collection<T> singleton(T obj) { - return obj == null ? Collections.emptyList() : Collections.singleton(obj); - } - - public static <T> List<T> newArrayList(List<List<T>> list) { - return list.stream().filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList()); - } - -} \ No newline at end of file diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/collection/MapUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/collection/MapUtils.java deleted file mode 100644 index 921dcbe..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/collection/MapUtils.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.iailab.framework.common.util.collection; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.util.ObjUtil; -import com.iailab.framework.common.core.KeyValue; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -/** - * Map 工具类 - * - * @author iailab - */ -public class MapUtils { - - /** - * 从哈希表表中,获得 keys 对应的所有 value 数组 - * - * @param multimap 哈希表 - * @param keys keys - * @return value 数组 - */ - public static <K, V> List<V> getList(Multimap<K, V> multimap, Collection<K> keys) { - List<V> result = new ArrayList<>(); - keys.forEach(k -> { - Collection<V> values = multimap.get(k); - if (CollectionUtil.isEmpty(values)) { - return; - } - result.addAll(values); - }); - return result; - } - - /** - * 从哈希表查找到 key 对应的 value,然后进一步处理 - * key 为 null 时, 不处理 - * 注意,如果查找到的 value 为 null 时,不进行处理 - * - * @param map 哈希表 - * @param key key - * @param consumer 进一步处理的逻辑 - */ - public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) { - if (ObjUtil.isNull(key) || CollUtil.isEmpty(map)) { - return; - } - V value = map.get(key); - if (value == null) { - return; - } - consumer.accept(value); - } - - public static <K, V> Map<K, V> convertMap(List<KeyValue<K, V>> keyValues) { - Map<K, V> map = Maps.newLinkedHashMapWithExpectedSize(keyValues.size()); - keyValues.forEach(keyValue -> map.put(keyValue.getKey(), keyValue.getValue())); - return map; - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/collection/SetUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/collection/SetUtils.java deleted file mode 100644 index 8777ee6..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/collection/SetUtils.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.iailab.framework.common.util.collection; - -import cn.hutool.core.collection.CollUtil; - -import java.util.Set; - -/** - * Set 工具类 - * - * @author iailab - */ -public class SetUtils { - - @SafeVarargs - public static <T> Set<T> asSet(T... objs) { - return CollUtil.newHashSet(objs); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/date/DateUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/date/DateUtils.java deleted file mode 100644 index 39f8a7e..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/date/DateUtils.java +++ /dev/null @@ -1,304 +0,0 @@ -package com.iailab.framework.common.util.date; - -import cn.hutool.core.date.LocalDateTimeUtil; -import org.joda.time.DateTime; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.time.*; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.List; - -/** - * 时间工具类 - * - * @author iailab - */ -public class DateUtils { - - /** - * 时区 - 默认 - */ - public static final String TIME_ZONE_DEFAULT = "GMT+8"; - - /** - * 秒转换成毫秒 - */ - public static final long SECOND_MILLIS = 1000; - - public static final String FORMAT_YEAR_MONTH_DAY = "yyyy-MM-dd"; - /** 时间格式(yyyy.MM.dd) */ - public final static String DATE_PATTERN_POINT = "yyyy.MM.dd"; - - public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss"; - - public final static String DATE_TIME_PATTERN_STRING = "yyyyMMddHHmmss"; - - public static final String FORMAT_SIMPLE_TIME = "HH:mm"; - - /** - * 日期格式化 日期格式为:yyyy-MM-dd - * @param date 日期 - * @return 返回yyyy-MM-dd格式日期 - */ - public static String format(Date date) { - return format(date, FORMAT_YEAR_MONTH_DAY); - } - - /** - * 日期格式化 日期格式为:yyyy-MM-dd - * @param date 日期 - * @param pattern 格式,如:DateUtils.DATE_TIME_PATTERN - * @return 返回yyyy-MM-dd格式日期 - */ - public static String format(Date date, String pattern) { - if(date != null){ - SimpleDateFormat df = new SimpleDateFormat(pattern); - return df.format(date); - } - return null; - } - - /** - * 日期解析 - * @param date 日期 - * @param pattern 格式,如:DateUtils.DATE_TIME_PATTERN - * @return 返回Date - */ - public static Date parse(String date, String pattern) { - try { - return new SimpleDateFormat(pattern).parse(date); - } catch (ParseException e) { - e.printStackTrace(); - } - return null; - } - - /** - * 将 LocalDateTime 转换成 Date - * - * @param date LocalDateTime - * @return LocalDateTime - */ - public static Date of(LocalDateTime date) { - if (date == null) { - return null; - } - // 将此日期时间与时区相结合以创建 ZonedDateTime - ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault()); - // 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳 - Instant instant = zonedDateTime.toInstant(); - // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间 - return Date.from(instant); - } - - /** - * 将 Date 转换成 LocalDateTime - * - * @param date Date - * @return LocalDateTime - */ - public static LocalDateTime of(Date date) { - if (date == null) { - return null; - } - // 转为时间戳 - Instant instant = date.toInstant(); - // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间 - return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); - } - - public static Date addTime(Duration duration) { - return new Date(System.currentTimeMillis() + duration.toMillis()); - } - - public static boolean isExpired(LocalDateTime time) { - LocalDateTime now = LocalDateTime.now(); - return now.isAfter(time); - } - - /** - * 创建指定时间 - * - * @param year 年 - * @param mouth 月 - * @param day 日 - * @return 指定时间 - */ - public static Date buildTime(int year, int mouth, int day) { - return buildTime(year, mouth, day, 0, 0, 0); - } - - /** - * 创建指定时间 - * - * @param year 年 - * @param mouth 月 - * @param day 日 - * @param hour 小时 - * @param minute 分钟 - * @param second 秒 - * @return 指定时间 - */ - public static Date buildTime(int year, int mouth, int day, - int hour, int minute, int second) { - Calendar calendar = Calendar.getInstance(); - calendar.set(Calendar.YEAR, year); - calendar.set(Calendar.MONTH, mouth - 1); - calendar.set(Calendar.DAY_OF_MONTH, day); - calendar.set(Calendar.HOUR_OF_DAY, hour); - calendar.set(Calendar.MINUTE, minute); - calendar.set(Calendar.SECOND, second); - calendar.set(Calendar.MILLISECOND, 0); // 一般情况下,都是 0 毫秒 - return calendar.getTime(); - } - - public static Date max(Date a, Date b) { - if (a == null) { - return b; - } - if (b == null) { - return a; - } - return a.compareTo(b) > 0 ? a : b; - } - - public static LocalDateTime max(LocalDateTime a, LocalDateTime b) { - if (a == null) { - return b; - } - if (b == null) { - return a; - } - return a.isAfter(b) ? a : b; - } - - /** - * 是否今天 - * - * @param date 日期 - * @return 是否 - */ - public static boolean isToday(LocalDateTime date) { - return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now()); - } - - /** - * 是否昨天 - * - * @param date 日期 - * @return 是否 - */ - public static boolean isYesterday(LocalDateTime date) { - return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now().minusDays(1)); - } - - public static List<String> getTimeScale(Date startDate, Date endDate, int seconds) { - List<String> days = new ArrayList<String>(); - Calendar calendar = Calendar.getInstance(); - calendar.setTime(startDate); - while (calendar.getTime().compareTo(endDate) <= 0) { - days.add(DateUtils.format(calendar.getTime(), FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)); - calendar.add(Calendar.SECOND, seconds); - } - return days; - } - - public static List<String> getTimeScale(Date startDate, Date endDate, int seconds, String timeFormat) { - List<String> days = new ArrayList<String>(); - Calendar calendar = Calendar.getInstance(); - calendar.setTime(startDate); - while (calendar.getTime().compareTo(endDate) <= 0) { - days.add(DateUtils.format(calendar.getTime(), timeFormat)); - calendar.add(Calendar.SECOND, seconds); - } - return days; - } - - /** - * 对日期的【秒】进行加/减 - * - * @param date 日期 - * @param seconds 秒数,负数为减 - * @return 加/减几秒后的日期 - */ - public static Date addDateSeconds(Date date, int seconds) { - DateTime dateTime = new DateTime(date); - return dateTime.plusSeconds(seconds).toDate(); - } - - /** - * 对日期的【分钟】进行加/减 - * - * @param date 日期 - * @param minutes 分钟数,负数为减 - * @return 加/减几分钟后的日期 - */ - public static Date addDateMinutes(Date date, int minutes) { - DateTime dateTime = new DateTime(date); - return dateTime.plusMinutes(minutes).toDate(); - } - - /** - * 对日期的【小时】进行加/减 - * - * @param date 日期 - * @param hours 小时数,负数为减 - * @return 加/减几小时后的日期 - */ - public static Date addDateHours(Date date, int hours) { - DateTime dateTime = new DateTime(date); - return dateTime.plusHours(hours).toDate(); - } - - /** - * 对日期的【天】进行加/减 - * - * @param date 日期 - * @param days 天数,负数为减 - * @return 加/减几天后的日期 - */ - public static Date addDateDays(Date date, int days) { - DateTime dateTime = new DateTime(date); - return dateTime.plusDays(days).toDate(); - } - - /** - * 对日期的【周】进行加/减 - * - * @param date 日期 - * @param weeks 周数,负数为减 - * @return 加/减几周后的日期 - */ - public static Date addDateWeeks(Date date, int weeks) { - DateTime dateTime = new DateTime(date); - return dateTime.plusWeeks(weeks).toDate(); - } - - /** - * 对日期的【月】进行加/减 - * - * @param date 日期 - * @param months 月数,负数为减 - * @return 加/减几月后的日期 - */ - public static Date addDateMonths(Date date, int months) { - DateTime dateTime = new DateTime(date); - return dateTime.plusMonths(months).toDate(); - } - - /** - * 对日期的【年】进行加/减 - * - * @param date 日期 - * @param years 年数,负数为减 - * @return 加/减几年后的日期 - */ - public static Date addDateYears(Date date, int years) { - DateTime dateTime = new DateTime(date); - return dateTime.plusYears(years).toDate(); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/date/LocalDateTimeUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/date/LocalDateTimeUtils.java deleted file mode 100644 index a54b17c..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/date/LocalDateTimeUtils.java +++ /dev/null @@ -1,309 +0,0 @@ -package com.iailab.framework.common.util.date; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.date.DatePattern; -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.enums.DateIntervalEnum; - -import java.time.*; -import java.time.format.DateTimeParseException; -import java.time.temporal.ChronoUnit; -import java.time.temporal.TemporalAdjusters; -import java.util.ArrayList; -import java.util.List; - -/** - * 时间工具类,用于 {@link java.time.LocalDateTime} - * - * @author iailab - */ -public class LocalDateTimeUtils { - - /** - * 空的 LocalDateTime 对象,主要用于 DB 唯一索引的默认值 - */ - public static LocalDateTime EMPTY = buildTime(1970, 1, 1); - - /** - * 解析时间 - * - * 相比 {@link LocalDateTimeUtil#parse(CharSequence)} 方法来说,会尽量去解析,直到成功 - * - * @param time 时间 - * @return 时间字符串 - */ - public static LocalDateTime parse(String time) { - try { - return LocalDateTimeUtil.parse(time, DatePattern.NORM_DATE_PATTERN); - } catch (DateTimeParseException e) { - return LocalDateTimeUtil.parse(time); - } - } - - public static LocalDateTime addTime(Duration duration) { - return LocalDateTime.now().plus(duration); - } - - public static LocalDateTime minusTime(Duration duration) { - return LocalDateTime.now().minus(duration); - } - - public static boolean beforeNow(LocalDateTime date) { - return date.isBefore(LocalDateTime.now()); - } - - public static boolean afterNow(LocalDateTime date) { - return date.isAfter(LocalDateTime.now()); - } - - /** - * 创建指定时间 - * - * @param year 年 - * @param mouth 月 - * @param day 日 - * @return 指定时间 - */ - public static LocalDateTime buildTime(int year, int mouth, int day) { - return LocalDateTime.of(year, mouth, day, 0, 0, 0); - } - - public static LocalDateTime[] buildBetweenTime(int year1, int mouth1, int day1, - int year2, int mouth2, int day2) { - return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)}; - } - - /** - * 判指定断时间,是否在该时间范围内 - * - * @param startTime 开始时间 - * @param endTime 结束时间 - * @param time 指定时间 - * @return 是否 - */ - public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) { - if (startTime == null || endTime == null || time == null) { - return false; - } - return LocalDateTimeUtil.isIn(parse(time), startTime, endTime); - } - - /** - * 判断当前时间是否在该时间范围内 - * - * @param startTime 开始时间 - * @param endTime 结束时间 - * @return 是否 - */ - public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime) { - if (startTime == null || endTime == null) { - return false; - } - return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime); - } - - /** - * 判断当前时间是否在该时间范围内 - * - * @param startTime 开始时间 - * @param endTime 结束时间 - * @return 是否 - */ - public static boolean isBetween(String startTime, String endTime) { - if (startTime == null || endTime == null) { - return false; - } - LocalDate nowDate = LocalDate.now(); - return LocalDateTimeUtil.isIn(LocalDateTime.now(), - LocalDateTime.of(nowDate, LocalTime.parse(startTime)), - LocalDateTime.of(nowDate, LocalTime.parse(endTime))); - } - - /** - * 判断时间段是否重叠 - * - * @param startTime1 开始 time1 - * @param endTime1 结束 time1 - * @param startTime2 开始 time2 - * @param endTime2 结束 time2 - * @return 重叠:true 不重叠:false - */ - public static boolean isOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) { - LocalDate nowDate = LocalDate.now(); - return LocalDateTimeUtil.isOverlap(LocalDateTime.of(nowDate, startTime1), LocalDateTime.of(nowDate, endTime1), - LocalDateTime.of(nowDate, startTime2), LocalDateTime.of(nowDate, endTime2)); - } - - /** - * 获取指定日期所在的月份的开始时间 - * 例如:2023-09-30 00:00:00,000 - * - * @param date 日期 - * @return 月份的开始时间 - */ - public static LocalDateTime beginOfMonth(LocalDateTime date) { - return date.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN); - } - - /** - * 获取指定日期所在的月份的最后时间 - * 例如:2023-09-30 23:59:59,999 - * - * @param date 日期 - * @return 月份的结束时间 - */ - public static LocalDateTime endOfMonth(LocalDateTime date) { - return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX); - } - - /** - * 获得指定日期所在季度 - * - * @param date 日期 - * @return 所在季度 - */ - public static int getQuarterOfYear(LocalDateTime date) { - return (date.getMonthValue() - 1) / 3 + 1; - } - - /** - * 获取指定日期到现在过了几天,如果指定日期在当前日期之后,获取结果为负 - * - * @param dateTime 日期 - * @return 相差天数 - */ - public static Long between(LocalDateTime dateTime) { - return LocalDateTimeUtil.between(dateTime, LocalDateTime.now(), ChronoUnit.DAYS); - } - - /** - * 获取今天的开始时间 - * - * @return 今天 - */ - public static LocalDateTime getToday() { - return LocalDateTimeUtil.beginOfDay(LocalDateTime.now()); - } - - /** - * 获取昨天的开始时间 - * - * @return 昨天 - */ - public static LocalDateTime getYesterday() { - return LocalDateTimeUtil.beginOfDay(LocalDateTime.now().minusDays(1)); - } - - /** - * 获取本月的开始时间 - * - * @return 本月 - */ - public static LocalDateTime getMonth() { - return beginOfMonth(LocalDateTime.now()); - } - - /** - * 获取本年的开始时间 - * - * @return 本年 - */ - public static LocalDateTime getYear() { - return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN); - } - - public static List<LocalDateTime[]> getDateRangeList(LocalDateTime startTime, - LocalDateTime endTime, - Integer interval) { - // 1.1 找到枚举 - DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval); - Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval); - // 1.2 将时间对齐 - startTime = LocalDateTimeUtil.beginOfDay(startTime); - endTime = LocalDateTimeUtil.endOfDay(endTime); - - // 2. 循环,生成时间范围 - List<LocalDateTime[]> timeRanges = new ArrayList<>(); - switch (intervalEnum) { - case DAY: - while (startTime.isBefore(endTime)) { - timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)}); - startTime = startTime.plusDays(1); - } - break; - case WEEK: - while (startTime.isBefore(endTime)) { - LocalDateTime endOfWeek = startTime.with(DayOfWeek.SUNDAY).plusDays(1).minusNanos(1); - timeRanges.add(new LocalDateTime[]{startTime, endOfWeek}); - startTime = endOfWeek.plusNanos(1); - } - break; - case MONTH: - while (startTime.isBefore(endTime)) { - LocalDateTime endOfMonth = startTime.with(TemporalAdjusters.lastDayOfMonth()).plusDays(1).minusNanos(1); - timeRanges.add(new LocalDateTime[]{startTime, endOfMonth}); - startTime = endOfMonth.plusNanos(1); - } - break; - case QUARTER: - while (startTime.isBefore(endTime)) { - int quarterOfYear = getQuarterOfYear(startTime); - LocalDateTime quarterEnd = quarterOfYear == 4 - ? startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1) - : startTime.withMonth(quarterOfYear * 3 + 1).withDayOfMonth(1).minusNanos(1); - timeRanges.add(new LocalDateTime[]{startTime, quarterEnd}); - startTime = quarterEnd.plusNanos(1); - } - break; - case YEAR: - while (startTime.isBefore(endTime)) { - LocalDateTime endOfYear = startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1); - timeRanges.add(new LocalDateTime[]{startTime, endOfYear}); - startTime = endOfYear.plusNanos(1); - } - break; - default: - throw new IllegalArgumentException("Invalid interval: " + interval); - } - // 3. 兜底,最后一个时间,需要保持在 endTime 之前 - LocalDateTime[] lastTimeRange = CollUtil.getLast(timeRanges); - if (lastTimeRange != null) { - lastTimeRange[1] = endTime; - } - return timeRanges; - } - - /** - * 格式化时间范围 - * - * @param startTime 开始时间 - * @param endTime 结束时间 - * @param interval 时间间隔 - * @return 时间范围 - */ - public static String formatDateRange(LocalDateTime startTime, LocalDateTime endTime, Integer interval) { - // 1. 找到枚举 - DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval); - Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval); - - // 2. 循环,生成时间范围 - switch (intervalEnum) { - case DAY: - return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN); - case WEEK: - return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN) - + StrUtil.format("(第 {} 周)", LocalDateTimeUtil.weekOfYear(startTime)); - case MONTH: - return LocalDateTimeUtil.format(startTime, DatePattern.NORM_MONTH_PATTERN); - case QUARTER: - return StrUtil.format("{}-Q{}", startTime.getYear(), getQuarterOfYear(startTime)); - case YEAR: - return LocalDateTimeUtil.format(startTime, DatePattern.NORM_YEAR_PATTERN); - default: - throw new IllegalArgumentException("Invalid interval: " + interval); - } - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/http/HttpUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/http/HttpUtils.java deleted file mode 100644 index 1728905..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/http/HttpUtils.java +++ /dev/null @@ -1,385 +0,0 @@ -package com.iailab.framework.common.util.http; - -import cn.hutool.core.codec.Base64; -import cn.hutool.core.map.TableMap; -import cn.hutool.core.net.url.UrlBuilder; -import cn.hutool.core.util.ReflectUtil; -import cn.hutool.core.util.StrUtil; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.servlet.http.HttpServletRequest; -import java.io.*; -import java.net.HttpURLConnection; -import java.net.URI; -import java.net.URL; -import java.net.URLConnection; -import java.nio.charset.Charset; -import java.util.List; -import java.util.Map; - -/** - * HTTP 工具类 - * - * @author iailab - */ -public class HttpUtils { - - @SuppressWarnings("unchecked") - public static String replaceUrlQuery(String url, String key, String value) { - UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset()); - // 先移除 - TableMap<CharSequence, CharSequence> query = (TableMap<CharSequence, CharSequence>) - ReflectUtil.getFieldValue(builder.getQuery(), "query"); - query.remove(key); - // 后添加 - builder.addQuery(key, value); - return builder.build(); - } - - private String append(String base, Map<String, ?> query, boolean fragment) { - return append(base, query, null, fragment); - } - - /** - * 拼接 URL - * - * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 append 方法 - * - * @param base 基础 URL - * @param query 查询参数 - * @param keys query 的 key,对应的原本的 key 的映射。例如说 query 里有个 key 是 xx,实际它的 key 是 extra_xx,则通过 keys 里添加这个映射 - * @param fragment URL 的 fragment,即拼接到 # 中 - * @return 拼接后的 URL - */ - public static String append(String base, Map<String, ?> query, Map<String, String> keys, boolean fragment) { - UriComponentsBuilder template = UriComponentsBuilder.newInstance(); - UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(base); - URI redirectUri; - try { - // assume it's encoded to start with (if it came in over the wire) - redirectUri = builder.build(true).toUri(); - } catch (Exception e) { - // ... but allow client registrations to contain hard-coded non-encoded values - redirectUri = builder.build().toUri(); - builder = UriComponentsBuilder.fromUri(redirectUri); - } - template.scheme(redirectUri.getScheme()).port(redirectUri.getPort()).host(redirectUri.getHost()) - .userInfo(redirectUri.getUserInfo()).path(redirectUri.getPath()); - - if (fragment) { - StringBuilder values = new StringBuilder(); - if (redirectUri.getFragment() != null) { - String append = redirectUri.getFragment(); - values.append(append); - } - for (String key : query.keySet()) { - if (values.length() > 0) { - values.append("&"); - } - String name = key; - if (keys != null && keys.containsKey(key)) { - name = keys.get(key); - } - values.append(name).append("={").append(key).append("}"); - } - if (values.length() > 0) { - template.fragment(values.toString()); - } - UriComponents encoded = template.build().expand(query).encode(); - builder.fragment(encoded.getFragment()); - } else { - for (String key : query.keySet()) { - String name = key; - if (keys != null && keys.containsKey(key)) { - name = keys.get(key); - } - template.queryParam(name, "{" + key + "}"); - } - template.fragment(redirectUri.getFragment()); - UriComponents encoded = template.build().expand(query).encode(); - builder.query(encoded.getQuery()); - } - return builder.build().toUriString(); - } - - public static String[] obtainBasicAuthorization(HttpServletRequest request) { - String clientId; - String clientSecret; - // 先从 Header 中获取 - String authorization = request.getHeader("Authorization"); - authorization = StrUtil.subAfter(authorization, "Basic ", true); - if (StringUtils.hasText(authorization)) { - authorization = Base64.decodeStr(authorization); - clientId = StrUtil.subBefore(authorization, ":", false); - clientSecret = StrUtil.subAfter(authorization, ":", false); - // 再从 Param 中获取 - } else { - clientId = request.getParameter("client_id"); - clientSecret = request.getParameter("client_secret"); - } - - // 如果两者非空,则返回 - if (StrUtil.isNotEmpty(clientId) && StrUtil.isNotEmpty(clientSecret)) { - return new String[]{clientId, clientSecret}; - } - return null; - } - - /** - * 向指定URL发送GET方法的请求 - * - * @param url 发送请求的URL - * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 - * @return URL 所代表远程资源的响应结果 - */ - public static String sendGet(String url, String param) { - String result = ""; - BufferedReader in = null; - try { - String urlNameString = url + "?" + param; - URL realUrl = new URL(urlNameString); - // 打开和URL之间的连接 - URLConnection connection = realUrl.openConnection(); - // 设置通用的请求属性 - connection.setRequestProperty("accept", "*/*"); - connection.setRequestProperty("connection", "Keep-Alive"); - connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); - - // 建立实际的连接 - connection.connect(); - // 获取所有响应头字段 - Map<String, List<String>> map = connection.getHeaderFields(); - // 遍历所有的响应头字段 - for (String key : map.keySet()) { - System.out.println(key + "--->" + map.get(key)); - } - // 定义 BufferedReader输入流来读取URL的响应 - in = new BufferedReader(new InputStreamReader(connection.getInputStream())); - String line; - while ((line = in.readLine()) != null) { - result += line; - } - } catch (Exception e) { - System.out.println("发送GET请求出现异常!" + e); - e.printStackTrace(); - } - // 使用finally块来关闭输入流 - finally { - try { - if (in != null) { - in.close(); - } - } catch (Exception e2) { - e2.printStackTrace(); - } - } - return result; - } - - public static String sendGet(String url, Map<String, String> params, String authorization) { - String result = ""; - BufferedReader in = null; - try { - StringBuilder sb = new StringBuilder(); - sb.append(url); - if (!CollectionUtils.isEmpty(params)) { - sb.append("?"); - params.forEach((k, v) -> { - sb.append(k + "=" + v + "&"); - }); - sb.append("t=" + System.currentTimeMillis()); - } - String urlNameString = sb.toString(); - URL realUrl = new URL(urlNameString); - // 打开和URL之间的连接 - URLConnection connection = realUrl.openConnection(); - // 设置通用的请求属性 - connection.setRequestProperty("Authorization", authorization); - connection.setRequestProperty("accept", "*/*"); - connection.setRequestProperty("connection", "Keep-Alive"); - connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); - - // 建立实际的连接 - connection.connect(); - // 获取所有响应头字段 - Map<String, List<String>> map = connection.getHeaderFields(); - // 遍历所有的响应头字段 - for (String key : map.keySet()) { - System.out.println(key + "--->" + map.get(key)); - } - // 定义 BufferedReader输入流来读取URL的响应 - in = new BufferedReader(new InputStreamReader(connection.getInputStream())); - String line; - while ((line = in.readLine()) != null) { - result += line; - } - } catch (Exception e) { - System.out.println("发送GET请求出现异常!" + e); - e.printStackTrace(); - } - // 使用finally块来关闭输入流 - finally { - try { - if (in != null) { - in.close(); - } - } catch (Exception e2) { - e2.printStackTrace(); - } - } - return result; - } - - /** - * 向指定 URL 发送POST方法的请求 - * - * @param url - * @param json - * @return - */ - public static String sendPost(String url, String json) { - PrintWriter out = null; - BufferedReader in = null; - String result = ""; - try { - URL realUrl = new URL(url); - // 打开和URL之间的连接 - URLConnection conn = realUrl.openConnection(); - // 设置通用的请求属性 - conn.setRequestProperty("content-type", "application/json"); - conn.setRequestProperty("accept", "*/*"); - conn.setRequestProperty("connection", "Keep-Alive"); - conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); - - // 发送POST请求必须设置如下两行 - conn.setDoOutput(true); - conn.setDoInput(true); - // 获取URLConnection对象对应的输出流 - out = new PrintWriter(conn.getOutputStream()); - // 发送请求参数 - out.print(json); - // flush输出流的缓冲 - out.flush(); - // 定义BufferedReader输入流来读取URL的响应 - in = new BufferedReader(new InputStreamReader(conn.getInputStream())); - String line; - while ((line = in.readLine()) != null) { - result += line; - } - } catch (Exception e) { - System.out.println("发送 POST 请求出现异常!" + e); - e.printStackTrace(); - } - //使用finally块来关闭输出流、输入流 - finally { - try { - if (out != null) { - out.close(); - } - if (in != null) { - in.close(); - } - } catch (IOException ex) { - ex.printStackTrace(); - } - } - return result; - } - - /** - * 向指定 URL 发送POST方法的请求 - * - * @param url - * @param json - * @param authorization - * @return - */ - public static String sendPost(String url, String json, String authorization) { - PrintWriter out = null; - BufferedReader in = null; - String result = ""; - try { - URL realUrl = new URL(url); - // 打开和URL之间的连接 - URLConnection conn = realUrl.openConnection(); - // 设置通用的请求属性 - conn.setRequestProperty("Authorization", authorization); - conn.setRequestProperty("accept", "*/*"); - conn.setRequestProperty("connection", "Keep-Alive"); - conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); - - // 发送POST请求必须设置如下两行 - conn.setDoOutput(true); - conn.setDoInput(true); - // 获取URLConnection对象对应的输出流 - out = new PrintWriter(conn.getOutputStream()); - // 发送请求参数 - out.print(json); - // flush输出流的缓冲 - out.flush(); - // 定义BufferedReader输入流来读取URL的响应 - in = new BufferedReader(new InputStreamReader(conn.getInputStream())); - String line; - while ((line = in.readLine()) != null) { - result += line; - } - } catch (Exception e) { - System.out.println("发送 POST 请求出现异常!" + e); - e.printStackTrace(); - } - //使用finally块来关闭输出流、输入流 - finally { - try { - if (out != null) { - out.close(); - } - if (in != null) { - in.close(); - } - } catch (IOException ex) { - ex.printStackTrace(); - } - } - return result; - } - - public static String sendPostToken(String url, String json, String authorization) { - String result = ""; - try { - URL realUrl = new URL(url); - HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection(); - connection.setRequestMethod("POST"); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setRequestProperty("Authorization", authorization); - connection.setDoOutput(true); - - // 发送POST请求的数据 - try (OutputStream os = connection.getOutputStream()) { - os.write(json.getBytes()); - os.flush(); - } - // 获取响应码和响应体 - int responseCode = connection.getResponseCode(); - if (responseCode == HttpURLConnection.HTTP_OK) { // 200 OK - try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { - StringBuilder response = new StringBuilder(); - String responseLine; - while ((responseLine = br.readLine()) != null) { - response.append(responseLine.trim()); - } - result = response.toString(); - } - } else { - System.out.println("POST request not worked"); - } - } catch (Exception e) { - e.printStackTrace(); - } - return result; - } - - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/io/FileUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/io/FileUtils.java deleted file mode 100644 index 5356e90..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/io/FileUtils.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.iailab.framework.common.util.io; - -import cn.hutool.core.io.FileTypeUtil; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.io.file.FileNameUtil; -import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.digest.DigestUtil; -import lombok.SneakyThrows; - -import java.io.ByteArrayInputStream; -import java.io.File; - -/** - * 文件工具类 - * - * @author iailab - */ -public class FileUtils { - - /** - * 创建临时文件 - * 该文件会在 JVM 退出时,进行删除 - * - * @param data 文件内容 - * @return 文件 - */ - @SneakyThrows - public static File createTempFile(String data) { - File file = createTempFile(); - // 写入内容 - FileUtil.writeUtf8String(data, file); - return file; - } - - /** - * 创建临时文件 - * 该文件会在 JVM 退出时,进行删除 - * - * @param data 文件内容 - * @return 文件 - */ - @SneakyThrows - public static File createTempFile(byte[] data) { - File file = createTempFile(); - // 写入内容 - FileUtil.writeBytes(data, file); - return file; - } - - /** - * 创建临时文件,无内容 - * 该文件会在 JVM 退出时,进行删除 - * - * @return 文件 - */ - @SneakyThrows - public static File createTempFile() { - // 创建文件,通过 UUID 保证唯一 - File file = File.createTempFile(IdUtil.simpleUUID(), null); - // 标记 JVM 退出时,自动删除 - file.deleteOnExit(); - return file; - } - - /** - * 生成文件路径 - * - * @param content 文件内容 - * @param originalName 原始文件名 - * @return path,唯一不可重复 - */ - public static String generatePath(byte[] content, String originalName) { - String sha256Hex = DigestUtil.sha256Hex(content); - // 情况一:如果存在 name,则优先使用 name 的后缀 - if (StrUtil.isNotBlank(originalName)) { - String extName = FileNameUtil.extName(originalName); - return StrUtil.isBlank(extName) ? sha256Hex : sha256Hex + "." + extName; - } - // 情况二:基于 content 计算 - return sha256Hex + '.' + FileTypeUtil.getType(new ByteArrayInputStream(content)); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/io/IoUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/io/IoUtils.java deleted file mode 100644 index be1d14c..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/io/IoUtils.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.framework.common.util.io; - -import cn.hutool.core.io.IORuntimeException; -import cn.hutool.core.io.IoUtil; -import cn.hutool.core.util.StrUtil; - -import java.io.InputStream; - -/** - * IO 工具类,用于 {@link cn.hutool.core.io.IoUtil} 缺失的方法 - * - * @author iailab - */ -public class IoUtils { - - /** - * 从流中读取 UTF8 编码的内容 - * - * @param in 输入流 - * @param isClose 是否关闭 - * @return 内容 - * @throws IORuntimeException IO 异常 - */ - public static String readUtf8(InputStream in, boolean isClose) throws IORuntimeException { - return StrUtil.utf8Str(IoUtil.read(in, isClose)); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/JsonUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/JsonUtils.java deleted file mode 100644 index 88e56cd..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/JsonUtils.java +++ /dev/null @@ -1,202 +0,0 @@ -package com.iailab.framework.common.util.json; - -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONUtil; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; - -import java.io.IOException; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; - -/** - * JSON 工具类 - * - * @author iailab - */ -@Slf4j -public class JsonUtils { - - private static ObjectMapper objectMapper = new ObjectMapper(); - - static { - objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 值 - objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化 - } - - /** - * 初始化 objectMapper 属性 - * <p> - * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean - * - * @param objectMapper ObjectMapper 对象 - */ - public static void init(ObjectMapper objectMapper) { - JsonUtils.objectMapper = objectMapper; - } - - @SneakyThrows - public static String toJsonString(Object object) { - return objectMapper.writeValueAsString(object); - } - - @SneakyThrows - public static byte[] toJsonByte(Object object) { - return objectMapper.writeValueAsBytes(object); - } - - @SneakyThrows - public static String toJsonPrettyString(Object object) { - return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object); - } - - public static <T> T parseObject(String text, Class<T> clazz) { - if (StrUtil.isEmpty(text)) { - return null; - } - try { - return objectMapper.readValue(text, clazz); - } catch (IOException e) { - log.error("json parse err,json:{}", text, e); - throw new RuntimeException(e); - } - } - - public static <T> T parseObject(String text, String path, Class<T> clazz) { - if (StrUtil.isEmpty(text)) { - return null; - } - try { - JsonNode treeNode = objectMapper.readTree(text); - JsonNode pathNode = treeNode.path(path); - return objectMapper.readValue(pathNode.toString(), clazz); - } catch (IOException e) { - log.error("json parse err,json:{}", text, e); - throw new RuntimeException(e); - } - } - - public static <T> T parseObject(String text, Type type) { - if (StrUtil.isEmpty(text)) { - return null; - } - try { - return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type)); - } catch (IOException e) { - log.error("json parse err,json:{}", text, e); - throw new RuntimeException(e); - } - } - - /** - * 将字符串解析成指定类型的对象 - * 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下, - * 如果 text 没有 class 属性,则会报错。此时,使用这个方法,可以解决。 - * - * @param text 字符串 - * @param clazz 类型 - * @return 对象 - */ - public static <T> T parseObject2(String text, Class<T> clazz) { - if (StrUtil.isEmpty(text)) { - return null; - } - return JSONUtil.toBean(text, clazz); - } - - public static <T> T parseObject(byte[] bytes, Class<T> clazz) { - if (ArrayUtil.isEmpty(bytes)) { - return null; - } - try { - return objectMapper.readValue(bytes, clazz); - } catch (IOException e) { - log.error("json parse err,json:{}", bytes, e); - throw new RuntimeException(e); - } - } - - public static <T> T parseObject(String text, TypeReference<T> typeReference) { - try { - return objectMapper.readValue(text, typeReference); - } catch (IOException e) { - log.error("json parse err,json:{}", text, e); - throw new RuntimeException(e); - } - } - - /** - * 解析 JSON 字符串成指定类型的对象,如果解析失败,则返回 null - * - * @param text 字符串 - * @param typeReference 类型引用 - * @return 指定类型的对象 - */ - public static <T> T parseObjectQuietly(String text, TypeReference<T> typeReference) { - try { - return objectMapper.readValue(text, typeReference); - } catch (IOException e) { - return null; - } - } - - public static <T> List<T> parseArray(String text, Class<T> clazz) { - if (StrUtil.isEmpty(text)) { - return new ArrayList<>(); - } - try { - return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz)); - } catch (IOException e) { - log.error("json parse err,json:{}", text, e); - throw new RuntimeException(e); - } - } - - public static <T> List<T> parseArray(String text, String path, Class<T> clazz) { - if (StrUtil.isEmpty(text)) { - return null; - } - try { - JsonNode treeNode = objectMapper.readTree(text); - JsonNode pathNode = treeNode.path(path); - return objectMapper.readValue(pathNode.toString(), objectMapper.getTypeFactory().constructCollectionType(List.class, clazz)); - } catch (IOException e) { - log.error("json parse err,json:{}", text, e); - throw new RuntimeException(e); - } - } - - public static JsonNode parseTree(String text) { - try { - return objectMapper.readTree(text); - } catch (IOException e) { - log.error("json parse err,json:{}", text, e); - throw new RuntimeException(e); - } - } - - public static JsonNode parseTree(byte[] text) { - try { - return objectMapper.readTree(text); - } catch (IOException e) { - log.error("json parse err,json:{}", text, e); - throw new RuntimeException(e); - } - } - - public static boolean isJson(String text) { - return JSONUtil.isTypeJSON(text); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/NumberSerializer.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/NumberSerializer.java deleted file mode 100644 index 19d0591..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/NumberSerializer.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.iailab.framework.common.util.json.databind; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; - -import java.io.IOException; - -/** - * Long 序列化规则 - * - * 会将超长 long 值转换为 string,解决前端 JavaScript 最大安全整数是 2^53-1 的问题 - * - * @author 星语 - */ -@JacksonStdImpl -public class NumberSerializer extends com.fasterxml.jackson.databind.ser.std.NumberSerializer { - - private static final long MAX_SAFE_INTEGER = 9007199254740991L; - private static final long MIN_SAFE_INTEGER = -9007199254740991L; - - public static final NumberSerializer INSTANCE = new NumberSerializer(Number.class); - - public NumberSerializer(Class<? extends Number> rawType) { - super(rawType); - } - - @Override - public void serialize(Number value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - // 超出范围 序列化位字符串 - if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) { - super.serialize(value, gen, serializers); - } else { - gen.writeString(value.toString()); - } - } -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java deleted file mode 100644 index d024c92..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.iailab.framework.common.util.json.databind; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; - -import java.io.IOException; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; - -/** - * 基于时间戳的 LocalDateTime 反序列化器 - * - * @author 老五 - */ -public class TimestampLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> { - - public static final TimestampLocalDateTimeDeserializer INSTANCE = new TimestampLocalDateTimeDeserializer(); - - @Override - public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - // 将 Long 时间戳,转换为 LocalDateTime 对象 - return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault()); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java deleted file mode 100644 index 6d43528..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.iailab.framework.common.util.json.databind; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; - -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.ZoneId; - -/** - * 基于时间戳的 LocalDateTime 序列化器 - * - * @author 老五 - */ -public class TimestampLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> { - - public static final TimestampLocalDateTimeSerializer INSTANCE = new TimestampLocalDateTimeSerializer(); - - @Override - public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - // 将 LocalDateTime 对象,转换为 Long 时间戳 - gen.writeNumber(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/monitor/TracerUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/monitor/TracerUtils.java deleted file mode 100644 index e285d44..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/monitor/TracerUtils.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.iailab.framework.common.util.monitor; - -import org.apache.skywalking.apm.toolkit.trace.TraceContext; - -/** - * 链路追踪工具类 - * - * 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 util 包下 - * - * @author iailab - */ -public class TracerUtils { - - /** - * 私有化构造方法 - */ - private TracerUtils() { - } - - /** - * 获得链路追踪编号,直接返回 SkyWalking 的 TraceId。 - * 如果不存在的话为空字符串!!! - * - * @return 链路追踪编号 - */ - public static String getTraceId() { - return TraceContext.traceId(); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/number/MoneyUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/number/MoneyUtils.java deleted file mode 100644 index eb435b7..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/number/MoneyUtils.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.iailab.framework.common.util.number; - -import cn.hutool.core.math.Money; -import cn.hutool.core.util.NumberUtil; - -import java.math.BigDecimal; -import java.math.RoundingMode; - -/** - * 金额工具类 - * - * @author iailab - */ -public class MoneyUtils { - - /** - * 金额的小数位数 - */ - private static final int PRICE_SCALE = 2; - - /** - * 百分比对应的 BigDecimal 对象 - */ - public static final BigDecimal PERCENT_100 = BigDecimal.valueOf(100); - - /** - * 计算百分比金额,四舍五入 - * - * @param price 金额 - * @param rate 百分比,例如说 56.77% 则传入 56.77 - * @return 百分比金额 - */ - public static Integer calculateRatePrice(Integer price, Double rate) { - return calculateRatePrice(price, rate, 0, RoundingMode.HALF_UP).intValue(); - } - - /** - * 计算百分比金额,向下传入 - * - * @param price 金额 - * @param rate 百分比,例如说 56.77% 则传入 56.77 - * @return 百分比金额 - */ - public static Integer calculateRatePriceFloor(Integer price, Double rate) { - return calculateRatePrice(price, rate, 0, RoundingMode.FLOOR).intValue(); - } - - /** - * 计算百分比金额 - * - * @param price 金额(单位分) - * @param count 数量 - * @param percent 折扣(单位分),列如 60.2%,则传入 6020 - * @return 商品总价 - */ - public static Integer calculator(Integer price, Integer count, Integer percent) { - price = price * count; - if (percent == null) { - return price; - } - return MoneyUtils.calculateRatePriceFloor(price, (double) (percent / 100)); - } - - /** - * 计算百分比金额 - * - * @param price 金额 - * @param rate 百分比,例如说 56.77% 则传入 56.77 - * @param scale 保留小数位数 - * @param roundingMode 舍入模式 - */ - public static BigDecimal calculateRatePrice(Number price, Number rate, int scale, RoundingMode roundingMode) { - return NumberUtil.toBigDecimal(price).multiply(NumberUtil.toBigDecimal(rate)) // 乘以 - .divide(BigDecimal.valueOf(100), scale, roundingMode); // 除以 100 - } - - /** - * 分转元 - * - * @param fen 分 - * @return 元 - */ - public static BigDecimal fenToYuan(int fen) { - return new Money(0, fen).getAmount(); - } - - /** - * 分转元(字符串) - * - * 例如说 fen 为 1 时,则结果为 0.01 - * - * @param fen 分 - * @return 元 - */ - public static String fenToYuanStr(int fen) { - return new Money(0, fen).toString(); - } - - /** - * 金额相乘,默认进行四舍五入 - * - * 位数:{@link #PRICE_SCALE} - * - * @param price 金额 - * @param count 数量 - * @return 金额相乘结果 - */ - public static BigDecimal priceMultiply(BigDecimal price, BigDecimal count) { - if (price == null || count == null) { - return null; - } - return price.multiply(count).setScale(PRICE_SCALE, RoundingMode.HALF_UP); - } - - /** - * 金额相乘(百分比),默认进行四舍五入 - * - * 位数:{@link #PRICE_SCALE} - * - * @param price 金额 - * @param percent 百分比 - * @return 金额相乘结果 - */ - public static BigDecimal priceMultiplyPercent(BigDecimal price, BigDecimal percent) { - if (price == null || percent == null) { - return null; - } - return price.multiply(percent).divide(PERCENT_100, PRICE_SCALE, RoundingMode.HALF_UP); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/number/NumberUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/number/NumberUtils.java deleted file mode 100644 index f45f673..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/number/NumberUtils.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.iailab.framework.common.util.number; - -import cn.hutool.core.util.NumberUtil; -import cn.hutool.core.util.StrUtil; - -import java.math.BigDecimal; - -/** - * 数字的工具类,补全 {@link cn.hutool.core.util.NumberUtil} 的功能 - * - * @author iailab - */ -public class NumberUtils { - - public static Long parseLong(String str) { - return StrUtil.isNotEmpty(str) ? Long.valueOf(str) : null; - } - - public static Integer parseInt(String str) { - return StrUtil.isNotEmpty(str) ? Integer.valueOf(str) : null; - } - - /** - * 通过经纬度获取地球上两点之间的距离 - * - * 参考 <<a href="https://gitee.com/dromara/hutool/blob/1caabb586b1f95aec66a21d039c5695df5e0f4c1/hutool-core/src/main/java/cn/hutool/core/util/DistanceUtil.java">DistanceUtil</a>> 实现,目前它已经被 hutool 删除 - * - * @param lat1 经度1 - * @param lng1 纬度1 - * @param lat2 经度2 - * @param lng2 纬度2 - * @return 距离,单位:千米 - */ - public static double getDistance(double lat1, double lng1, double lat2, double lng2) { - double radLat1 = lat1 * Math.PI / 180.0; - double radLat2 = lat2 * Math.PI / 180.0; - double a = radLat1 - radLat2; - double b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0; - double distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) - + Math.cos(radLat1) * Math.cos(radLat2) - * Math.pow(Math.sin(b / 2), 2))); - distance = distance * 6378.137; - distance = Math.round(distance * 10000d) / 10000d; - return distance; - } - - /** - * 提供精确的乘法运算 - * - * 和 hutool {@link NumberUtil#mul(BigDecimal...)} 的差别是,如果存在 null,则返回 null - * - * @param values 多个被乘值 - * @return 积 - */ - public static BigDecimal mul(BigDecimal... values) { - for (BigDecimal value : values) { - if (value == null) { - return null; - } - } - return NumberUtil.mul(values); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/object/BeanUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/object/BeanUtils.java deleted file mode 100644 index 80c1beb..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/object/BeanUtils.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.iailab.framework.common.util.object; - -import cn.hutool.core.bean.BeanUtil; -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.common.util.collection.CollectionUtils; - -import java.util.List; -import java.util.function.Consumer; - -/** - * Bean 工具类 - * - * 1. 默认使用 {@link cn.hutool.core.bean.BeanUtil} 作为实现类,虽然不同 bean 工具的性能有差别,但是对绝大多数同学的项目,不用在意这点性能 - * 2. 针对复杂的对象转换,可以搜参考 AuthConvert 实现,通过 mapstruct + default 配合实现 - * - * @author iailab - */ -public class BeanUtils { - - public static <T> T toBean(Object source, Class<T> targetClass) { - return BeanUtil.toBean(source, targetClass); - } - - public static <T> T toBean(Object source, Class<T> targetClass, Consumer<T> peek) { - T target = toBean(source, targetClass); - if (target != null) { - peek.accept(target); - } - return target; - } - - public static <S, T> List<T> toBean(List<S> source, Class<T> targetType) { - if (source == null) { - return null; - } - return CollectionUtils.convertList(source, s -> toBean(s, targetType)); - } - - public static <S, T> List<T> toBean(List<S> source, Class<T> targetType, Consumer<T> peek) { - List<T> list = toBean(source, targetType); - if (list != null) { - list.forEach(peek); - } - return list; - } - - public static <S, T> PageResult<T> toBean(PageResult<S> source, Class<T> targetType) { - return toBean(source, targetType, null); - } - - public static <S, T> PageResult<T> toBean(PageResult<S> source, Class<T> targetType, Consumer<T> peek) { - if (source == null) { - return null; - } - List<T> list = toBean(source.getList(), targetType); - if (peek != null) { - list.forEach(peek); - } - return new PageResult<>(list, source.getTotal()); - } - - public static void copyProperties(Object source, Object target) { - if (source == null || target == null) { - return; - } - BeanUtil.copyProperties(source, target, false); - } - -} \ No newline at end of file diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/object/ConvertUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/object/ConvertUtils.java deleted file mode 100644 index 4faad98..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/object/ConvertUtils.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.util.object; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.BeanUtils; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -/** - * 转换工具类 - * - * @author Mark sunlightcs@gmail.com - */ -public class ConvertUtils { - private static Logger logger = LoggerFactory.getLogger(ConvertUtils.class); - - public static <T> T sourceToTarget(Object source, Class<T> target){ - if(source == null){ - return null; - } - T targetObject = null; - try { - targetObject = target.newInstance(); - BeanUtils.copyProperties(source, targetObject); - } catch (Exception e) { - logger.error("convert error ", e); - } - - return targetObject; - } - - public static <T> List<T> sourceToTarget(Collection<?> sourceList, Class<T> target){ - if(sourceList == null){ - return null; - } - - List targetList = new ArrayList<>(sourceList.size()); - try { - for(Object source : sourceList){ - T targetObject = target.newInstance(); - BeanUtils.copyProperties(source, targetObject); - targetList.add(targetObject); - } - }catch (Exception e){ - logger.error("convert error ", e); - } - - return targetList; - } - /** - * 获取类的所有属性,包括父类 - * - * @param object - * @return - */ - public static Field[] getAllFields(Object object) { - Class<?> clazz = object.getClass(); - List<Field> fieldList = new ArrayList<>(); - while (clazz != null) { - fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()))); - clazz = clazz.getSuperclass(); - } - Field[] fields = new Field[fieldList.size()]; - fieldList.toArray(fields); - return fields; - } -} \ No newline at end of file diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/object/ObjectUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/object/ObjectUtils.java deleted file mode 100644 index 728287e..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/object/ObjectUtils.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.iailab.framework.common.util.object; - -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.ReflectUtil; - -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.function.Consumer; - -/** - * Object 工具类 - * - * @author iailab - */ -public class ObjectUtils { - - /** - * 复制对象,并忽略 Id 编号 - * - * @param object 被复制对象 - * @param consumer 消费者,可以二次编辑被复制对象 - * @return 复制后的对象 - */ - public static <T> T cloneIgnoreId(T object, Consumer<T> consumer) { - T result = ObjectUtil.clone(object); - // 忽略 id 编号 - Field field = ReflectUtil.getField(object.getClass(), "id"); - if (field != null) { - ReflectUtil.setFieldValue(result, field, null); - } - // 二次编辑 - if (result != null) { - consumer.accept(result); - } - return result; - } - - public static <T extends Comparable<T>> T max(T obj1, T obj2) { - if (obj1 == null) { - return obj2; - } - if (obj2 == null) { - return obj1; - } - return obj1.compareTo(obj2) > 0 ? obj1 : obj2; - } - - @SafeVarargs - public static <T> T defaultIfNull(T... array) { - for (T item : array) { - if (item != null) { - return item; - } - } - return null; - } - - @SafeVarargs - public static <T> boolean equalsAny(T obj, T... array) { - return Arrays.asList(array).contains(obj); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/object/PageUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/object/PageUtils.java deleted file mode 100644 index 3476b90..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/object/PageUtils.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.iailab.framework.common.util.object; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.func.Func1; -import cn.hutool.core.lang.func.LambdaUtil; -import cn.hutool.core.util.ArrayUtil; -import com.iailab.framework.common.pojo.PageParam; -import com.iailab.framework.common.pojo.SortablePageParam; -import com.iailab.framework.common.pojo.SortingField; -import org.springframework.util.Assert; - -import static java.util.Collections.singletonList; - -/** - * {@link com.iailab.framework.common.pojo.PageParam} 工具类 - * - * @author iailab - */ -public class PageUtils { - - private static final Object[] ORDER_TYPES = new String[]{SortingField.ORDER_ASC, SortingField.ORDER_DESC}; - - public static int getStart(PageParam pageParam) { - return (pageParam.getPageNo() - 1) * pageParam.getPageSize(); - } - - /** - * 构建排序字段(默认倒序) - * - * @param func 排序字段的 Lambda 表达式 - * @param <T> 排序字段所属的类型 - * @return 排序字段 - */ - public static <T> SortingField buildSortingField(Func1<T, ?> func) { - return buildSortingField(func, SortingField.ORDER_DESC); - } - - /** - * 构建排序字段 - * - * @param func 排序字段的 Lambda 表达式 - * @param order 排序类型 {@link SortingField#ORDER_ASC} {@link SortingField#ORDER_DESC} - * @param <T> 排序字段所属的类型 - * @return 排序字段 - */ - public static <T> SortingField buildSortingField(Func1<T, ?> func, String order) { - Assert.isTrue(ArrayUtil.contains(ORDER_TYPES, order), String.format("字段的排序类型只能是 %s/%s", ORDER_TYPES)); - - String fieldName = LambdaUtil.getFieldName(func); - return new SortingField(fieldName, order); - } - - /** - * 构建默认的排序字段 - * 如果排序字段为空,则设置排序字段;否则忽略 - * - * @param sortablePageParam 排序分页查询参数 - * @param func 排序字段的 Lambda 表达式 - * @param <T> 排序字段所属的类型 - */ - public static <T> void buildDefaultSortingField(SortablePageParam sortablePageParam, Func1<T, ?> func) { - if (sortablePageParam != null && CollUtil.isEmpty(sortablePageParam.getSortingFields())) { - sortablePageParam.setSortingFields(singletonList(buildSortingField(func))); - } - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/package-info.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/package-info.java deleted file mode 100644 index 74c17a4..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * 对于工具类的选择,优先查找 Hutool 中有没对应的方法 - * 如果没有,则自己封装对应的工具类,以 Utils 结尾,用于区分 - * - * ps:如果担心 Hutool 存在坑的问题,可以阅读 Hutool 的实现源码,以确保可靠性。并且,可以补充相关的单元测试。 - */ -package com.iailab.framework.common.util; diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/servlet/ServletUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/servlet/ServletUtils.java deleted file mode 100644 index 7f12b67..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/servlet/ServletUtils.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.iailab.framework.common.util.servlet; - -import cn.hutool.core.io.IoUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.extra.servlet.ServletUtil; -import com.iailab.framework.common.util.json.JsonUtils; -import org.springframework.http.MediaType; -import org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import javax.servlet.ServletRequest; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.net.URLEncoder; -import java.util.Map; - -/** - * 客户端工具类 - * - * @author iailab - */ -public class ServletUtils { - - /** - * 返回 JSON 字符串 - * - * @param response 响应 - * @param object 对象,会序列化成 JSON 字符串 - */ - @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码 - public static void writeJSON(HttpServletResponse response, Object object) { - String content = JsonUtils.toJsonString(object); - ServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE); - } - - /** - * 返回附件 - * - * @param response 响应 - * @param filename 文件名 - * @param content 附件内容 - */ - public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException { - // 设置 header 和 contentType - response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); - response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); - // 输出附件 - IoUtil.write(response.getOutputStream(), false, content); - } - - /** - * @param request 请求 - * @return ua - */ - public static String getUserAgent(HttpServletRequest request) { - String ua = request.getHeader("User-Agent"); - return ua != null ? ua : ""; - } - - /** - * 获得请求 - * - * @return HttpServletRequest - */ - public static HttpServletRequest getRequest() { - RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); - if (!(requestAttributes instanceof ServletRequestAttributes)) { - return null; - } - return ((ServletRequestAttributes) requestAttributes).getRequest(); - } - - public static String getUserAgent() { - HttpServletRequest request = getRequest(); - if (request == null) { - return null; - } - return getUserAgent(request); - } - - public static String getClientIP() { - HttpServletRequest request = getRequest(); - if (request == null) { - return null; - } - return ServletUtil.getClientIP(request); - } - - public static boolean isJsonRequest(ServletRequest request) { - return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE); - } - - public static String getBody(HttpServletRequest request) { - // 只有在 json 请求在读取,因为只有 CacheRequestBodyFilter 才会进行缓存,支持重复读取 - if (isJsonRequest(request)) { - return ServletUtil.getBody(request); - } - return null; - } - - public static byte[] getBodyBytes(HttpServletRequest request) { - // 只有在 json 请求在读取,因为只有 CacheRequestBodyFilter 才会进行缓存,支持重复读取 - if (isJsonRequest(request)) { - return ServletUtil.getBodyBytes(request); - } - return null; - } - - public static String getClientIP(HttpServletRequest request) { - return ServletUtil.getClientIP(request); - } - - public static Map<String, String> getParamMap(HttpServletRequest request) { - return ServletUtil.getParamMap(request); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/spring/SpringContextUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/spring/SpringContextUtils.java deleted file mode 100644 index 1cc4391..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/spring/SpringContextUtils.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.util.spring; - -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.stereotype.Component; - -/** - * Spring Context 工具类 - * - * @author Mark sunlightcs@gmail.com - */ -@Component -public class SpringContextUtils implements ApplicationContextAware { - public static ApplicationContext applicationContext; - - @Override - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { - SpringContextUtils.applicationContext = applicationContext; - } - - public static Object getBean(String name) { - return applicationContext.getBean(name); - } - - public static <T> T getBean(Class<T> requiredType) { - return applicationContext.getBean(requiredType); - } - - public static <T> T getBean(String name, Class<T> requiredType) { - return applicationContext.getBean(name, requiredType); - } - - public static boolean containsBean(String name) { - return applicationContext.containsBean(name); - } - - public static boolean isSingleton(String name) { - return applicationContext.isSingleton(name); - } - - public static Class<? extends Object> getType(String name) { - return applicationContext.getType(name); - } - -} \ No newline at end of file diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/spring/SpringExpressionUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/spring/SpringExpressionUtils.java deleted file mode 100644 index 9302a86..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/spring/SpringExpressionUtils.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.iailab.framework.common.util.spring; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.ArrayUtil; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.core.DefaultParameterNameDiscoverer; -import org.springframework.core.ParameterNameDiscoverer; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; - -import java.lang.reflect.Method; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * Spring EL 表达式的工具类 - * - * @author mashu - */ -public class SpringExpressionUtils { - - /** - * Spring EL 表达式解析器 - */ - private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser(); - /** - * 参数名发现器 - */ - private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer(); - - private SpringExpressionUtils() { - } - - /** - * 从切面中,单个解析 EL 表达式的结果 - * - * @param joinPoint 切面点 - * @param expressionString EL 表达式数组 - * @return 执行界面 - */ - public static Object parseExpression(JoinPoint joinPoint, String expressionString) { - Map<String, Object> result = parseExpressions(joinPoint, Collections.singletonList(expressionString)); - return result.get(expressionString); - } - - /** - * 从切面中,批量解析 EL 表达式的结果 - * - * @param joinPoint 切面点 - * @param expressionStrings EL 表达式数组 - * @return 结果,key 为表达式,value 为对应值 - */ - public static Map<String, Object> parseExpressions(JoinPoint joinPoint, List<String> expressionStrings) { - // 如果为空,则不进行解析 - if (CollUtil.isEmpty(expressionStrings)) { - return MapUtil.newHashMap(); - } - - // 第一步,构建解析的上下文 EvaluationContext - // 通过 joinPoint 获取被注解方法 - MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); - Method method = methodSignature.getMethod(); - // 使用 spring 的 ParameterNameDiscoverer 获取方法形参名数组 - String[] paramNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method); - // Spring 的表达式上下文对象 - EvaluationContext context = new StandardEvaluationContext(); - // 给上下文赋值 - if (ArrayUtil.isNotEmpty(paramNames)) { - Object[] args = joinPoint.getArgs(); - for (int i = 0; i < paramNames.length; i++) { - context.setVariable(paramNames[i], args[i]); - } - } - - // 第二步,逐个参数解析 - Map<String, Object> result = MapUtil.newHashMap(expressionStrings.size(), true); - expressionStrings.forEach(key -> { - Object value = EXPRESSION_PARSER.parseExpression(key).getValue(context); - result.put(key, value); - }); - return result; - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/spring/SpringUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/spring/SpringUtils.java deleted file mode 100644 index 00ca53b..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/spring/SpringUtils.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.iailab.framework.common.util.spring; - -import cn.hutool.extra.spring.SpringUtil; - -import java.util.Objects; - -/** - * Spring 工具类 - * - * @author iailab - */ -public class SpringUtils extends SpringUtil { - - /** - * 是否为生产环境 - * - * @return 是否生产环境 - */ - public static boolean isProd() { - String activeProfile = getActiveProfile(); - return Objects.equals("prod", activeProfile); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/string/StrUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/string/StrUtils.java deleted file mode 100644 index 31e2c4c..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/string/StrUtils.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.iailab.framework.common.util.string; - -import cn.hutool.core.text.StrPool; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.StrUtil; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * 字符串工具类 - * - * @author iailab - */ -public class StrUtils { - - public static String maxLength(CharSequence str, int maxLength) { - return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好 - } - - /** - * 给定字符串是否以任何一个字符串开始 - * 给定字符串和数组为空都返回 false - * - * @param str 给定字符串 - * @param prefixes 需要检测的开始字符串 - * @since 3.0.6 - */ - public static boolean startWithAny(String str, Collection<String> prefixes) { - if (StrUtil.isEmpty(str) || ArrayUtil.isEmpty(prefixes)) { - return false; - } - - for (CharSequence suffix : prefixes) { - if (StrUtil.startWith(str, suffix, false)) { - return true; - } - } - return false; - } - - public static List<Long> splitToLong(String value, CharSequence separator) { - long[] longs = StrUtil.splitToLong(value, separator); - return Arrays.stream(longs).boxed().collect(Collectors.toList()); - } - - public static Set<Long> splitToLongSet(String value) { - return splitToLongSet(value, StrPool.COMMA); - } - - public static Set<Long> splitToLongSet(String value, CharSequence separator) { - long[] longs = StrUtil.splitToLong(value, separator); - return Arrays.stream(longs).boxed().collect(Collectors.toSet()); - } - - public static List<Integer> splitToInteger(String value, CharSequence separator) { - int[] integers = StrUtil.splitToInt(value, separator); - return Arrays.stream(integers).boxed().collect(Collectors.toList()); - } - - /** - * 移除字符串中,包含指定字符串的行 - * - * @param content 字符串 - * @param sequence 包含的字符串 - * @return 移除后的字符串 - */ - public static String removeLineContains(String content, String sequence) { - if (StrUtil.isEmpty(content) || StrUtil.isEmpty(sequence)) { - return content; - } - return Arrays.stream(content.split("\n")) - .filter(line -> !line.contains(sequence)) - .collect(Collectors.joining("\n")); - } - - /** - * 判断字符串是不是数字 - * - * @param str - * @return - */ - public static boolean isNumeric(String str) { - return str.matches("-?\\d+(\\.\\d+)?"); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/validation/ValidationUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/validation/ValidationUtils.java deleted file mode 100644 index d798b49..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/validation/ValidationUtils.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.iailab.framework.common.util.validation; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import org.springframework.util.StringUtils; - -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; -import javax.validation.Validation; -import javax.validation.Validator; -import java.util.Set; -import java.util.regex.Pattern; - -/** - * 校验工具类 - * - * @author iailab - */ -public class ValidationUtils { - - private static final Pattern PATTERN_MOBILE = Pattern.compile("^(?:(?:\\+|00)86)?1(?:(?:3[\\d])|(?:4[0,1,4-9])|(?:5[0-3,5-9])|(?:6[2,5-7])|(?:7[0-8])|(?:8[\\d])|(?:9[0-3,5-9]))\\d{8}$"); - - private static final Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"); - - private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*"); - - public static boolean isMobile(String mobile) { - return StringUtils.hasText(mobile) - && PATTERN_MOBILE.matcher(mobile).matches(); - } - - public static boolean isURL(String url) { - return StringUtils.hasText(url) - && PATTERN_URL.matcher(url).matches(); - } - - public static boolean isXmlNCName(String str) { - return StringUtils.hasText(str) - && PATTERN_XML_NCNAME.matcher(str).matches(); - } - - public static void validate(Object object, Class<?>... groups) { - Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); - Assert.notNull(validator); - validate(validator, object, groups); - } - - public static void validate(Validator validator, Object object, Class<?>... groups) { - Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups); - if (CollUtil.isNotEmpty(constraintViolations)) { - throw new ConstraintViolationException(constraintViolations); - } - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnum.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnum.java deleted file mode 100644 index d7bc460..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnum.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.iailab.framework.common.validation; - -import com.iailab.framework.common.core.IntArrayValuable; - -import javax.validation.Constraint; -import javax.validation.Payload; -import java.lang.annotation.*; - -@Target({ - ElementType.METHOD, - ElementType.FIELD, - ElementType.ANNOTATION_TYPE, - ElementType.CONSTRUCTOR, - ElementType.PARAMETER, - ElementType.TYPE_USE -}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Constraint( - validatedBy = {InEnumValidator.class, InEnumCollectionValidator.class} -) -public @interface InEnum { - - /** - * @return 实现 EnumValuable 接口的 - */ - Class<? extends IntArrayValuable> value(); - - String message() default "必须在指定范围 {value}"; - - Class<?>[] groups() default {}; - - Class<? extends Payload>[] payload() default {}; - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumCollectionValidator.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumCollectionValidator.java deleted file mode 100644 index ce162b2..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumCollectionValidator.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.iailab.framework.common.validation; - -import cn.hutool.core.collection.CollUtil; -import com.iailab.framework.common.core.IntArrayValuable; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<Integer>> { - - private List<Integer> values; - - @Override - public void initialize(InEnum annotation) { - IntArrayValuable[] values = annotation.value().getEnumConstants(); - if (values.length == 0) { - this.values = Collections.emptyList(); - } else { - this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList()); - } - } - - @Override - public boolean isValid(Collection<Integer> list, ConstraintValidatorContext context) { - // 校验通过 - if (CollUtil.containsAll(values, list)) { - return true; - } - // 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值) - context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值 - context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate() - .replaceAll("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句 - return false; - } - -} - diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumValidator.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumValidator.java deleted file mode 100644 index f20b1fb..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumValidator.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.iailab.framework.common.validation; - -import com.iailab.framework.common.core.IntArrayValuable; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -public class InEnumValidator implements ConstraintValidator<InEnum, Integer> { - - private List<Integer> values; - - @Override - public void initialize(InEnum annotation) { - IntArrayValuable[] values = annotation.value().getEnumConstants(); - if (values.length == 0) { - this.values = Collections.emptyList(); - } else { - this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList()); - } - } - - @Override - public boolean isValid(Integer value, ConstraintValidatorContext context) { - // 为空时,默认不校验,即认为通过 - if (value == null) { - return true; - } - // 校验通过 - if (values.contains(value)) { - return true; - } - // 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值) - context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值 - context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate() - .replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句 - return false; - } - -} - diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/Mobile.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/Mobile.java deleted file mode 100644 index e1fedfd..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/Mobile.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.framework.common.validation; - -import javax.validation.Constraint; -import javax.validation.Payload; -import java.lang.annotation.*; - -@Target({ - ElementType.METHOD, - ElementType.FIELD, - ElementType.ANNOTATION_TYPE, - ElementType.CONSTRUCTOR, - ElementType.PARAMETER, - ElementType.TYPE_USE -}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Constraint( - validatedBy = MobileValidator.class -) -public @interface Mobile { - - String message() default "手机号格式不正确"; - - Class<?>[] groups() default {}; - - Class<? extends Payload>[] payload() default {}; - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/MobileValidator.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/MobileValidator.java deleted file mode 100644 index 1e0a24a..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/MobileValidator.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.common.validation; - -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.util.validation.ValidationUtils; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; - -public class MobileValidator implements ConstraintValidator<Mobile, String> { - - @Override - public void initialize(Mobile annotation) { - } - - @Override - public boolean isValid(String value, ConstraintValidatorContext context) { - // 如果手机号为空,默认不校验,即校验通过 - if (StrUtil.isEmpty(value)) { - return true; - } - // 校验手机 - return ValidationUtils.isMobile(value); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/Telephone.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/Telephone.java deleted file mode 100644 index ccc83e8..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/Telephone.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.framework.common.validation; - -import javax.validation.Constraint; -import javax.validation.Payload; -import java.lang.annotation.*; - -@Target({ - ElementType.METHOD, - ElementType.FIELD, - ElementType.ANNOTATION_TYPE, - ElementType.CONSTRUCTOR, - ElementType.PARAMETER, - ElementType.TYPE_USE -}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Constraint( - validatedBy = TelephoneValidator.class -) -public @interface Telephone { - - String message() default "电话格式不正确"; - - Class<?>[] groups() default {}; - - Class<? extends Payload>[] payload() default {}; - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/TelephoneValidator.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/TelephoneValidator.java deleted file mode 100644 index 42b3bfd..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/TelephoneValidator.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.framework.common.validation; - -import cn.hutool.core.text.CharSequenceUtil; -import cn.hutool.core.util.PhoneUtil; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; - -public class TelephoneValidator implements ConstraintValidator<Telephone, String> { - - @Override - public void initialize(Telephone annotation) { - } - - @Override - public boolean isValid(String value, ConstraintValidatorContext context) { - // 如果手机号为空,默认不校验,即校验通过 - if (CharSequenceUtil.isEmpty(value)) { - return true; - } - // 校验手机 - return PhoneUtil.isTel(value) || PhoneUtil.isPhone(value); - } - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/group/AddGroup.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/group/AddGroup.java deleted file mode 100644 index a453fff..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/group/AddGroup.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.validation.group; - -/** - * 新增 Group - * - * @author Mark sunlightcs@gmail.com - * @since 1.0.0 - */ -public interface AddGroup { - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/group/DefaultGroup.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/group/DefaultGroup.java deleted file mode 100644 index 895e477..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/group/DefaultGroup.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.validation.group; - -/** - * 默认 Group - * - * @author Mark sunlightcs@gmail.com - * @since 1.0.0 - */ -public interface DefaultGroup { - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/group/Group.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/group/Group.java deleted file mode 100644 index ac31f90..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/group/Group.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.validation.group; - -import javax.validation.GroupSequence; - -/** - * 定义校验顺序,如果AddGroup组失败,则UpdateGroup组不会再校验 - * - * @author Mark sunlightcs@gmail.com - * @since 1.0.0 - */ -@GroupSequence({AddGroup.class, UpdateGroup.class}) -public interface Group { - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/group/UpdateGroup.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/group/UpdateGroup.java deleted file mode 100644 index 2ab7e74..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/group/UpdateGroup.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2018 人人开源 All rights reserved. - * - * https://www.renren.io - * - * 版权所有,侵权必究! - */ - -package com.iailab.framework.common.validation.group; - -/** - * 修改 Group - * - * @author Mark sunlightcs@gmail.com - * @since 1.0.0 - */ -public interface UpdateGroup { - -} diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/package-info.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/package-info.java deleted file mode 100644 index 8fcc528..0000000 --- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 使用 Hibernate Validator 实现参数校验 - */ -package com.iailab.framework.common.validation; diff --git a/iailab-framework/iailab-common/src/test/java/com/iailab/framework/common/util/collection/CollectionUtilsTest.java b/iailab-framework/iailab-common/src/test/java/com/iailab/framework/common/util/collection/CollectionUtilsTest.java deleted file mode 100644 index 335e67c..0000000 --- a/iailab-framework/iailab-common/src/test/java/com/iailab/framework/common/util/collection/CollectionUtilsTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.iailab.framework.common.util.collection; - -import lombok.AllArgsConstructor; -import lombok.Data; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.function.BiFunction; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * {@link CollectionUtils} 的单元测试 - */ -public class CollectionUtilsTest { - - @Data - @AllArgsConstructor - private static class Dog { - - private Integer id; - private String name; - private String code; - - } - - @Test - public void testDiffList() { - // 准备参数 - Collection<Dog> oldList = Arrays.asList( - new Dog(1, "花花", "hh"), - new Dog(2, "旺财", "wc") - ); - Collection<Dog> newList = Arrays.asList( - new Dog(null, "花花2", "hh"), - new Dog(null, "小白", "xb") - ); - BiFunction<Dog, Dog, Boolean> sameFunc = (oldObj, newObj) -> { - boolean same = oldObj.getCode().equals(newObj.getCode()); - // 如果相等的情况下,需要设置下 id,后续好更新 - if (same) { - newObj.setId(oldObj.getId()); - } - return same; - }; - - // 调用 - List<List<Dog>> result = CollectionUtils.diffList(oldList, newList, sameFunc); - // 断言 - assertEquals(result.size(), 3); - // 断言 create - assertEquals(result.get(0).size(), 1); - assertEquals(result.get(0).get(0), new Dog(null, "小白", "xb")); - // 断言 update - assertEquals(result.get(1).size(), 1); - assertEquals(result.get(1).get(0), new Dog(1, "花花2", "hh")); - // 断言 delete - assertEquals(result.get(2).size(), 1); - assertEquals(result.get(2).get(0), new Dog(2, "旺财", "wc")); - } - -} diff --git a/iailab-framework/pom.xml b/iailab-framework/pom.xml deleted file mode 100644 index 1ca5b3b..0000000 --- a/iailab-framework/pom.xml +++ /dev/null @@ -1,118 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <artifactId>iailab-plat</artifactId> - <groupId>com.iailab</groupId> - <version>${revision}</version> - </parent> - <packaging>pom</packaging> - <modules> - <module>iailab-common</module> - <module>iailab-common-env</module> - <module>iailab-common-mybatis</module> - <module>iailab-common-redis</module> - <module>iailab-common-web</module> - <module>iailab-common-security</module> - <module>iailab-common-websocket</module> - - <module>iailab-common-monitor</module> - <module>iailab-common-protection</module> - <module>iailab-common-job</module> - <module>iailab-common-mq</module> - <module>iailab-common-rpc</module> - - <module>iailab-common-excel</module> - <module>iailab-common-test</module> - - <module>iailab-common-biz-tenant</module> - <module>iailab-common-biz-data-permission</module> - <module>iailab-common-biz-ip</module> - </modules> - - <artifactId>iailab-framework</artifactId> - <description> - 该包是技术组件,每个子包,代表一个组件。每个组件包括两部分: - 1. core 包:是该组件的核心封装 - 2. config 包:是该组件基于 Spring 的配置 - - 技术组件,也分成两类: - 1. 框架组件:和我们熟悉的 MyBatis、Redis 等等的拓展 - 2. 业务组件:和业务相关的组件的封装,例如说数据字典、操作日志等等。 - 如果是业务组件,Maven 名字会包含 biz - </description> - <url>http://172.16.8.100:8888/summary/iailab-plat.git</url> - - <build> - <pluginManagement> - <plugins> - <!-- maven-surefire-plugin 插件,用于运行单元测试。 --> - <!-- 注意,需要使用 3.0.X+,因为要支持 Junit 5 版本 --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <version>${maven-surefire-plugin.version}</version> - </plugin> - <!-- maven-compiler-plugin 插件,解决 Lombok + MapStruct 组合 --> - <!-- https://stackoverflow.com/questions/33483697/re-run-spring-boot-configuration-annotation-processor-to-update-generated-metada --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <version>${maven-compiler-plugin.version}</version> - <configuration> - <annotationProcessorPaths> - <path> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-configuration-processor</artifactId> - <version>${spring.boot.version}</version> - </path> - <path> - <groupId>org.projectlombok</groupId> - <artifactId>lombok</artifactId> - <version>${lombok.version}</version> - </path> - <path> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct-processor</artifactId> - <version>${mapstruct.version}</version> - </path> - </annotationProcessorPaths> - </configuration> - </plugin> - </plugins> - </pluginManagement> - - <plugins> - <!-- 统一 revision 版本 --> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>flatten-maven-plugin</artifactId> - <version>${flatten-maven-plugin.version}</version> - <configuration> - <flattenMode>resolveCiFriendliesOnly</flattenMode> - <updatePomFile>true</updatePomFile> - </configuration> - <executions> - <execution> - <goals> - <goal>flatten</goal> - </goals> - <id>flatten</id> - <phase>process-resources</phase> - </execution> - <execution> - <goals> - <goal>clean</goal> - </goals> - <id>flatten.clean</id> - <phase>clean</phase> - </execution> - </executions> - </plugin> - - </plugins> - </build> - -</project> diff --git a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/SmsCodeApi.java b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/SmsCodeApi.java deleted file mode 100644 index a976e7d..0000000 --- a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/SmsCodeApi.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.iailab.module.system.api.sms; - -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; -import com.iailab.module.system.api.sms.dto.code.SmsCodeSendReqDTO; -import com.iailab.module.system.api.sms.dto.code.SmsCodeUseReqDTO; -import com.iailab.module.system.enums.ApiConstants; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Operation; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; - -import javax.validation.Valid; - -@FeignClient(name = ApiConstants.NAME) -@Tag(name = "RPC 服务 - 短信验证码") -public interface SmsCodeApi { - - String PREFIX = ApiConstants.PREFIX + "/oauth2/sms/code"; - - @PostMapping(PREFIX + "/send") - @Operation(summary = "创建短信验证码,并进行发送") - CommonResult<Boolean> sendSmsCode(@Valid @RequestBody SmsCodeSendReqDTO reqDTO); - - @PutMapping(PREFIX + "/use") - @Operation(summary = "验证短信验证码,并进行使用") - CommonResult<Boolean> useSmsCode(@Valid @RequestBody SmsCodeUseReqDTO reqDTO); - - @GetMapping(PREFIX + "/validate") - @Operation(summary = "检查验证码是否有效") - CommonResult<Boolean> validateSmsCode(@Valid @RequestBody SmsCodeValidateReqDTO reqDTO); - -} diff --git a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/SmsSendApi.java b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/SmsSendApi.java deleted file mode 100644 index d1fdca5..0000000 --- a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/SmsSendApi.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.module.system.api.sms; - -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO; -import com.iailab.module.system.enums.ApiConstants; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Operation; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; - -import javax.validation.Valid; - -@FeignClient(name = ApiConstants.NAME) -@Tag(name = "RPC 服务 - 短信发送") -public interface SmsSendApi { - - String PREFIX = ApiConstants.PREFIX + "/sms/send"; - - @PostMapping(PREFIX + "/send-single-admin") - @Operation(summary = "发送单条短信给 Admin 用户", description = "在 mobile 为空时,使用 userId 加载对应 Admin 的手机号") - CommonResult<Long> sendSingleSmsToAdmin(@Valid @RequestBody SmsSendSingleToUserReqDTO reqDTO); - - @PostMapping(PREFIX + "/send-single-member") - @Operation(summary = "发送单条短信给 Member 用户", description = "在 mobile 为空时,使用 userId 加载对应 Member 的手机号") - CommonResult<Long> sendSingleSmsToMember(@Valid @RequestBody SmsSendSingleToUserReqDTO reqDTO); - -} diff --git a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/dto/code/SmsCodeSendReqDTO.java b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/dto/code/SmsCodeSendReqDTO.java deleted file mode 100644 index 425f04e..0000000 --- a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/dto/code/SmsCodeSendReqDTO.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.iailab.module.system.api.sms.dto.code; - -import com.iailab.framework.common.validation.InEnum; -import com.iailab.framework.common.validation.Mobile; -import com.iailab.module.system.enums.sms.SmsSceneEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -@Schema(description = "RPC 服务 - 短信验证码的发送 Request DTO") -@Data -public class SmsCodeSendReqDTO { - - @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") - @Mobile - @NotEmpty(message = "手机号不能为空") - private String mobile; - - @Schema(description = "发送场景", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "发送场景不能为空") - @InEnum(SmsSceneEnum.class) - - private Integer scene; - @Schema(description = "发送 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "10.20.30.40") - @NotEmpty(message = "发送 IP 不能为空") - private String createIp; - -} diff --git a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/dto/code/SmsCodeUseReqDTO.java b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/dto/code/SmsCodeUseReqDTO.java deleted file mode 100644 index a9b4d55..0000000 --- a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/dto/code/SmsCodeUseReqDTO.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.iailab.module.system.api.sms.dto.code; - -import com.iailab.framework.common.validation.InEnum; -import com.iailab.framework.common.validation.Mobile; -import com.iailab.module.system.enums.sms.SmsSceneEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -@Schema(description = "RPC 服务 - 短信验证码的使用 Request DTO") -@Data -public class SmsCodeUseReqDTO { - - @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") - @Mobile - @NotEmpty(message = "手机号不能为空") - private String mobile; - - @Schema(description = "发送场景", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "发送场景不能为空") - @InEnum(SmsSceneEnum.class) - private Integer scene; - - @Schema(description = "验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "验证码") - private String code; - - @Schema(description = "发送 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "10.20.30.40") - @NotEmpty(message = "使用 IP 不能为空") - private String usedIp; - -} diff --git a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/dto/code/SmsCodeValidateReqDTO.java b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/dto/code/SmsCodeValidateReqDTO.java deleted file mode 100644 index 11d892f..0000000 --- a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/dto/code/SmsCodeValidateReqDTO.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.iailab.module.system.api.sms.dto.code; - -import com.iailab.framework.common.validation.InEnum; -import com.iailab.framework.common.validation.Mobile; -import com.iailab.module.system.enums.sms.SmsSceneEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -@Schema(description = "RPC 服务 - 短信验证码的校验 Request DTO") -@Data -public class SmsCodeValidateReqDTO { - - @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") - @Mobile - @NotEmpty(message = "手机号不能为空") - private String mobile; - - @Schema(description = "发送场景", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "发送场景不能为空") - @InEnum(SmsSceneEnum.class) - private Integer scene; - - @Schema(description = "验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "验证码") - private String code; - -} diff --git a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/dto/send/SmsSendSingleToUserReqDTO.java b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/dto/send/SmsSendSingleToUserReqDTO.java deleted file mode 100644 index 05f2290..0000000 --- a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/sms/dto/send/SmsSendSingleToUserReqDTO.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.iailab.module.system.api.sms.dto.send; - -import com.iailab.framework.common.validation.Mobile; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import java.util.Map; - -@Schema(description = "RPC 服务 - 短信发送给 Admin 或者 Member 用户 Request DTO") -@Data -public class SmsSendSingleToUserReqDTO { - - @Schema(description = "用户编号", example = "1024") - private Long userId; - @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") - @Mobile - private String mobile; - - @Schema(description = "短信模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "USER_SEND") - @NotEmpty(message = "短信模板编号不能为空") - private String templateCode; - @Schema(description = "短信模板参数") - private Map<String, Object> templateParams; - -} diff --git a/iailab-module-system/iailab-module-system-biz/pom.xml b/iailab-module-system/iailab-module-system-biz/pom.xml index 70eed45..c92d927 100644 --- a/iailab-module-system/iailab-module-system-biz/pom.xml +++ b/iailab-module-system/iailab-module-system-biz/pom.xml @@ -90,6 +90,12 @@ <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> + <!-- Job 定时任务相关 --> +<!-- <dependency>--> +<!-- <groupId>com.iailab</groupId>--> +<!-- <artifactId>iailab-common-job</artifactId>--> +<!-- </dependency>--> + <!-- 消息队列相关 --> <dependency> <groupId>com.iailab</groupId> @@ -126,38 +132,35 @@ <artifactId>iailab-common-monitor</artifactId> </dependency> - <!-- 三方云服务相关 --> - <dependency> - <groupId>com.xingyuv</groupId> - <artifactId>spring-boot-starter-justauth</artifactId> <!-- 社交登陆(例如说,个人微信、企业微信等等) --> - </dependency> +<!-- <!– 三方云服务相关 –>--> +<!-- <dependency>--> +<!-- <groupId>com.xingyuv</groupId>--> +<!-- <artifactId>spring-boot-starter-justauth</artifactId> <!– 社交登陆(例如说,个人微信、企业微信等等) –>--> +<!-- </dependency>--> - <dependency> - <groupId>com.github.binarywang</groupId> - <artifactId>wx-java-mp-spring-boot-starter</artifactId> <!-- 微信登录(公众号) --> - </dependency> - <dependency> - <groupId>com.github.binarywang</groupId> - <artifactId>wx-java-miniapp-spring-boot-starter</artifactId> <!-- 微信登录(小程序) --> - </dependency> - - <dependency> - <groupId>com.aliyun</groupId> - <artifactId>aliyun-java-sdk-core</artifactId> <!-- 短信(阿里云) --> - </dependency> - <dependency> - <groupId>com.aliyun</groupId> - <artifactId>aliyun-java-sdk-dysmsapi</artifactId> <!-- 短信(阿里云) --> - </dependency> - <dependency> - <groupId>com.tencentcloudapi</groupId> - <artifactId>tencentcloud-sdk-java-sms</artifactId> <!-- 短信(腾讯云) --> - </dependency> +<!-- <dependency>--> +<!-- <groupId>com.aliyun</groupId>--> +<!-- <artifactId>aliyun-java-sdk-core</artifactId> <!– 短信(阿里云) –>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>com.aliyun</groupId>--> +<!-- <artifactId>aliyun-java-sdk-dysmsapi</artifactId> <!– 短信(阿里云) –>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>com.tencentcloudapi</groupId>--> +<!-- <artifactId>tencentcloud-sdk-java-sms</artifactId> <!– 短信(腾讯云) –>--> +<!-- </dependency>--> <dependency> <groupId>com.xingyuv</groupId> <artifactId>spring-boot-starter-captcha-plus</artifactId> <!-- 验证码,一般用于登录使用 --> </dependency> + +<!-- <dependency>--> +<!-- <groupId>com.iailab</groupId>--> +<!-- <artifactId>iailab-plat-sdk</artifactId>--> +<!-- <version>1.0.0</version>--> +<!-- </dependency>--> </dependencies> @@ -165,6 +168,45 @@ <!-- 设置构建的 jar 包名 --> <finalName>${project.artifactId}</finalName> <plugins> + <plugin> + <groupId>com.github.wvengen</groupId> + <artifactId>proguard-maven-plugin</artifactId> + <version>2.7.0</version> + <executions> + <!-- 以下配置说明执行mvn的package命令时候,会执行proguard--> + <execution> + <phase>package</phase> + <goals> + <goal>proguard</goal> + </goals> + </execution> + </executions> + <configuration> + <!-- 就是输入Jar的名称,我们要知道,代码混淆其实是将一个原始的jar,生成一个混淆后的jar,那么就会有输入输出。 --> + <injar>${project.build.finalName}.jar</injar> + <!-- 输出jar名称,输入输出jar同名的时候就是覆盖,也是比较常用的配置。 --> + <outjar>${project.build.finalName}.jar</outjar> + <!-- 是否混淆 默认是true --> + <obfuscate>true</obfuscate> + <putLibraryJarsInTempDir>true</putLibraryJarsInTempDir> + <!-- 配置一个文件,通常叫做proguard.cfg,该文件主要是配置options选项,也就是说使用proguard.cfg那么options下的所有内容都可以移到proguard.cfg中 --> + <proguardInclude>${project.basedir}/proguard.cfg</proguardInclude> + <!-- 额外的jar包,通常是项目编译所需要的jar --> + <libs> + <lib>${java.home}/lib/rt.jar</lib> + <lib>${java.home}/lib/jce.jar</lib> + <lib>${java.home}/lib/jsse.jar</lib> + </libs> + <!-- 对输入jar进行过滤比如,如下配置就是对META-INFO文件不处理。 --> + <inLibsFilter>!META-INF/**,!META-INF/versions/9/**.class</inLibsFilter> + <!-- 这是输出路径配置,但是要注意这个路径必须要包括injar标签填写的jar --> + <outputDirectory>${project.basedir}/target</outputDirectory> + <!--这里特别重要,此处主要是配置混淆的一些细节选项,比如哪些类不需要混淆,哪些需要混淆--> + <options> + <!-- 可以在此处写option标签配置,不过我上面使用了proguardInclude,故而我更喜欢在proguard.cfg中配置 --> + </options> + </configuration> + </plugin> <!-- 打包 --> <plugin> <groupId>org.springframework.boot</groupId> diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/sms/SmsCodeApiImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/sms/SmsCodeApiImpl.java deleted file mode 100644 index 4f31dda..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/sms/SmsCodeApiImpl.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.iailab.module.system.api.sms; - -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.module.system.api.sms.dto.code.SmsCodeSendReqDTO; -import com.iailab.module.system.api.sms.dto.code.SmsCodeUseReqDTO; -import com.iailab.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; -import com.iailab.module.system.service.sms.SmsCodeService; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; - -import static com.iailab.framework.common.pojo.CommonResult.success; - -@RestController // 提供 RESTful API 接口,给 Feign 调用 -@Validated -public class SmsCodeApiImpl implements SmsCodeApi { - - @Resource - private SmsCodeService smsCodeService; - - @Override - public CommonResult<Boolean> sendSmsCode(SmsCodeSendReqDTO reqDTO) { - smsCodeService.sendSmsCode(reqDTO); - return success(true); - } - - @Override - public CommonResult<Boolean> useSmsCode(SmsCodeUseReqDTO reqDTO) { - smsCodeService.useSmsCode(reqDTO); - return success(true); - } - - @Override - public CommonResult<Boolean> validateSmsCode(SmsCodeValidateReqDTO reqDTO) { - smsCodeService.validateSmsCode(reqDTO); - return success(true); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/sms/SmsSendApiImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/sms/SmsSendApiImpl.java deleted file mode 100644 index f68c108..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/sms/SmsSendApiImpl.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.iailab.module.system.api.sms; - -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO; -import com.iailab.module.system.service.sms.SmsSendService; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; - -import static com.iailab.framework.common.pojo.CommonResult.success; - -@RestController // 提供 RESTful API 接口,给 Feign 调用 -@Validated -public class SmsSendApiImpl implements SmsSendApi { - - @Resource - private SmsSendService smsSendService; - - @Override - public CommonResult<Long> sendSingleSmsToAdmin(SmsSendSingleToUserReqDTO reqDTO) { - return success(smsSendService.sendSingleSmsToAdmin(reqDTO.getMobile(), reqDTO.getUserId(), - reqDTO.getTemplateCode(), reqDTO.getTemplateParams())); - } - - @Override - public CommonResult<Long> sendSingleSmsToMember(SmsSendSingleToUserReqDTO reqDTO) { - return success(smsSendService.sendSingleSmsToMember(reqDTO.getMobile(), reqDTO.getUserId(), - reqDTO.getTemplateCode(), reqDTO.getTemplateParams())); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/social/SocialClientApiImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/social/SocialClientApiImpl.java deleted file mode 100644 index 265adc6..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/social/SocialClientApiImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.iailab.module.system.api.social; - -import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.util.object.BeanUtils; -import com.iailab.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO; -import com.iailab.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO; -import com.iailab.module.system.service.social.SocialClientService; -import me.chanjar.weixin.common.bean.WxJsapiSignature; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; - -import static com.iailab.framework.common.pojo.CommonResult.success; - -/** - * 社交应用的 API 实现类 - * - * @author iailab - */ -@RestController -@Validated -public class SocialClientApiImpl implements SocialClientApi { - - @Resource - private SocialClientService socialClientService; - - @Override - public CommonResult<String> getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) { - return success(socialClientService.getAuthorizeUrl(socialType, userType, redirectUri)); - } - - @Override - public CommonResult<SocialWxJsapiSignatureRespDTO> createWxMpJsapiSignature(Integer userType, String url) { - WxJsapiSignature signature = socialClientService.createWxMpJsapiSignature(userType, url); - return success(BeanUtils.toBean(signature, SocialWxJsapiSignatureRespDTO.class)); - } - - @Override - public CommonResult<SocialWxPhoneNumberInfoRespDTO> getWxMaPhoneNumberInfo(Integer userType, String phoneCode) { - WxMaPhoneNumberInfo info = socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode); - return success(BeanUtils.toBean(info, SocialWxPhoneNumberInfoRespDTO.class)); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/social/SocialUserApiImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/social/SocialUserApiImpl.java deleted file mode 100644 index c8d03f6..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/social/SocialUserApiImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.iailab.module.system.api.social; - -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.module.system.api.social.dto.SocialUserBindReqDTO; -import com.iailab.module.system.api.social.dto.SocialUserRespDTO; -import com.iailab.module.system.api.social.dto.SocialUserUnbindReqDTO; -import com.iailab.module.system.service.social.SocialUserService; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; - -import static com.iailab.framework.common.pojo.CommonResult.success; - -@RestController // 提供 RESTful API 接口,给 Feign 调用 -@Validated -public class SocialUserApiImpl implements SocialUserApi { - - @Resource - private SocialUserService socialUserService; - - @Override - public CommonResult<String> bindSocialUser(SocialUserBindReqDTO reqDTO) { - return success(socialUserService.bindSocialUser(reqDTO)); - } - - @Override - public CommonResult<Boolean> unbindSocialUser(SocialUserUnbindReqDTO reqDTO) { - socialUserService.unbindSocialUser(reqDTO.getUserId(), reqDTO.getUserType(), - reqDTO.getSocialType(), reqDTO.getOpenid()); - return success(true); - } - - @Override - public CommonResult<SocialUserRespDTO> getSocialUserByUserId(Integer userType, Long userId, Integer socialType) { - return success(socialUserService.getSocialUserByUserId(userType, userId, socialType)); - } - - @Override - public CommonResult<SocialUserRespDTO> getSocialUserByCode(Integer userType, Integer socialType, String code, String state) { - return success(socialUserService.getSocialUserByCode(userType, socialType, code, state)); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/auth/AuthController.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/auth/AuthController.java index 2fcd758..c3c0e3c 100644 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/auth/AuthController.java +++ b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/auth/AuthController.java @@ -4,17 +4,12 @@ import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.StrUtil; import com.iailab.framework.common.enums.CommonStatusEnum; -import com.iailab.framework.common.enums.UserTypeEnum; import com.iailab.framework.common.pojo.CommonResult; import com.iailab.framework.common.util.object.BeanUtils; import com.iailab.framework.security.config.SecurityProperties; -import com.iailab.framework.security.core.LoginUser; import com.iailab.framework.security.core.util.SecurityFrameworkUtils; -import com.iailab.module.system.controller.admin.app.vo.AppMenuRespVO; import com.iailab.module.system.controller.admin.app.vo.AppRespVO; import com.iailab.module.system.controller.admin.auth.vo.*; -import com.iailab.module.system.controller.admin.permission.vo.menu.MenuListReqVO; -import com.iailab.module.system.controller.admin.permission.vo.menu.MenuRespVO; import com.iailab.module.system.convert.auth.AuthConvert; import com.iailab.module.system.dal.dataobject.app.AppDO; import com.iailab.module.system.dal.dataobject.permission.MenuDO; @@ -27,14 +22,11 @@ import com.iailab.module.system.service.permission.MenuService; import com.iailab.module.system.service.permission.PermissionService; import com.iailab.module.system.service.permission.RoleService; -import com.iailab.module.system.service.social.SocialClientService; import com.iailab.module.system.service.user.AdminUserService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.Authentication; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -68,8 +60,6 @@ private MenuService menuService; @Resource private PermissionService permissionService; - @Resource - private SocialClientService socialClientService; @Resource private SecurityProperties securityProperties; @Resource @@ -264,45 +254,6 @@ menuVOS = AuthConvert.INSTANCE.buildMenuTree(tempChildren, menuDO.getId(), menuDO.getPath(), info.getType()); // 2. 拼接结果返回 return success(menuVOS); - } - - // ========== 短信登录相关 ========== - - @PostMapping("/sms-login") - @PermitAll - @Operation(summary = "使用短信验证码登录") - public CommonResult<AuthLoginRespVO> smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) { - return success(authService.smsLogin(reqVO)); - } - - @PostMapping("/send-sms-code") - @PermitAll - @Operation(summary = "发送手机验证码") - public CommonResult<Boolean> sendLoginSmsCode(@RequestBody @Valid AuthSmsSendReqVO reqVO) { - authService.sendSmsCode(reqVO); - return success(true); - } - - // ========== 社交登录相关 ========== - - @GetMapping("/social-auth-redirect") - @PermitAll - @Operation(summary = "社交授权的跳转") - @Parameters({ - @Parameter(name = "type", description = "社交类型", required = true), - @Parameter(name = "redirectUri", description = "回调路径") - }) - public CommonResult<String> socialLogin(@RequestParam("type") Integer type, - @RequestParam("redirectUri") String redirectUri) { - return success(socialClientService.getAuthorizeUrl( - type, UserTypeEnum.ADMIN.getValue(), redirectUri)); - } - - @PostMapping("/social-login") - @PermitAll - @Operation(summary = "社交快捷登录,使用 code 授权码", description = "适合未登录的用户,但是社交账号已绑定用户") - public CommonResult<AuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AuthSocialLoginReqVO reqVO) { - return success(authService.socialLogin(reqVO)); } } diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsCallbackController.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsCallbackController.java deleted file mode 100644 index 8eb8495..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsCallbackController.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.iailab.module.system.controller.admin.sms; - -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.util.servlet.ServletUtils; -import com.iailab.module.system.framework.sms.core.enums.SmsChannelEnum; -import com.iailab.module.system.service.sms.SmsSendService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; -import javax.annotation.security.PermitAll; -import javax.servlet.http.HttpServletRequest; - -import static com.iailab.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 短信回调") -@RestController -@RequestMapping("/system/sms/callback") -public class SmsCallbackController { - - @Resource - private SmsSendService smsSendService; - - @PostMapping("/aliyun") - @PermitAll - @Operation(summary = "阿里云短信的回调", description = "参见 https://help.aliyun.com/zh/sms/developer-reference/configure-delivery-receipts-1 文档") - public CommonResult<Boolean> receiveAliyunSmsStatus(HttpServletRequest request) throws Throwable { - String text = ServletUtils.getBody(request); - smsSendService.receiveSmsStatus(SmsChannelEnum.ALIYUN.getCode(), text); - return success(true); - } - - @PostMapping("/tencent") - @PermitAll - @Operation(summary = "腾讯云短信的回调", description = "参见 https://cloud.tencent.com/document/product/382/59178 文档") - public CommonResult<Boolean> receiveTencentSmsStatus(HttpServletRequest request) throws Throwable { - String text = ServletUtils.getBody(request); - smsSendService.receiveSmsStatus(SmsChannelEnum.TENCENT.getCode(), text); - return success(true); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsChannelController.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsChannelController.java deleted file mode 100644 index 577245e..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsChannelController.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.iailab.module.system.controller.admin.sms; - -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.common.util.object.BeanUtils; -import com.iailab.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; -import com.iailab.module.system.controller.admin.sms.vo.channel.SmsChannelRespVO; -import com.iailab.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO; -import com.iailab.module.system.controller.admin.sms.vo.channel.SmsChannelSimpleRespVO; -import com.iailab.module.system.dal.dataobject.sms.SmsChannelDO; -import com.iailab.module.system.service.sms.SmsChannelService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.util.Comparator; -import java.util.List; - -import static com.iailab.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 短信渠道") -@RestController -@RequestMapping("system/sms-channel") -public class SmsChannelController { - - @Resource - private SmsChannelService smsChannelService; - - @PostMapping("/create") - @Operation(summary = "创建短信渠道") - @PreAuthorize("@ss.hasPermission('system:sms-channel:create')") - public CommonResult<Long> createSmsChannel(@Valid @RequestBody SmsChannelSaveReqVO createReqVO) { - return success(smsChannelService.createSmsChannel(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新短信渠道") - @PreAuthorize("@ss.hasPermission('system:sms-channel:update')") - public CommonResult<Boolean> updateSmsChannel(@Valid @RequestBody SmsChannelSaveReqVO updateReqVO) { - smsChannelService.updateSmsChannel(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除短信渠道") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('system:sms-channel:delete')") - public CommonResult<Boolean> deleteSmsChannel(@RequestParam("id") Long id) { - smsChannelService.deleteSmsChannel(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得短信渠道") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:sms-channel:query')") - public CommonResult<SmsChannelRespVO> getSmsChannel(@RequestParam("id") Long id) { - SmsChannelDO channel = smsChannelService.getSmsChannel(id); - return success(BeanUtils.toBean(channel, SmsChannelRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得短信渠道分页") - @PreAuthorize("@ss.hasPermission('system:sms-channel:query')") - public CommonResult<PageResult<SmsChannelRespVO>> getSmsChannelPage(@Valid SmsChannelPageReqVO pageVO) { - PageResult<SmsChannelDO> pageResult = smsChannelService.getSmsChannelPage(pageVO); - return success(BeanUtils.toBean(pageResult, SmsChannelRespVO.class)); - } - - @GetMapping({"/list-all-simple", "/simple-list"}) - @Operation(summary = "获得短信渠道精简列表", description = "包含被禁用的短信渠道") - public CommonResult<List<SmsChannelSimpleRespVO>> getSimpleSmsChannelList() { - List<SmsChannelDO> list = smsChannelService.getSmsChannelList(); - list.sort(Comparator.comparing(SmsChannelDO::getId)); - return success(BeanUtils.toBean(list, SmsChannelSimpleRespVO.class)); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsLogController.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsLogController.java deleted file mode 100644 index 0fd6893..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsLogController.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.iailab.module.system.controller.admin.sms; - -import com.iailab.framework.apilog.core.annotation.ApiAccessLog; -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.pojo.PageParam; -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.common.util.object.BeanUtils; -import com.iailab.framework.excel.core.util.ExcelUtils; -import com.iailab.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; -import com.iailab.module.system.controller.admin.sms.vo.log.SmsLogRespVO; -import com.iailab.module.system.dal.dataobject.sms.SmsLogDO; -import com.iailab.module.system.service.sms.SmsLogService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; -import java.io.IOException; -import java.util.List; - -import static com.iailab.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static com.iailab.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 短信日志") -@RestController -@RequestMapping("/system/sms-log") -@Validated -public class SmsLogController { - - @Resource - private SmsLogService smsLogService; - - @GetMapping("/page") - @Operation(summary = "获得短信日志分页") - @PreAuthorize("@ss.hasPermission('system:sms-log:query')") - public CommonResult<PageResult<SmsLogRespVO>> getSmsLogPage(@Valid SmsLogPageReqVO pageReqVO) { - PageResult<SmsLogDO> pageResult = smsLogService.getSmsLogPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, SmsLogRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出短信日志 Excel") - @PreAuthorize("@ss.hasPermission('system:sms-log:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportSmsLogExcel(@Valid SmsLogPageReqVO exportReqVO, - HttpServletResponse response) throws IOException { - exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List<SmsLogDO> list = smsLogService.getSmsLogPage(exportReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "短信日志.xls", "数据", SmsLogRespVO.class, - BeanUtils.toBean(list, SmsLogRespVO.class)); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsTemplateController.http b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsTemplateController.http deleted file mode 100644 index ee24e92..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsTemplateController.http +++ /dev/null @@ -1,14 +0,0 @@ -### 请求 /system/sms-template/send-sms 接口 => 成功 -POST {{baseUrl}}/system/sms-template/send-sms -Authorization: Bearer {{token}} -Content-Type: application/json -tenant-id: {{adminTenentId}} - -{ - "templateCode": "test_01", - "mobile": "15601691390", - "templateParams": { - "operation": "value01", - "code": "value02" - } -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsTemplateController.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsTemplateController.java deleted file mode 100644 index 752d15a..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/SmsTemplateController.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.iailab.module.system.controller.admin.sms; - -import com.iailab.framework.apilog.core.annotation.ApiAccessLog; -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.pojo.PageParam; -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.common.util.object.BeanUtils; -import com.iailab.framework.excel.core.util.ExcelUtils; -import com.iailab.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; -import com.iailab.module.system.controller.admin.sms.vo.template.SmsTemplateRespVO; -import com.iailab.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO; -import com.iailab.module.system.controller.admin.sms.vo.template.SmsTemplateSendReqVO; -import com.iailab.module.system.dal.dataobject.sms.SmsTemplateDO; -import com.iailab.module.system.service.sms.SmsSendService; -import com.iailab.module.system.service.sms.SmsTemplateService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; -import java.io.IOException; -import java.util.List; - -import static com.iailab.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static com.iailab.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 短信模板") -@RestController -@RequestMapping("/system/sms-template") -public class SmsTemplateController { - - @Resource - private SmsTemplateService smsTemplateService; - @Resource - private SmsSendService smsSendService; - - @PostMapping("/create") - @Operation(summary = "创建短信模板") - @PreAuthorize("@ss.hasPermission('system:sms-template:create')") - public CommonResult<Long> createSmsTemplate(@Valid @RequestBody SmsTemplateSaveReqVO createReqVO) { - return success(smsTemplateService.createSmsTemplate(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新短信模板") - @PreAuthorize("@ss.hasPermission('system:sms-template:update')") - public CommonResult<Boolean> updateSmsTemplate(@Valid @RequestBody SmsTemplateSaveReqVO updateReqVO) { - smsTemplateService.updateSmsTemplate(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除短信模板") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('system:sms-template:delete')") - public CommonResult<Boolean> deleteSmsTemplate(@RequestParam("id") Long id) { - smsTemplateService.deleteSmsTemplate(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得短信模板") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:sms-template:query')") - public CommonResult<SmsTemplateRespVO> getSmsTemplate(@RequestParam("id") Long id) { - SmsTemplateDO template = smsTemplateService.getSmsTemplate(id); - return success(BeanUtils.toBean(template, SmsTemplateRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得短信模板分页") - @PreAuthorize("@ss.hasPermission('system:sms-template:query')") - public CommonResult<PageResult<SmsTemplateRespVO>> getSmsTemplatePage(@Valid SmsTemplatePageReqVO pageVO) { - PageResult<SmsTemplateDO> pageResult = smsTemplateService.getSmsTemplatePage(pageVO); - return success(BeanUtils.toBean(pageResult, SmsTemplateRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出短信模板 Excel") - @PreAuthorize("@ss.hasPermission('system:sms-template:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportSmsTemplateExcel(@Valid SmsTemplatePageReqVO exportReqVO, - HttpServletResponse response) throws IOException { - exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List<SmsTemplateDO> list = smsTemplateService.getSmsTemplatePage(exportReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "短信模板.xls", "数据", SmsTemplateRespVO.class, - BeanUtils.toBean(list, SmsTemplateRespVO.class)); - } - - @PostMapping("/send-sms") - @Operation(summary = "发送短信") - @PreAuthorize("@ss.hasPermission('system:sms-template:send-sms')") - public CommonResult<Long> sendSms(@Valid @RequestBody SmsTemplateSendReqVO sendReqVO) { - return success(smsSendService.sendSingleSmsToAdmin(sendReqVO.getMobile(), null, - sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams())); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/channel/SmsChannelPageReqVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/channel/SmsChannelPageReqVO.java deleted file mode 100644 index f719478..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/channel/SmsChannelPageReqVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.iailab.module.system.controller.admin.sms.vo.channel; - -import com.iailab.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 短信渠道分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SmsChannelPageReqVO extends PageParam { - - @Schema(description = "任务状态", example = "1") - private Integer status; - - @Schema(description = "短信签名,模糊匹配", example = "iailab") - private String signature; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "创建时间") - private LocalDateTime[] createTime; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/channel/SmsChannelRespVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/channel/SmsChannelRespVO.java deleted file mode 100644 index 6f6d006..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/channel/SmsChannelRespVO.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.iailab.module.system.controller.admin.sms.vo.channel; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.hibernate.validator.constraints.URL; - -import javax.validation.constraints.NotNull; -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 短信渠道 Response VO") -@Data -public class SmsChannelRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "短信签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "iailab") - @NotNull(message = "短信签名不能为空") - private String signature; - - @Schema(description = "渠道编码,参见 SmsChannelEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "YUN_PIAN") - private String code; - - @Schema(description = "启用状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "启用状态不能为空") - private Integer status; - - @Schema(description = "备注", example = "好吃!") - private String remark; - - @Schema(description = "短信 API 的账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "iailab") - @NotNull(message = "短信 API 的账号不能为空") - private String apiKey; - - @Schema(description = "短信 API 的密钥", example = "yuanma") - private String apiSecret; - - @Schema(description = "短信发送回调 URL", example = "https://www.baidu.com") - @URL(message = "回调 URL 格式不正确") - private String callbackUrl; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/channel/SmsChannelSaveReqVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/channel/SmsChannelSaveReqVO.java deleted file mode 100644 index ae78ff2..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/channel/SmsChannelSaveReqVO.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.iailab.module.system.controller.admin.sms.vo.channel; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.hibernate.validator.constraints.URL; - -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 短信渠道创建/修改 Request VO") -@Data -public class SmsChannelSaveReqVO { - - @Schema(description = "编号", example = "1024") - private Long id; - - @Schema(description = "短信签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "iailab") - @NotNull(message = "短信签名不能为空") - private String signature; - - @Schema(description = "渠道编码,参见 SmsChannelEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "YUN_PIAN") - @NotNull(message = "渠道编码不能为空") - private String code; - - @Schema(description = "启用状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "启用状态不能为空") - private Integer status; - - @Schema(description = "备注", example = "好吃!") - private String remark; - - @Schema(description = "短信 API 的账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "iailab") - @NotNull(message = "短信 API 的账号不能为空") - private String apiKey; - - @Schema(description = "短信 API 的密钥", example = "yuanma") - private String apiSecret; - - @Schema(description = "短信发送回调 URL", example = "") - @URL(message = "回调 URL 格式不正确") - private String callbackUrl; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/channel/SmsChannelSimpleRespVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/channel/SmsChannelSimpleRespVO.java deleted file mode 100644 index 1b441c8..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/channel/SmsChannelSimpleRespVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.iailab.module.system.controller.admin.sms.vo.channel; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 短信渠道精简 Response VO") -@Data -public class SmsChannelSimpleRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "短信签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "iailab") - private String signature; - - @Schema(description = "渠道编码,参见 SmsChannelEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "YUN_PIAN") - private String code; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/log/SmsLogPageReqVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/log/SmsLogPageReqVO.java deleted file mode 100644 index 86360ba..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/log/SmsLogPageReqVO.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.iailab.module.system.controller.admin.sms.vo.log; - -import com.iailab.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 短信日志分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SmsLogPageReqVO extends PageParam { - - @Schema(description = "短信渠道编号", example = "10") - private Long channelId; - - @Schema(description = "模板编号", example = "20") - private Long templateId; - - @Schema(description = "手机号", example = "15601691300") - private String mobile; - - @Schema(description = "发送状态,参见 SmsSendStatusEnum 枚举类", example = "1") - private Integer sendStatus; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "发送时间") - private LocalDateTime[] sendTime; - - @Schema(description = "接收状态,参见 SmsReceiveStatusEnum 枚举类", example = "0") - private Integer receiveStatus; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "接收时间") - private LocalDateTime[] receiveTime; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java deleted file mode 100644 index 99e5570..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.iailab.module.system.controller.admin.sms.vo.log; - -import com.iailab.framework.excel.core.annotations.DictFormat; -import com.iailab.framework.excel.core.convert.DictConvert; -import com.iailab.framework.excel.core.convert.JsonConvert; -import com.iailab.module.system.enums.DictTypeConstants; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.Map; - -@Schema(description = "管理后台 - 短信日志 Response VO") -@Data -@ExcelIgnoreUnannotated -public class SmsLogRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "短信渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @ExcelProperty("短信渠道编号") - private Long channelId; - - @Schema(description = "短信渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "ALIYUN") - @ExcelProperty("短信渠道编码") - private String channelCode; - - @Schema(description = "模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") - @ExcelProperty("模板编号") - private Long templateId; - - @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test-01") - @ExcelProperty("模板编码") - private String templateCode; - - @Schema(description = "短信类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "短信类型", converter = DictConvert.class) - @DictFormat(DictTypeConstants.SMS_TEMPLATE_TYPE) - private Integer templateType; - - @Schema(description = "短信内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你的验证码是 1024") - @ExcelProperty("短信内容") - private String templateContent; - - @Schema(description = "短信参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "name,code") - @ExcelProperty(value = "短信参数", converter = JsonConvert.class) - private Map<String, Object> templateParams; - - @Schema(description = "短信 API 的模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "SMS_207945135") - @ExcelProperty("短信 API 的模板编号") - private String apiTemplateId; - - @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") - @ExcelProperty("手机号") - private String mobile; - - @Schema(description = "用户编号", example = "10") - @ExcelProperty("用户编号") - private Long userId; - - @Schema(description = "用户类型", example = "1") - @ExcelProperty(value = "用户类型", converter = DictConvert.class) - @DictFormat(DictTypeConstants.USER_TYPE) - private Integer userType; - - @Schema(description = "发送状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "发送状态", converter = DictConvert.class) - @DictFormat(DictTypeConstants.SMS_SEND_STATUS) - private Integer sendStatus; - - @Schema(description = "发送时间") - @ExcelProperty("发送时间") - private LocalDateTime sendTime; - - @Schema(description = "短信 API 发送结果的编码", example = "SUCCESS") - @ExcelProperty("短信 API 发送结果的编码") - private String apiSendCode; - - @Schema(description = "短信 API 发送失败的提示", example = "成功") - @ExcelProperty("短信 API 发送失败的提示") - private String apiSendMsg; - - @Schema(description = "短信 API 发送返回的唯一请求 ID", example = "3837C6D3-B96F-428C-BBB2-86135D4B5B99") - @ExcelProperty("短信 API 发送返回的唯一请求 ID") - private String apiRequestId; - - @Schema(description = "短信 API 发送返回的序号", example = "62923244790") - @ExcelProperty("短信 API 发送返回的序号") - private String apiSerialNo; - - @Schema(description = "接收状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - @ExcelProperty(value = "接收状态", converter = DictConvert.class) - @DictFormat(DictTypeConstants.SMS_RECEIVE_STATUS) - private Integer receiveStatus; - - @Schema(description = "接收时间") - @ExcelProperty("接收时间") - private LocalDateTime receiveTime; - - @Schema(description = "API 接收结果的编码", example = "DELIVRD") - @ExcelProperty("API 接收结果的编码") - private String apiReceiveCode; - - @Schema(description = "API 接收结果的说明", example = "用户接收成功") - @ExcelProperty("API 接收结果的说明") - private String apiReceiveMsg; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/template/SmsTemplatePageReqVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/template/SmsTemplatePageReqVO.java deleted file mode 100644 index 364f5ff..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/template/SmsTemplatePageReqVO.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.iailab.module.system.controller.admin.sms.vo.template; - -import com.iailab.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 短信模板分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SmsTemplatePageReqVO extends PageParam { - - @Schema(description = "短信签名", example = "1") - private Integer type; - - @Schema(description = "开启状态", example = "1") - private Integer status; - - @Schema(description = "模板编码,模糊匹配", example = "test_01") - private String code; - - @Schema(description = "模板内容,模糊匹配", example = "你好,{name}。你长的太{like}啦!") - private String content; - - @Schema(description = "短信 API 的模板编号,模糊匹配", example = "4383920") - private String apiTemplateId; - - @Schema(description = "短信渠道编号", example = "10") - private Long channelId; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "创建时间") - private LocalDateTime[] createTime; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/template/SmsTemplateRespVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/template/SmsTemplateRespVO.java deleted file mode 100644 index deb9e40..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/template/SmsTemplateRespVO.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.iailab.module.system.controller.admin.sms.vo.template; - -import com.iailab.framework.excel.core.annotations.DictFormat; -import com.iailab.framework.excel.core.convert.DictConvert; -import com.iailab.module.system.enums.DictTypeConstants; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "管理后台 - 短信模板 Response VO") -@Data -@ExcelIgnoreUnannotated -public class SmsTemplateRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "短信类型,参见 SmsTemplateTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "短信签名", converter = DictConvert.class) - @DictFormat(DictTypeConstants.SMS_TEMPLATE_TYPE) - private Integer type; - - @Schema(description = "开启状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "开启状态", converter = DictConvert.class) - @DictFormat(DictTypeConstants.COMMON_STATUS) - private Integer status; - - @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") - @ExcelProperty("模板编码") - private String code; - - @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "iailab") - @ExcelProperty("模板名称") - private String name; - - @Schema(description = "模板内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,{name}。你长的太{like}啦!") - @ExcelProperty("模板内容") - private String content; - - @Schema(description = "参数数组", example = "name,code") - private List<String> params; - - @Schema(description = "备注", example = "哈哈哈") - @ExcelProperty("备注") - private String remark; - - @Schema(description = "短信 API 的模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4383920") - @ExcelProperty("短信 API 的模板编号") - private String apiTemplateId; - - @Schema(description = "短信渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @ExcelProperty("短信渠道编号") - private Long channelId; - - @Schema(description = "短信渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "ALIYUN") - @ExcelProperty(value = "短信渠道编码", converter = DictConvert.class) - @DictFormat(DictTypeConstants.SMS_CHANNEL_CODE) - private String channelCode; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/template/SmsTemplateSaveReqVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/template/SmsTemplateSaveReqVO.java deleted file mode 100644 index 52a8695..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/template/SmsTemplateSaveReqVO.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.iailab.module.system.controller.admin.sms.vo.template; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 短信模板创建/修改 Request VO") -@Data -public class SmsTemplateSaveReqVO { - - @Schema(description = "编号", example = "1024") - private Long id; - - @Schema(description = "短信类型,参见 SmsTemplateTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "短信类型不能为空") - private Integer type; - - @Schema(description = "开启状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "开启状态不能为空") - private Integer status; - - @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") - @NotNull(message = "模板编码不能为空") - private String code; - - @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "iailab") - @NotNull(message = "模板名称不能为空") - private String name; - - @Schema(description = "模板内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,{name}。你长的太{like}啦!") - @NotNull(message = "模板内容不能为空") - private String content; - - @Schema(description = "备注", example = "哈哈哈") - private String remark; - - @Schema(description = "短信 API 的模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4383920") - @NotNull(message = "短信 API 的模板编号不能为空") - private String apiTemplateId; - - @Schema(description = "短信渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @NotNull(message = "短信渠道编号不能为空") - private Long channelId; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/template/SmsTemplateSendReqVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/template/SmsTemplateSendReqVO.java deleted file mode 100644 index 3631878..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/sms/vo/template/SmsTemplateSendReqVO.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.iailab.module.system.controller.admin.sms.vo.template; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; -import java.util.Map; - -@Schema(description = "管理后台 - 短信模板的发送 Request VO") -@Data -public class SmsTemplateSendReqVO { - - @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") - @NotNull(message = "手机号不能为空") - private String mobile; - - @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") - @NotNull(message = "模板编码不能为空") - private String templateCode; - - @Schema(description = "模板参数") - private Map<String, Object> templateParams; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/SocialClientController.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/SocialClientController.java deleted file mode 100644 index 685119e..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/SocialClientController.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.iailab.module.system.controller.admin.socail; - -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.common.util.object.BeanUtils; -import com.iailab.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; -import com.iailab.module.system.controller.admin.socail.vo.client.SocialClientRespVO; -import com.iailab.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; -import com.iailab.module.system.dal.dataobject.social.SocialClientDO; -import com.iailab.module.system.service.social.SocialClientService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; - -import static com.iailab.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 社交客户端") -@RestController -@RequestMapping("/system/social-client") -@Validated -public class SocialClientController { - - @Resource - private SocialClientService socialClientService; - - @PostMapping("/create") - @Operation(summary = "创建社交客户端") - @PreAuthorize("@ss.hasPermission('system:social-client:create')") - public CommonResult<Long> createSocialClient(@Valid @RequestBody SocialClientSaveReqVO createReqVO) { - return success(socialClientService.createSocialClient(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新社交客户端") - @PreAuthorize("@ss.hasPermission('system:social-client:update')") - public CommonResult<Boolean> updateSocialClient(@Valid @RequestBody SocialClientSaveReqVO updateReqVO) { - socialClientService.updateSocialClient(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除社交客户端") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('system:social-client:delete')") - public CommonResult<Boolean> deleteSocialClient(@RequestParam("id") Long id) { - socialClientService.deleteSocialClient(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得社交客户端") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:social-client:query')") - public CommonResult<SocialClientRespVO> getSocialClient(@RequestParam("id") Long id) { - SocialClientDO client = socialClientService.getSocialClient(id); - return success(BeanUtils.toBean(client, SocialClientRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得社交客户端分页") - @PreAuthorize("@ss.hasPermission('system:social-client:query')") - public CommonResult<PageResult<SocialClientRespVO>> getSocialClientPage(@Valid SocialClientPageReqVO pageVO) { - PageResult<SocialClientDO> pageResult = socialClientService.getSocialClientPage(pageVO); - return success(BeanUtils.toBean(pageResult, SocialClientRespVO.class)); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/SocialUserController.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/SocialUserController.java deleted file mode 100644 index 78cd6ee..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/SocialUserController.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.iailab.module.system.controller.admin.socail; - -import com.iailab.framework.common.enums.UserTypeEnum; -import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.common.util.object.BeanUtils; -import com.iailab.module.system.controller.admin.socail.vo.user.SocialUserBindReqVO; -import com.iailab.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; -import com.iailab.module.system.controller.admin.socail.vo.user.SocialUserRespVO; -import com.iailab.module.system.controller.admin.socail.vo.user.SocialUserUnbindReqVO; -import com.iailab.module.system.convert.social.SocialUserConvert; -import com.iailab.module.system.dal.dataobject.social.SocialUserDO; -import com.iailab.module.system.service.social.SocialUserService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; - -import static com.iailab.framework.common.pojo.CommonResult.success; -import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -@Tag(name = "管理后台 - 社交用户") -@RestController -@RequestMapping("/system/social-user") -@Validated -public class SocialUserController { - - @Resource - private SocialUserService socialUserService; - - @PostMapping("/bind") - @Operation(summary = "社交绑定,使用 code 授权码") - public CommonResult<Boolean> socialBind(@RequestBody @Valid SocialUserBindReqVO reqVO) { - socialUserService.bindSocialUser(SocialUserConvert.INSTANCE.convert( - getLoginUserId(), UserTypeEnum.ADMIN.getValue(), reqVO)); - return CommonResult.success(true); - } - - @DeleteMapping("/unbind") - @Operation(summary = "取消社交绑定") - public CommonResult<Boolean> socialUnbind(@RequestBody SocialUserUnbindReqVO reqVO) { - socialUserService.unbindSocialUser(getLoginUserId(), UserTypeEnum.ADMIN.getValue(), reqVO.getType(), reqVO.getOpenid()); - return CommonResult.success(true); - } - - // ==================== 社交用户 CRUD ==================== - - @GetMapping("/get") - @Operation(summary = "获得社交用户") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:social-user:query')") - public CommonResult<SocialUserRespVO> getSocialUser(@RequestParam("id") Long id) { - SocialUserDO socialUser = socialUserService.getSocialUser(id); - return success(BeanUtils.toBean(socialUser, SocialUserRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得社交用户分页") - @PreAuthorize("@ss.hasPermission('system:social-user:query')") - public CommonResult<PageResult<SocialUserRespVO>> getSocialUserPage(@Valid SocialUserPageReqVO pageVO) { - PageResult<SocialUserDO> pageResult = socialUserService.getSocialUserPage(pageVO); - return success(BeanUtils.toBean(pageResult, SocialUserRespVO.class)); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/client/SocialClientPageReqVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/client/SocialClientPageReqVO.java deleted file mode 100644 index 6f41fe1..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/client/SocialClientPageReqVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.iailab.module.system.controller.admin.socail.vo.client; - -import com.iailab.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -@Schema(description = "管理后台 - 社交客户端分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SocialClientPageReqVO extends PageParam { - - @Schema(description = "应用名", example = "iailab商城") - private String name; - - @Schema(description = "社交平台的类型", example = "31") - private Integer socialType; - - @Schema(description = "用户类型", example = "2") - private Integer userType; - - @Schema(description = "客户端编号", example = "145442115") - private String clientId; - - @Schema(description = "状态", example = "1") - private Integer status; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/client/SocialClientRespVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/client/SocialClientRespVO.java deleted file mode 100644 index 470bd35..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/client/SocialClientRespVO.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.iailab.module.system.controller.admin.socail.vo.client; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 社交客户端 Response VO") -@Data -public class SocialClientRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "27162") - private Long id; - - @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "iailab商城") - private String name; - - @Schema(description = "社交平台的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "31") - private Integer socialType; - - @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private Integer userType; - - @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "wwd411c69a39ad2e54") - private String clientId; - - @Schema(description = "客户端密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "peter") - private String clientSecret; - - @Schema(description = "授权方的网页应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000045") - private String agentId; - - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/client/SocialClientSaveReqVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/client/SocialClientSaveReqVO.java deleted file mode 100644 index 8866c64..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/client/SocialClientSaveReqVO.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.iailab.module.system.controller.admin.socail.vo.client; - -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.enums.CommonStatusEnum; -import com.iailab.framework.common.enums.UserTypeEnum; -import com.iailab.framework.common.validation.InEnum; -import com.iailab.module.system.enums.social.SocialTypeEnum; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.AssertTrue; -import javax.validation.constraints.NotNull; -import java.util.Objects; - -@Schema(description = "管理后台 - 社交客户端创建/修改 Request VO") -@Data -public class SocialClientSaveReqVO { - - @Schema(description = "编号", example = "27162") - private Long id; - - @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "iailab商城") - @NotNull(message = "应用名不能为空") - private String name; - - @Schema(description = "社交平台的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "31") - @NotNull(message = "社交平台的类型不能为空") - @InEnum(SocialTypeEnum.class) - private Integer socialType; - - @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @NotNull(message = "用户类型不能为空") - @InEnum(UserTypeEnum.class) - private Integer userType; - - @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "wwd411c69a39ad2e54") - @NotNull(message = "客户端编号不能为空") - private String clientId; - - @Schema(description = "客户端密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "peter") - @NotNull(message = "客户端密钥不能为空") - private String clientSecret; - - @Schema(description = "授权方的网页应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000045") - private String agentId; - - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @AssertTrue(message = "agentId 不能为空") - @JsonIgnore - public boolean isAgentIdValid() { - // 如果是企业微信,必须填写 agentId 属性 - return !Objects.equals(socialType, SocialTypeEnum.WECHAT_ENTERPRISE.getType()) - || !StrUtil.isEmpty(agentId); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java deleted file mode 100644 index ca9f9af..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.iailab.module.system.controller.admin.socail.vo.user; - -import com.iailab.framework.common.validation.InEnum; -import com.iailab.module.system.enums.social.SocialTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 社交绑定 Request VO,使用 code 授权码") -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class SocialUserBindReqVO { - - @Schema(description = "社交平台的类型,参见 UserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @InEnum(SocialTypeEnum.class) - @NotNull(message = "社交平台的类型不能为空") - private Integer type; - - @Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "授权码不能为空") - private String code; - - @Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") - @NotEmpty(message = "state 不能为空") - private String state; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java deleted file mode 100644 index e422277..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.iailab.module.system.controller.admin.socail.vo.user; - -import com.iailab.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 社交用户分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SocialUserPageReqVO extends PageParam { - - @Schema(description = "社交平台的类型", example = "30") - private Integer type; - - @Schema(description = "用户昵称", example = "李四") - private String nickname; - - @Schema(description = "社交 openid", example = "oz-Jdt0kd_jdhUxJHQdBJMlOFN7w") - private String openid; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/user/SocialUserRespVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/user/SocialUserRespVO.java deleted file mode 100644 index 07a0e34..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/user/SocialUserRespVO.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.iailab.module.system.controller.admin.socail.vo.user; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 社交用户 Response VO") -@Data -public class SocialUserRespVO { - - @Schema(description = "主键(自增策略)", requiredMode = Schema.RequiredMode.REQUIRED, example = "14569") - private Long id; - - @Schema(description = "社交平台的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "30") - private Integer type; - - @Schema(description = "社交 openid", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") - private String openid; - - @Schema(description = "社交 token", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") - private String token; - - @Schema(description = "原始 Token 数据,一般是 JSON 格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") - private String rawTokenInfo; - - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "iailab") - private String nickname; - - @Schema(description = "用户头像", example = "https://www.baidu.com/xxx.png") - private String avatar; - - @Schema(description = "原始用户数据,一般是 JSON 格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") - private String rawUserInfo; - - @Schema(description = "最后一次的认证 code", requiredMode = Schema.RequiredMode.REQUIRED, example = "666666") - private String code; - - @Schema(description = "最后一次的认证 state", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") - private String state; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - - @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime updateTime; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/user/SocialUserUnbindReqVO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/user/SocialUserUnbindReqVO.java deleted file mode 100644 index 40ca4fe..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/socail/vo/user/SocialUserUnbindReqVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.iailab.module.system.controller.admin.socail.vo.user; - -import com.iailab.framework.common.validation.InEnum; -import com.iailab.module.system.enums.social.SocialTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 取消社交绑定 Request VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class SocialUserUnbindReqVO { - - @Schema(description = "社交平台的类型,参见 UserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @InEnum(SocialTypeEnum.class) - @NotNull(message = "社交平台的类型不能为空") - private Integer type; - - @Schema(description = "社交用户的 openid", requiredMode = Schema.RequiredMode.REQUIRED, example = "IPRmJ0wvBptiPIlGEZiPewGwiEiE") - @NotEmpty(message = "社交用户的 openid 不能为空") - private String openid; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/user/UserProfileController.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/user/UserProfileController.java index 339a1ce..ec1b766 100644 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/user/UserProfileController.java +++ b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/controller/admin/user/UserProfileController.java @@ -1,7 +1,6 @@ package com.iailab.module.system.controller.admin.user; import cn.hutool.core.collection.CollUtil; -import com.iailab.framework.common.enums.UserTypeEnum; import com.iailab.framework.common.pojo.CommonResult; import com.iailab.framework.datapermission.core.annotation.DataPermission; import com.iailab.module.system.controller.admin.user.vo.profile.UserProfileRespVO; @@ -11,13 +10,11 @@ import com.iailab.module.system.dal.dataobject.dept.DeptDO; import com.iailab.module.system.dal.dataobject.dept.PostDO; import com.iailab.module.system.dal.dataobject.permission.RoleDO; -import com.iailab.module.system.dal.dataobject.social.SocialUserDO; import com.iailab.module.system.dal.dataobject.user.AdminUserDO; import com.iailab.module.system.service.dept.DeptService; import com.iailab.module.system.service.dept.PostService; import com.iailab.module.system.service.permission.PermissionService; import com.iailab.module.system.service.permission.RoleService; -import com.iailab.module.system.service.social.SocialUserService; import com.iailab.module.system.service.user.AdminUserService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -52,8 +49,6 @@ private PermissionService permissionService; @Resource private RoleService roleService; - @Resource - private SocialUserService socialService; @GetMapping("/get") @Operation(summary = "获得登录用户信息") @@ -68,8 +63,7 @@ // 获得岗位信息 List<PostDO> posts = CollUtil.isNotEmpty(user.getPostIds()) ? postService.getPostList(user.getPostIds()) : null; // 获得社交用户信息 - List<SocialUserDO> socialUsers = socialService.getSocialUserList(user.getId(), UserTypeEnum.ADMIN.getValue()); - return success(UserConvert.INSTANCE.convert(user, userRoles, dept, posts, socialUsers)); + return success(UserConvert.INSTANCE.convert(user, userRoles, dept, posts)); } @PutMapping("/update") diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/convert/auth/AuthConvert.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/convert/auth/AuthConvert.java index 5315a17..a14b65d 100644 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/convert/auth/AuthConvert.java +++ b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/convert/auth/AuthConvert.java @@ -2,8 +2,6 @@ import cn.hutool.core.collection.CollUtil; import com.iailab.framework.common.util.object.BeanUtils; -import com.iailab.module.system.api.sms.dto.code.SmsCodeSendReqDTO; -import com.iailab.module.system.api.sms.dto.code.SmsCodeUseReqDTO; import com.iailab.module.system.api.social.dto.SocialUserBindReqDTO; import com.iailab.module.system.controller.admin.auth.vo.*; import com.iailab.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; @@ -218,9 +216,5 @@ } SocialUserBindReqDTO convert(Long userId, Integer userType, AuthSocialLoginReqVO reqVO); - - SmsCodeSendReqDTO convert(AuthSmsSendReqVO reqVO); - - SmsCodeUseReqDTO convert(AuthSmsLoginReqVO reqVO, Integer scene, String usedIp); } diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/convert/social/SocialUserConvert.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/convert/social/SocialUserConvert.java deleted file mode 100644 index 435c9f0..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/convert/social/SocialUserConvert.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.iailab.module.system.convert.social; - -import com.iailab.module.system.api.social.dto.SocialUserBindReqDTO; -import com.iailab.module.system.controller.admin.socail.vo.user.SocialUserBindReqVO; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.factory.Mappers; - -@Mapper -public interface SocialUserConvert { - - SocialUserConvert INSTANCE = Mappers.getMapper(SocialUserConvert.class); - - @Mapping(source = "reqVO.type", target = "socialType") - SocialUserBindReqDTO convert(Long userId, Integer userType, SocialUserBindReqVO reqVO); - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/convert/user/UserConvert.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/convert/user/UserConvert.java index 3bbd788..2b19556 100644 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/convert/user/UserConvert.java +++ b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/convert/user/UserConvert.java @@ -12,7 +12,6 @@ import com.iailab.module.system.dal.dataobject.dept.DeptDO; import com.iailab.module.system.dal.dataobject.dept.PostDO; import com.iailab.module.system.dal.dataobject.permission.RoleDO; -import com.iailab.module.system.dal.dataobject.social.SocialUserDO; import com.iailab.module.system.dal.dataobject.user.AdminUserDO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; @@ -46,12 +45,11 @@ } default UserProfileRespVO convert(AdminUserDO user, List<RoleDO> userRoles, - DeptDO dept, List<PostDO> posts, List<SocialUserDO> socialUsers) { + DeptDO dept, List<PostDO> posts) { UserProfileRespVO userVO = BeanUtils.toBean(user, UserProfileRespVO.class); userVO.setRoles(BeanUtils.toBean(userRoles, RoleSimpleRespVO.class)); userVO.setDept(BeanUtils.toBean(dept, DeptSimpleRespVO.class)); userVO.setPosts(BeanUtils.toBean(posts, PostSimpleRespVO.class)); - userVO.setSocialUsers(BeanUtils.toBean(socialUsers, UserProfileRespVO.SocialUser.class)); return userVO; } diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/sms/SmsChannelDO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/sms/SmsChannelDO.java deleted file mode 100644 index 9a9ec12..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/sms/SmsChannelDO.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.iailab.module.system.dal.dataobject.sms; - -import com.iailab.framework.common.enums.CommonStatusEnum; -import com.iailab.framework.mybatis.core.dataobject.BaseDO; -import com.iailab.module.system.framework.sms.core.enums.SmsChannelEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -/** - * 短信渠道 DO - * - * @author zzf - * @since 2021-01-25 - */ -@TableName(value = "system_sms_channel", autoResultMap = true) -@KeySequence("system_sms_channel_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SmsChannelDO extends BaseDO { - - /** - * 渠道编号 - */ - private Long id; - /** - * 短信签名 - */ - private String signature; - /** - * 渠道编码 - * - * 枚举 {@link SmsChannelEnum} - */ - private String code; - /** - * 启用状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 备注 - */ - private String remark; - /** - * 短信 API 的账号 - */ - private String apiKey; - /** - * 短信 API 的密钥 - */ - private String apiSecret; - /** - * 短信发送回调 URL - */ - private String callbackUrl; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/sms/SmsCodeDO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/sms/SmsCodeDO.java deleted file mode 100644 index f0417f3..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/sms/SmsCodeDO.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.iailab.module.system.dal.dataobject.sms; - -import com.iailab.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -import java.time.LocalDateTime; - -/** - * 手机验证码 DO - * - * idx_mobile 索引:基于 {@link #mobile} 字段 - * - * @author iailab - */ -@TableName("system_sms_code") -@KeySequence("system_sms_code_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class SmsCodeDO extends BaseDO { - - /** - * 编号 - */ - private Long id; - /** - * 手机号 - */ - private String mobile; - /** - * 验证码 - */ - private String code; - /** - * 发送场景 - * - * 枚举 {@link SmsCodeDO} - */ - private Integer scene; - /** - * 创建 IP - */ - private String createIp; - /** - * 今日发送的第几条 - */ - private Integer todayIndex; - /** - * 是否使用 - */ - private Boolean used; - /** - * 使用时间 - */ - private LocalDateTime usedTime; - /** - * 使用 IP - */ - private String usedIp; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/sms/SmsLogDO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/sms/SmsLogDO.java deleted file mode 100644 index a0b027c..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/sms/SmsLogDO.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.iailab.module.system.dal.dataobject.sms; - -import com.iailab.framework.common.enums.UserTypeEnum; -import com.iailab.framework.mybatis.core.dataobject.BaseDO; -import com.iailab.module.system.enums.sms.SmsReceiveStatusEnum; -import com.iailab.module.system.enums.sms.SmsSendStatusEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.*; - -import java.time.LocalDateTime; -import java.util.Map; - -/** - * 短信日志 DO - * - * @author zzf - * @since 2021-01-25 - */ -@TableName(value = "system_sms_log", autoResultMap = true) -@KeySequence("system_sms_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class SmsLogDO extends BaseDO { - - /** - * 自增编号 - */ - private Long id; - - // ========= 渠道相关字段 ========= - - /** - * 短信渠道编号 - * - * 关联 {@link SmsChannelDO#getId()} - */ - private Long channelId; - /** - * 短信渠道编码 - * - * 冗余 {@link SmsChannelDO#getCode()} - */ - private String channelCode; - - // ========= 模板相关字段 ========= - - /** - * 模板编号 - * - * 关联 {@link SmsTemplateDO#getId()} - */ - private Long templateId; - /** - * 模板编码 - * - * 冗余 {@link SmsTemplateDO#getCode()} - */ - private String templateCode; - /** - * 短信类型 - * - * 冗余 {@link SmsTemplateDO#getType()} - */ - private Integer templateType; - /** - * 基于 {@link SmsTemplateDO#getContent()} 格式化后的内容 - */ - private String templateContent; - /** - * 基于 {@link SmsTemplateDO#getParams()} 输入后的参数 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private Map<String, Object> templateParams; - /** - * 短信 API 的模板编号 - * - * 冗余 {@link SmsTemplateDO#getApiTemplateId()} - */ - private String apiTemplateId; - - // ========= 手机相关字段 ========= - - /** - * 手机号 - */ - private String mobile; - /** - * 用户编号 - */ - private Long userId; - /** - * 用户类型 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer userType; - - // ========= 发送相关字段 ========= - - /** - * 发送状态 - * - * 枚举 {@link SmsSendStatusEnum} - */ - private Integer sendStatus; - /** - * 发送时间 - */ - private LocalDateTime sendTime; - /** - * 短信 API 发送结果的编码 - * - * 由于第三方的错误码可能是字符串,所以使用 String 类型 - */ - private String apiSendCode; - /** - * 短信 API 发送失败的提示 - */ - private String apiSendMsg; - /** - * 短信 API 发送返回的唯一请求 ID - * - * 用于和短信 API 进行定位于排错 - */ - private String apiRequestId; - /** - * 短信 API 发送返回的序号 - * - * 用于和短信 API 平台的发送记录关联 - */ - private String apiSerialNo; - - // ========= 接收相关字段 ========= - - /** - * 接收状态 - * - * 枚举 {@link SmsReceiveStatusEnum} - */ - private Integer receiveStatus; - /** - * 接收时间 - */ - private LocalDateTime receiveTime; - /** - * 短信 API 接收结果的编码 - */ - private String apiReceiveCode; - /** - * 短信 API 接收结果的提示 - */ - private String apiReceiveMsg; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/sms/SmsTemplateDO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/sms/SmsTemplateDO.java deleted file mode 100644 index 8fb046e..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/sms/SmsTemplateDO.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.iailab.module.system.dal.dataobject.sms; - -import com.iailab.module.system.enums.sms.SmsTemplateTypeEnum; -import com.iailab.framework.common.enums.CommonStatusEnum; -import com.iailab.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -import java.util.List; - -/** - * 短信模板 DO - * - * @author zzf - * @since 2021-01-25 - */ -@TableName(value = "system_sms_template", autoResultMap = true) -@KeySequence("system_sms_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SmsTemplateDO extends BaseDO { - - /** - * 自增编号 - */ - private Long id; - - // ========= 模板相关字段 ========= - - /** - * 短信类型 - * - * 枚举 {@link SmsTemplateTypeEnum} - */ - private Integer type; - /** - * 启用状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 模板编码,保证唯一 - */ - private String code; - /** - * 模板名称 - */ - private String name; - /** - * 模板内容 - * - * 内容的参数,使用 {} 包括,例如说 {name} - */ - private String content; - /** - * 参数数组(自动根据内容生成) - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List<String> params; - /** - * 备注 - */ - private String remark; - /** - * 短信 API 的模板编号 - */ - private String apiTemplateId; - - // ========= 渠道相关字段 ========= - - /** - * 短信渠道编号 - * - * 关联 {@link SmsChannelDO#getId()} - */ - private Long channelId; - /** - * 短信渠道编码 - * - * 冗余 {@link SmsChannelDO#getCode()} - */ - private String channelCode; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/social/SocialClientDO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/social/SocialClientDO.java deleted file mode 100644 index 53bf0b4..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/social/SocialClientDO.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.iailab.module.system.dal.dataobject.social; - -import com.iailab.framework.common.enums.CommonStatusEnum; -import com.iailab.framework.common.enums.UserTypeEnum; -import com.iailab.framework.tenant.core.db.TenantBaseDO; -import com.iailab.module.system.enums.social.SocialTypeEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.xingyuv.jushauth.config.AuthConfig; -import lombok.*; - -/** - * 社交客户端 DO - * - * 对应 {@link AuthConfig} 配置,满足不同租户,有自己的客户端配置,实现社交(三方)登录 - * - * @author iailab - */ -@TableName(value = "system_social_client", autoResultMap = true) -@KeySequence("system_social_client_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class SocialClientDO extends TenantBaseDO { - - /** - * 编号,自增 - */ - @TableId - private Long id; - /** - * 应用名 - */ - private String name; - /** - * 社交类型 - * - * 枚举 {@link SocialTypeEnum} - */ - private Integer socialType; - /** - * 用户类型 - * - * 目的:不同用户类型,对应不同的小程序,需要自己的配置 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer userType; - /** - * 状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - - /** - * 客户端 id - */ - private String clientId; - /** - * 客户端 Secret - */ - private String clientSecret; - - /** - * 代理编号 - * - * 目前只有部分“社交类型”在使用: - * 1. 企业微信:对应授权方的网页应用 ID - */ - private String agentId; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/social/SocialUserBindDO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/social/SocialUserBindDO.java deleted file mode 100644 index 7f02ff2..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/social/SocialUserBindDO.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.iailab.module.system.dal.dataobject.social; - -import com.iailab.framework.common.enums.UserTypeEnum; -import com.iailab.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -/** - * 社交用户的绑定 - * 即 {@link SocialUserDO} 与 UserDO 的关联表 - * - * @author iailab - */ -@TableName(value = "system_social_user_bind", autoResultMap = true) -@KeySequence("system_social_user_bind_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class SocialUserBindDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 关联的用户编号 - * - * 关联 UserDO 的编号 - */ - private Long userId; - /** - * 用户类型 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer userType; - - /** - * 社交平台的用户编号 - * - * 关联 {@link SocialUserDO#getId()} - */ - private Long socialUserId; - /** - * 社交平台的类型 - * - * 冗余 {@link SocialUserDO#getType()} - */ - private Integer socialType; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/social/SocialUserDO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/social/SocialUserDO.java deleted file mode 100644 index 3847f07..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/dataobject/social/SocialUserDO.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.iailab.module.system.dal.dataobject.social; - -import com.iailab.framework.mybatis.core.dataobject.BaseDO; -import com.iailab.module.system.enums.social.SocialTypeEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -/** - * 社交(三方)用户 - * - * @author weir - */ -@TableName(value = "system_social_user", autoResultMap = true) -@KeySequence("system_social_user_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class SocialUserDO extends BaseDO { - - /** - * 自增主键 - */ - @TableId - private Long id; - /** - * 社交平台的类型 - * - * 枚举 {@link SocialTypeEnum} - */ - private Integer type; - - /** - * 社交 openid - */ - private String openid; - /** - * 社交 token - */ - private String token; - /** - * 原始 Token 数据,一般是 JSON 格式 - */ - private String rawTokenInfo; - - /** - * 用户昵称 - */ - private String nickname; - /** - * 用户头像 - */ - private String avatar; - /** - * 原始用户数据,一般是 JSON 格式 - */ - private String rawUserInfo; - - /** - * 最后一次的认证 code - */ - private String code; - /** - * 最后一次的认证 state - */ - private String state; - -} - - diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/sms/SmsChannelMapper.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/sms/SmsChannelMapper.java deleted file mode 100644 index 48c4cdf..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/sms/SmsChannelMapper.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.module.system.dal.mysql.sms; - -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.mybatis.core.mapper.BaseMapperX; -import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.iailab.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; -import com.iailab.module.system.dal.dataobject.sms.SmsChannelDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> { - - default PageResult<SmsChannelDO> selectPage(SmsChannelPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX<SmsChannelDO>() - .likeIfPresent(SmsChannelDO::getSignature, reqVO.getSignature()) - .eqIfPresent(SmsChannelDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(SmsChannelDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(SmsChannelDO::getId)); - } - - default SmsChannelDO selectByCode(String code) { - return selectOne(SmsChannelDO::getCode, code); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/sms/SmsCodeMapper.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/sms/SmsCodeMapper.java deleted file mode 100644 index a0e8dab..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/sms/SmsCodeMapper.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.module.system.dal.mysql.sms; - -import com.iailab.framework.mybatis.core.mapper.BaseMapperX; -import com.iailab.framework.mybatis.core.query.QueryWrapperX; -import com.iailab.module.system.dal.dataobject.sms.SmsCodeDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface SmsCodeMapper extends BaseMapperX<SmsCodeDO> { - - /** - * 获得手机号的最后一个手机验证码 - * - * @param mobile 手机号 - * @param scene 发送场景,选填 - * @param code 验证码 选填 - * @return 手机验证码 - */ - default SmsCodeDO selectLastByMobile(String mobile, String code, Integer scene) { - return selectOne(new QueryWrapperX<SmsCodeDO>() - .eq("mobile", mobile) - .eqIfPresent("scene", scene) - .eqIfPresent("code", code) - .orderByDesc("id") - .limitN(1)); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/sms/SmsLogMapper.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/sms/SmsLogMapper.java deleted file mode 100644 index d1b7252..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/sms/SmsLogMapper.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.iailab.module.system.dal.mysql.sms; - -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.mybatis.core.mapper.BaseMapperX; -import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.iailab.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; -import com.iailab.module.system.dal.dataobject.sms.SmsLogDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface SmsLogMapper extends BaseMapperX<SmsLogDO> { - - default PageResult<SmsLogDO> selectPage(SmsLogPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX<SmsLogDO>() - .eqIfPresent(SmsLogDO::getChannelId, reqVO.getChannelId()) - .eqIfPresent(SmsLogDO::getTemplateId, reqVO.getTemplateId()) - .likeIfPresent(SmsLogDO::getMobile, reqVO.getMobile()) - .eqIfPresent(SmsLogDO::getSendStatus, reqVO.getSendStatus()) - .betweenIfPresent(SmsLogDO::getSendTime, reqVO.getSendTime()) - .eqIfPresent(SmsLogDO::getReceiveStatus, reqVO.getReceiveStatus()) - .betweenIfPresent(SmsLogDO::getReceiveTime, reqVO.getReceiveTime()) - .orderByDesc(SmsLogDO::getId)); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/sms/SmsTemplateMapper.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/sms/SmsTemplateMapper.java deleted file mode 100644 index 98789e5..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/sms/SmsTemplateMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.iailab.module.system.dal.mysql.sms; - -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.mybatis.core.mapper.BaseMapperX; -import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.iailab.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; -import com.iailab.module.system.dal.dataobject.sms.SmsTemplateDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface SmsTemplateMapper extends BaseMapperX<SmsTemplateDO> { - - default SmsTemplateDO selectByCode(String code) { - return selectOne(SmsTemplateDO::getCode, code); - } - - default PageResult<SmsTemplateDO> selectPage(SmsTemplatePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX<SmsTemplateDO>() - .eqIfPresent(SmsTemplateDO::getType, reqVO.getType()) - .eqIfPresent(SmsTemplateDO::getStatus, reqVO.getStatus()) - .likeIfPresent(SmsTemplateDO::getCode, reqVO.getCode()) - .likeIfPresent(SmsTemplateDO::getContent, reqVO.getContent()) - .likeIfPresent(SmsTemplateDO::getApiTemplateId, reqVO.getApiTemplateId()) - .eqIfPresent(SmsTemplateDO::getChannelId, reqVO.getChannelId()) - .betweenIfPresent(SmsTemplateDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(SmsTemplateDO::getId)); - } - - default Long selectCountByChannelId(Long channelId) { - return selectCount(SmsTemplateDO::getChannelId, channelId); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/social/SocialClientMapper.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/social/SocialClientMapper.java deleted file mode 100644 index 8110e24..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/social/SocialClientMapper.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iailab.module.system.dal.mysql.social; - -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.mybatis.core.mapper.BaseMapperX; -import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.iailab.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; -import com.iailab.module.system.dal.dataobject.social.SocialClientDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface SocialClientMapper extends BaseMapperX<SocialClientDO> { - - default SocialClientDO selectBySocialTypeAndUserType(Integer socialType, Integer userType) { - return selectOne(SocialClientDO::getSocialType, socialType, - SocialClientDO::getUserType, userType); - } - - default PageResult<SocialClientDO> selectPage(SocialClientPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX<SocialClientDO>() - .likeIfPresent(SocialClientDO::getName, reqVO.getName()) - .eqIfPresent(SocialClientDO::getSocialType, reqVO.getSocialType()) - .eqIfPresent(SocialClientDO::getUserType, reqVO.getUserType()) - .likeIfPresent(SocialClientDO::getClientId, reqVO.getClientId()) - .eqIfPresent(SocialClientDO::getStatus, reqVO.getStatus()) - .orderByDesc(SocialClientDO::getId)); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/social/SocialUserBindMapper.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/social/SocialUserBindMapper.java deleted file mode 100644 index 0ba92b5..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/social/SocialUserBindMapper.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.iailab.module.system.dal.mysql.social; - -import com.iailab.framework.mybatis.core.mapper.BaseMapperX; -import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.iailab.module.system.dal.dataobject.social.SocialUserBindDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -@Mapper -public interface SocialUserBindMapper extends BaseMapperX<SocialUserBindDO> { - - default void deleteByUserTypeAndUserIdAndSocialType(Integer userType, Long userId, Integer socialType) { - delete(new LambdaQueryWrapperX<SocialUserBindDO>() - .eq(SocialUserBindDO::getUserType, userType) - .eq(SocialUserBindDO::getUserId, userId) - .eq(SocialUserBindDO::getSocialType, socialType)); - } - - default void deleteByUserTypeAndSocialUserId(Integer userType, Long socialUserId) { - delete(new LambdaQueryWrapperX<SocialUserBindDO>() - .eq(SocialUserBindDO::getUserType, userType) - .eq(SocialUserBindDO::getSocialUserId, socialUserId)); - } - - default SocialUserBindDO selectByUserTypeAndSocialUserId(Integer userType, Long socialUserId) { - return selectOne(SocialUserBindDO::getUserType, userType, - SocialUserBindDO::getSocialUserId, socialUserId); - } - - default List<SocialUserBindDO> selectListByUserIdAndUserType(Long userId, Integer userType) { - return selectList(new LambdaQueryWrapperX<SocialUserBindDO>() - .eq(SocialUserBindDO::getUserId, userId) - .eq(SocialUserBindDO::getUserType, userType)); - } - - default SocialUserBindDO selectByUserIdAndUserTypeAndSocialType(Long userId, Integer userType, Integer socialType) { - return selectOne(new LambdaQueryWrapperX<SocialUserBindDO>() - .eq(SocialUserBindDO::getUserId, userId) - .eq(SocialUserBindDO::getUserType, userType) - .eq(SocialUserBindDO::getSocialType, socialType)); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/social/SocialUserMapper.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/social/SocialUserMapper.java deleted file mode 100644 index 9a801fb..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/social/SocialUserMapper.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.iailab.module.system.dal.mysql.social; - -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.mybatis.core.mapper.BaseMapperX; -import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.iailab.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; -import com.iailab.module.system.dal.dataobject.social.SocialUserDO; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface SocialUserMapper extends BaseMapperX<SocialUserDO> { - - default SocialUserDO selectByTypeAndCodeAnState(Integer type, String code, String state) { - return selectOne(new LambdaQueryWrapper<SocialUserDO>() - .eq(SocialUserDO::getType, type) - .eq(SocialUserDO::getCode, code) - .eq(SocialUserDO::getState, state)); - } - - default SocialUserDO selectByTypeAndOpenid(Integer type, String openid) { - return selectOne(new LambdaQueryWrapper<SocialUserDO>() - .eq(SocialUserDO::getType, type) - .eq(SocialUserDO::getOpenid, openid)); - } - - default PageResult<SocialUserDO> selectPage(SocialUserPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX<SocialUserDO>() - .eqIfPresent(SocialUserDO::getType, reqVO.getType()) - .likeIfPresent(SocialUserDO::getNickname, reqVO.getNickname()) - .likeIfPresent(SocialUserDO::getOpenid, reqVO.getOpenid()) - .betweenIfPresent(SocialUserDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(SocialUserDO::getId)); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/rpc/config/RpcConfiguration.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/rpc/config/RpcConfiguration.java index 751584e..c71cb83 100644 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/rpc/config/RpcConfiguration.java +++ b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/rpc/config/RpcConfiguration.java @@ -3,12 +3,13 @@ import com.iailab.module.infra.api.config.ConfigApi; import com.iailab.module.infra.api.db.DataSourceConfigServiceApi; import com.iailab.module.infra.api.file.FileApi; +import com.iailab.module.infra.api.monitor.MonitorApi; import com.iailab.module.infra.api.websocket.WebSocketSenderApi; import com.iailab.module.system.api.tenant.TenantApi; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) -@EnableFeignClients(clients = {FileApi.class, WebSocketSenderApi.class, DataSourceConfigServiceApi.class, ConfigApi.class, TenantApi.class}) +@EnableFeignClients(clients = {FileApi.class, MonitorApi.class, WebSocketSenderApi.class, DataSourceConfigServiceApi.class, ConfigApi.class, TenantApi.class}) public class RpcConfiguration { } diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/config/SmsCodeProperties.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/config/SmsCodeProperties.java deleted file mode 100644 index a6c68b5..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/config/SmsCodeProperties.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.iailab.module.system.framework.sms.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.validation.annotation.Validated; - -import javax.validation.constraints.NotNull; -import java.time.Duration; - -@ConfigurationProperties(prefix = "iailab.sms-code") -@Validated -@Data -public class SmsCodeProperties { - - /** - * 过期时间 - */ - @NotNull(message = "过期时间不能为空") - private Duration expireTimes; - /** - * 短信发送频率 - */ - @NotNull(message = "短信发送频率不能为空") - private Duration sendFrequency; - /** - * 每日发送最大数量 - */ - @NotNull(message = "每日发送最大数量不能为空") - private Integer sendMaximumQuantityPerDay; - /** - * 验证码最小值 - */ - @NotNull(message = "验证码最小值不能为空") - private Integer beginCode; - /** - * 验证码最大值 - */ - @NotNull(message = "验证码最大值不能为空") - private Integer endCode; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/config/SmsConfiguration.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/config/SmsConfiguration.java deleted file mode 100644 index b193136..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/config/SmsConfiguration.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.iailab.module.system.framework.sms.config; - -import com.iailab.module.system.framework.sms.core.client.SmsClientFactory; -import com.iailab.module.system.framework.sms.core.client.impl.SmsClientFactoryImpl; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * 短信配置类,包括短信客户端、短信验证码两部分 - * - * @author iailab - */ -@Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties(SmsCodeProperties.class) -public class SmsConfiguration { - - @Bean - public SmsClientFactory smsClientFactory() { - return new SmsClientFactoryImpl(); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/SmsClient.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/SmsClient.java deleted file mode 100644 index af964bb..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/SmsClient.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.iailab.module.system.framework.sms.core.client; - -import com.iailab.framework.common.core.KeyValue; -import com.iailab.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import com.iailab.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import com.iailab.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; - -import java.util.List; - -/** - * 短信客户端,用于对接各短信平台的 SDK,实现短信发送等功能 - * - * @author zzf - * @since 2021/1/25 14:14 - */ -public interface SmsClient { - - /** - * 获得渠道编号 - * - * @return 渠道编号 - */ - Long getId(); - - /** - * 发送消息 - * - * @param logId 日志编号 - * @param mobile 手机号 - * @param apiTemplateId 短信 API 的模板编号 - * @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序 - * @return 短信发送结果 - */ - SmsSendRespDTO sendSms(Long logId, String mobile, String apiTemplateId, - List<KeyValue<String, Object>> templateParams) throws Throwable; - - /** - * 解析接收短信的接收结果 - * - * @param text 结果 - * @return 结果内容 - * @throws Throwable 当解析 text 发生异常时,则会抛出异常 - */ - List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable; - - /** - * 查询指定的短信模板 - * - * @param apiTemplateId 短信 API 的模板编号 - * @return 短信模板 - */ - SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/SmsClientFactory.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/SmsClientFactory.java deleted file mode 100644 index f3f7e9e..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/SmsClientFactory.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.iailab.module.system.framework.sms.core.client; - -import com.iailab.module.system.framework.sms.core.property.SmsChannelProperties; - -/** - * 短信客户端的工厂接口 - * - * @author zzf - * @since 2021/1/28 14:01 - */ -public interface SmsClientFactory { - - /** - * 获得短信 Client - * - * @param channelId 渠道编号 - * @return 短信 Client - */ - SmsClient getSmsClient(Long channelId); - - /** - * 获得短信 Client - * - * @param channelCode 渠道编码 - * @return 短信 Client - */ - SmsClient getSmsClient(String channelCode); - - /** - * 创建短信 Client - * - * @param properties 配置对象 - */ - void createOrUpdateSmsClient(SmsChannelProperties properties); - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/dto/SmsReceiveRespDTO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/dto/SmsReceiveRespDTO.java deleted file mode 100644 index 03c0c25..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/dto/SmsReceiveRespDTO.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.iailab.module.system.framework.sms.core.client.dto; - -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * 消息接收 Response DTO - * - * @author iailab - */ -@Data -public class SmsReceiveRespDTO { - - /** - * 是否接收成功 - */ - private Boolean success; - /** - * API 接收结果的编码 - */ - private String errorCode; - /** - * API 接收结果的说明 - */ - private String errorMsg; - - /** - * 手机号 - */ - private String mobile; - /** - * 用户接收时间 - */ - private LocalDateTime receiveTime; - - /** - * 短信 API 发送返回的序号 - */ - private String serialNo; - /** - * 短信日志编号 - * - * 对应 SysSmsLogDO 的编号 - */ - private Long logId; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/dto/SmsSendRespDTO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/dto/SmsSendRespDTO.java deleted file mode 100644 index b47e08e..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/dto/SmsSendRespDTO.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.iailab.module.system.framework.sms.core.client.dto; - -import lombok.Data; - -/** - * 短信发送 Response DTO - * - * @author iailab - */ -@Data -public class SmsSendRespDTO { - - /** - * 是否成功 - */ - private Boolean success; - - /** - * API 请求编号 - */ - private String apiRequestId; - - // ==================== 成功时字段 ==================== - - /** - * 短信 API 发送返回的序号 - */ - private String serialNo; - - // ==================== 失败时字段 ==================== - - /** - * API 返回错误码 - * - * 由于第三方的错误码可能是字符串,所以使用 String 类型 - */ - private String apiCode; - /** - * API 返回提示 - */ - private String apiMsg; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/dto/SmsTemplateRespDTO.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/dto/SmsTemplateRespDTO.java deleted file mode 100644 index 6c23252..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/dto/SmsTemplateRespDTO.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.iailab.module.system.framework.sms.core.client.dto; - -import com.iailab.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import lombok.Data; - -/** - * 短信模板 Response DTO - * - * @author iailab - */ -@Data -public class SmsTemplateRespDTO { - - /** - * 模板编号 - */ - private String id; - /** - * 短信内容 - */ - private String content; - /** - * 审核状态 - * - * 枚举 {@link SmsTemplateAuditStatusEnum} - */ - private Integer auditStatus; - /** - * 审核未通过的理由 - */ - private String auditReason; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/AbstractSmsClient.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/AbstractSmsClient.java deleted file mode 100644 index d50e30b..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/AbstractSmsClient.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.iailab.module.system.framework.sms.core.client.impl; - -import com.iailab.module.system.framework.sms.core.client.SmsClient; -import com.iailab.module.system.framework.sms.core.property.SmsChannelProperties; -import lombok.extern.slf4j.Slf4j; - -/** - * 短信客户端的抽象类,提供模板方法,减少子类的冗余代码 - * - * @author zzf - * @since 2021/2/1 9:28 - */ -@Slf4j -public abstract class AbstractSmsClient implements SmsClient { - - /** - * 短信渠道配置 - */ - protected volatile SmsChannelProperties properties; - - public AbstractSmsClient(SmsChannelProperties properties) { - this.properties = properties; - } - - /** - * 初始化 - */ - public final void init() { - doInit(); - log.debug("[init][配置({}) 初始化完成]", properties); - } - - /** - * 自定义初始化 - */ - protected abstract void doInit(); - - public final void refresh(SmsChannelProperties properties) { - // 判断是否更新 - if (properties.equals(this.properties)) { - return; - } - log.info("[refresh][配置({})发生变化,重新初始化]", properties); - this.properties = properties; - // 初始化 - this.init(); - } - - @Override - public Long getId() { - return properties.getId(); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/AliyunSmsClient.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/AliyunSmsClient.java deleted file mode 100644 index a3f2ab2..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/AliyunSmsClient.java +++ /dev/null @@ -1,183 +0,0 @@ -package com.iailab.module.system.framework.sms.core.client.impl; - -import cn.hutool.core.lang.Assert; -import com.iailab.framework.common.core.KeyValue; -import com.iailab.framework.common.util.collection.MapUtils; -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import com.iailab.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import com.iailab.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import com.iailab.module.system.framework.sms.core.client.impl.AbstractSmsClient; -import com.iailab.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import com.iailab.module.system.framework.sms.core.property.SmsChannelProperties; -import com.aliyuncs.DefaultAcsClient; -import com.aliyuncs.IAcsClient; -import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest; -import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateResponse; -import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; -import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; -import com.aliyuncs.profile.DefaultProfile; -import com.aliyuncs.profile.IClientProfile; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.annotations.VisibleForTesting; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Objects; - -import static com.iailab.framework.common.util.collection.CollectionUtils.convertList; -import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -import static com.iailab.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; - -/** - * 阿里短信客户端的实现类 - * - * @author zzf - * @since 2021/1/25 14:17 - */ -@Slf4j -public class AliyunSmsClient extends AbstractSmsClient { - - /** - * 调用成功 code - */ - public static final String API_CODE_SUCCESS = "OK"; - - /** - * REGION, 使用杭州 - */ - private static final String ENDPOINT = "cn-hangzhou"; - - /** - * 阿里云客户端 - */ - private volatile IAcsClient client; - - public AliyunSmsClient(SmsChannelProperties properties) { - super(properties); - Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); - Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); - } - - @Override - protected void doInit() { - IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, properties.getApiKey(), properties.getApiSecret()); - client = new DefaultAcsClient(profile); - } - - @Override - public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId, - List<KeyValue<String, Object>> templateParams) throws Throwable { - // 构建请求 - SendSmsRequest request = new SendSmsRequest(); - request.setPhoneNumbers(mobile); - request.setSignName(properties.getSignature()); - request.setTemplateCode(apiTemplateId); - request.setTemplateParam(JsonUtils.toJsonString(MapUtils.convertMap(templateParams))); - request.setOutId(String.valueOf(sendLogId)); - // 执行请求 - SendSmsResponse response = client.getAcsResponse(request); - return new SmsSendRespDTO().setSuccess(Objects.equals(response.getCode(), API_CODE_SUCCESS)).setSerialNo(response.getBizId()) - .setApiRequestId(response.getRequestId()).setApiCode(response.getCode()).setApiMsg(response.getMessage()); - } - - @Override - public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) { - List<SmsReceiveStatus> statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class); - return convertList(statuses, status -> new SmsReceiveRespDTO().setSuccess(status.getSuccess()) - .setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg()) - .setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime()) - .setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId()))); - } - - @Override - public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { - // 构建请求 - QuerySmsTemplateRequest request = new QuerySmsTemplateRequest(); - request.setTemplateCode(apiTemplateId); - // 执行请求 - QuerySmsTemplateResponse response = client.getAcsResponse(request); - if (response.getTemplateStatus() == null) { - return null; - } - return new SmsTemplateRespDTO().setId(response.getTemplateCode()).setContent(response.getTemplateContent()) - .setAuditStatus(convertSmsTemplateAuditStatus(response.getTemplateStatus())).setAuditReason(response.getReason()); - } - - @VisibleForTesting - Integer convertSmsTemplateAuditStatus(Integer templateStatus) { - switch (templateStatus) { - case 0: return SmsTemplateAuditStatusEnum.CHECKING.getStatus(); - case 1: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); - case 2: return SmsTemplateAuditStatusEnum.FAIL.getStatus(); - default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus)); - } - } - - /** - * 短信接收状态 - * - * 参见 <a href="https://help.aliyun.com/document_detail/101867.html">文档</a> - * - * @author iailab - */ - @Data - public static class SmsReceiveStatus { - - /** - * 手机号 - */ - @JsonProperty("phone_number") - private String phoneNumber; - /** - * 发送时间 - */ - @JsonProperty("send_time") - @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) - private LocalDateTime sendTime; - /** - * 状态报告时间 - */ - @JsonProperty("report_time") - @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) - private LocalDateTime reportTime; - /** - * 是否接收成功 - */ - private Boolean success; - /** - * 状态报告说明 - */ - @JsonProperty("err_msg") - private String errMsg; - /** - * 状态报告编码 - */ - @JsonProperty("err_code") - private String errCode; - /** - * 发送序列号 - */ - @JsonProperty("biz_id") - private String bizId; - /** - * 用户序列号 - * - * 这里我们传递的是 SysSmsLogDO 的日志编号 - */ - @JsonProperty("out_id") - private String outId; - /** - * 短信长度,例如说 1、2、3 - * - * 140 字节算一条短信,短信长度超过 140 字节时会拆分成多条短信发送 - */ - @JsonProperty("sms_size") - private Integer smsSize; - - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/DebugDingTalkSmsClient.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/DebugDingTalkSmsClient.java deleted file mode 100644 index 33bbfd2..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/DebugDingTalkSmsClient.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.iailab.module.system.framework.sms.core.client.impl; - -import cn.hutool.core.codec.Base64; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.digest.DigestUtil; -import cn.hutool.crypto.digest.HmacAlgorithm; -import cn.hutool.http.HttpUtil; -import com.iailab.framework.common.core.KeyValue; -import com.iailab.framework.common.util.collection.MapUtils; -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import com.iailab.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import com.iailab.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import com.iailab.module.system.framework.sms.core.client.impl.AbstractSmsClient; -import com.iailab.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import com.iailab.module.system.framework.sms.core.property.SmsChannelProperties; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * 基于钉钉 WebHook 实现的调试的短信客户端实现类 - * - * 考虑到省钱,我们使用钉钉 WebHook 模拟发送短信,方便调试。 - * - * @author iailab - */ -public class DebugDingTalkSmsClient extends AbstractSmsClient { - - public DebugDingTalkSmsClient(SmsChannelProperties properties) { - super(properties); - Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); - Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); - } - - @Override - protected void doInit() { - } - - @Override - public SmsSendRespDTO sendSms(Long sendLogId, String mobile, - String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable { - // 构建请求 - String url = buildUrl("robot/send"); - Map<String, Object> params = new HashMap<>(); - params.put("msgtype", "text"); - String content = String.format("【模拟短信】\n手机号:%s\n短信日志编号:%d\n模板参数:%s", - mobile, sendLogId, MapUtils.convertMap(templateParams)); - params.put("text", MapUtil.builder().put("content", content).build()); - // 执行请求 - String responseText = HttpUtil.post(url, JsonUtils.toJsonString(params)); - // 解析结果 - Map<?, ?> responseObj = JsonUtils.parseObject(responseText, Map.class); - String errorCode = MapUtil.getStr(responseObj, "errcode"); - return new SmsSendRespDTO().setSuccess(Objects.equals(errorCode, "0")).setSerialNo(StrUtil.uuid()) - .setApiCode(errorCode).setApiMsg(MapUtil.getStr(responseObj, "errorMsg")); - } - - /** - * 构建请求地址 - * - * 参见 <a href="https://developers.dingtalk.com/document/app/custom-robot-access/title-nfv-794-g71">文档</a> - * - * @param path 请求路径 - * @return 请求地址 - */ - @SuppressWarnings("SameParameterValue") - private String buildUrl(String path) { - // 生成 timestamp - long timestamp = System.currentTimeMillis(); - // 生成 sign - String secret = properties.getApiSecret(); - String stringToSign = timestamp + "\n" + secret; - byte[] signData = DigestUtil.hmac(HmacAlgorithm.HmacSHA256, StrUtil.bytes(secret)).digest(stringToSign); - String sign = Base64.encode(signData); - // 构建最终 URL - return String.format("https://oapi.dingtalk.com/%s?access_token=%s×tamp=%d&sign=%s", - path, properties.getApiKey(), timestamp, sign); - } - - @Override - public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) { - throw new UnsupportedOperationException("模拟短信客户端,暂时无需解析回调"); - } - - @Override - public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) { - return new SmsTemplateRespDTO().setId(apiTemplateId).setContent("") - .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(""); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java deleted file mode 100644 index ab4bb20..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.iailab.module.system.framework.sms.core.client.impl; - -import com.iailab.module.system.framework.sms.core.client.SmsClient; -import com.iailab.module.system.framework.sms.core.client.SmsClientFactory; -import com.iailab.module.system.framework.sms.core.enums.SmsChannelEnum; -import com.iailab.module.system.framework.sms.core.property.SmsChannelProperties; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.Assert; -import org.springframework.validation.annotation.Validated; - -import java.util.Arrays; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * 短信客户端工厂接口 - * - * @author zzf - */ -@Validated -@Slf4j -public class SmsClientFactoryImpl implements SmsClientFactory { - - /** - * 短信客户端 Map - * key:渠道编号,使用 {@link SmsChannelProperties#getId()} - */ - private final ConcurrentMap<Long, AbstractSmsClient> channelIdClients = new ConcurrentHashMap<>(); - - /** - * 短信客户端 Map - * key:渠道编码,使用 {@link SmsChannelProperties#getCode()} ()} - * - * 注意,一些场景下,需要获得某个渠道类型的客户端,所以需要使用它。 - * 例如说,解析短信接收结果,是相对通用的,不需要使用某个渠道编号的 {@link #channelIdClients} - */ - private final ConcurrentMap<String, AbstractSmsClient> channelCodeClients = new ConcurrentHashMap<>(); - - public SmsClientFactoryImpl() { - // 初始化 channelCodeClients 集合 - Arrays.stream(SmsChannelEnum.values()).forEach(channel -> { - // 创建一个空的 SmsChannelProperties 对象 - SmsChannelProperties properties = new SmsChannelProperties().setCode(channel.getCode()) - .setApiKey("default default").setApiSecret("default"); - // 创建 Sms 客户端 - AbstractSmsClient smsClient = createSmsClient(properties); - channelCodeClients.put(channel.getCode(), smsClient); - }); - } - - @Override - public SmsClient getSmsClient(Long channelId) { - return channelIdClients.get(channelId); - } - - @Override - public SmsClient getSmsClient(String channelCode) { - return channelCodeClients.get(channelCode); - } - - @Override - public void createOrUpdateSmsClient(SmsChannelProperties properties) { - AbstractSmsClient client = channelIdClients.get(properties.getId()); - if (client == null) { - client = this.createSmsClient(properties); - client.init(); - channelIdClients.put(client.getId(), client); - } else { - client.refresh(properties); - } - } - - private AbstractSmsClient createSmsClient(SmsChannelProperties properties) { - SmsChannelEnum channelEnum = SmsChannelEnum.getByCode(properties.getCode()); - Assert.notNull(channelEnum, String.format("渠道类型(%s) 为空", channelEnum)); - // 创建客户端 - switch (channelEnum) { - case ALIYUN: return new AliyunSmsClient(properties); - case DEBUG_DING_TALK: return new DebugDingTalkSmsClient(properties); - case TENCENT: return new TencentSmsClient(properties); - } - // 创建失败,错误日志 + 抛出异常 - log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", properties); - throw new IllegalArgumentException(String.format("配置(%s) 找不到合适的客户端实现", properties)); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/TencentSmsClient.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/TencentSmsClient.java deleted file mode 100644 index 4dea4f7..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/client/impl/TencentSmsClient.java +++ /dev/null @@ -1,219 +0,0 @@ -package com.iailab.module.system.framework.sms.core.client.impl; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.core.KeyValue; -import com.iailab.framework.common.util.collection.ArrayUtils; -import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import com.iailab.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import com.iailab.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import com.iailab.module.system.framework.sms.core.client.impl.AbstractSmsClient; -import com.iailab.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import com.iailab.module.system.framework.sms.core.property.SmsChannelProperties; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.annotations.VisibleForTesting; -import com.tencentcloudapi.common.Credential; -import com.tencentcloudapi.sms.v20210111.SmsClient; -import com.tencentcloudapi.sms.v20210111.models.*; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Objects; - -import static com.iailab.framework.common.util.collection.CollectionUtils.convertList; -import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -import static com.iailab.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; - -/** - * 腾讯云短信功能实现 - * - * 参见 <a href="https://cloud.tencent.com/document/product/382/52077">文档</a> - * - * @author shiwp - */ -public class TencentSmsClient extends AbstractSmsClient { - - /** - * 调用成功 code - */ - public static final String API_CODE_SUCCESS = "Ok"; - - /** - * REGION,使用南京 - */ - private static final String ENDPOINT = "ap-nanjing"; - - /** - * 是否国际/港澳台短信: - * - * 0:表示国内短信。 - * 1:表示国际/港澳台短信。 - */ - private static final long INTERNATIONAL_CHINA = 0L; - - private SmsClient client; - - public TencentSmsClient(SmsChannelProperties properties) { - super(properties); - Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); - validateSdkAppId(properties); - } - - @Override - protected void doInit() { - // 实例化一个认证对象,入参需要传入腾讯云账户密钥对 secretId,secretKey - Credential credential = new Credential(getApiKey(), properties.getApiSecret()); - client = new SmsClient(credential, ENDPOINT); - } - - /** - * 参数校验腾讯云的 SDK AppId - * - * 原因是:腾讯云发放短信的时候,需要额外的参数 sdkAppId - * - * 解决方案:考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。 - * - * @param properties 配置 - */ - private static void validateSdkAppId(SmsChannelProperties properties) { - String combineKey = properties.getApiKey(); - Assert.notEmpty(combineKey, "apiKey 不能为空"); - String[] keys = combineKey.trim().split(" "); - Assert.isTrue(keys.length == 2, "腾讯云短信 apiKey 配置格式错误,请配置 为[secretId sdkAppId]"); - } - - private String getSdkAppId() { - return StrUtil.subAfter(properties.getApiKey(), " ", true); - } - - private String getApiKey() { - return StrUtil.subBefore(properties.getApiKey(), " ", true); - } - - @Override - public SmsSendRespDTO sendSms(Long sendLogId, String mobile, - String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable { - // 构建请求 - SendSmsRequest request = new SendSmsRequest(); - request.setSmsSdkAppId(getSdkAppId()); - request.setPhoneNumberSet(new String[]{mobile}); - request.setSignName(properties.getSignature()); - request.setTemplateId(apiTemplateId); - request.setTemplateParamSet(ArrayUtils.toArray(templateParams, e -> String.valueOf(e.getValue()))); - request.setSessionContext(JsonUtils.toJsonString(new SessionContext().setLogId(sendLogId))); - // 执行请求 - SendSmsResponse response = client.SendSms(request); - SendStatus status = response.getSendStatusSet()[0]; - return new SmsSendRespDTO().setSuccess(Objects.equals(status.getCode(), API_CODE_SUCCESS)).setSerialNo(status.getSerialNo()) - .setApiRequestId(response.getRequestId()).setApiCode(status.getCode()).setApiMsg(status.getMessage()); - } - - @Override - public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) { - List<SmsReceiveStatus> callback = JsonUtils.parseArray(text, SmsReceiveStatus.class); - return convertList(callback, status -> new SmsReceiveRespDTO() - .setSuccess(SmsReceiveStatus.SUCCESS_CODE.equalsIgnoreCase(status.getStatus())) - .setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription()) - .setMobile(status.getMobile()).setReceiveTime(status.getReceiveTime()) - .setSerialNo(status.getSerialNo()).setLogId(status.getSessionContext().getLogId())); - } - - @Override - public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { - // 构建请求 - DescribeSmsTemplateListRequest request = new DescribeSmsTemplateListRequest(); - request.setTemplateIdSet(new Long[]{Long.parseLong(apiTemplateId)}); - request.setInternational(INTERNATIONAL_CHINA); - // 执行请求 - DescribeSmsTemplateListResponse response = client.DescribeSmsTemplateList(request); - DescribeTemplateListStatus status = response.getDescribeTemplateStatusSet()[0]; - if (status == null || status.getStatusCode() == null) { - return null; - } - return new SmsTemplateRespDTO().setId(status.getTemplateId().toString()).setContent(status.getTemplateContent()) - .setAuditStatus(convertSmsTemplateAuditStatus(status.getStatusCode().intValue())).setAuditReason(status.getReviewReply()); - } - - @VisibleForTesting - Integer convertSmsTemplateAuditStatus(int templateStatus) { - switch (templateStatus) { - case 1: return SmsTemplateAuditStatusEnum.CHECKING.getStatus(); - case 0: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); - case -1: return SmsTemplateAuditStatusEnum.FAIL.getStatus(); - default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus)); - } - } - - @Data - private static class SmsReceiveStatus { - - /** - * 短信接受成功 code - */ - public static final String SUCCESS_CODE = "SUCCESS"; - - /** - * 用户实际接收到短信的时间 - */ - @JsonProperty("user_receive_time") - @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) - private LocalDateTime receiveTime; - - /** - * 国家(或地区)码 - */ - @JsonProperty("nationcode") - private String nationCode; - - /** - * 手机号码 - */ - private String mobile; - - /** - * 实际是否收到短信接收状态,SUCCESS(成功)、FAIL(失败) - */ - @JsonProperty("report_status") - private String status; - - /** - * 用户接收短信状态码错误信息 - */ - @JsonProperty("errmsg") - private String errCode; - - /** - * 用户接收短信状态描述 - */ - @JsonProperty("description") - private String description; - - /** - * 本次发送标识 ID(与发送接口返回的SerialNo对应) - */ - @JsonProperty("sid") - private String serialNo; - - /** - * 用户的 session 内容(与发送接口的请求参数 SessionContext 一致) - */ - @JsonProperty("ext") - private SessionContext sessionContext; - - } - - @VisibleForTesting - @Data - static class SessionContext { - - /** - * 发送短信记录id - */ - private Long logId; - - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/enums/SmsChannelEnum.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/enums/SmsChannelEnum.java deleted file mode 100644 index 878d3c1..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/enums/SmsChannelEnum.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.iailab.module.system.framework.sms.core.enums; - -import cn.hutool.core.util.ArrayUtil; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 短信渠道枚举 - * - * @author zzf - * @since 2021/1/25 10:56 - */ -@Getter -@AllArgsConstructor -public enum SmsChannelEnum { - - DEBUG_DING_TALK("DEBUG_DING_TALK", "调试(钉钉)"), - ALIYUN("ALIYUN", "阿里云"), - TENCENT("TENCENT", "腾讯云"), -// HUA_WEI("HUA_WEI", "华为云"), - ; - - /** - * 编码 - */ - private final String code; - /** - * 名字 - */ - private final String name; - - public static SmsChannelEnum getByCode(String code) { - return ArrayUtil.firstMatch(o -> o.getCode().equals(code), values()); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/enums/SmsTemplateAuditStatusEnum.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/enums/SmsTemplateAuditStatusEnum.java deleted file mode 100644 index c550ce1..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/enums/SmsTemplateAuditStatusEnum.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.iailab.module.system.framework.sms.core.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 短信模板的审核状态枚举 - * - * @author iailab - */ -@AllArgsConstructor -@Getter -public enum SmsTemplateAuditStatusEnum { - - CHECKING(1), - SUCCESS(2), - FAIL(3); - - private final Integer status; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/property/SmsChannelProperties.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/property/SmsChannelProperties.java deleted file mode 100644 index 410a78e..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/framework/sms/core/property/SmsChannelProperties.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.iailab.module.system.framework.sms.core.property; - -import com.iailab.module.system.framework.sms.core.enums.SmsChannelEnum; -import lombok.Data; -import org.springframework.validation.annotation.Validated; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -/** - * 短信渠道配置类 - * - * @author zzf - * @since 2021/1/25 17:01 - */ -@Data -@Validated -public class SmsChannelProperties { - - /** - * 渠道编号 - */ - @NotNull(message = "短信渠道 ID 不能为空") - private Long id; - /** - * 短信签名 - */ - @NotEmpty(message = "短信签名不能为空") - private String signature; - /** - * 渠道编码 - * - * 枚举 {@link SmsChannelEnum} - */ - @NotEmpty(message = "渠道编码不能为空") - private String code; - /** - * 短信 API 的账号 - */ - @NotEmpty(message = "短信 API 的账号不能为空") - private String apiKey; - /** - * 短信 API 的密钥 - */ - @NotEmpty(message = "短信 API 的密钥不能为空") - private String apiSecret; - /** - * 短信发送回调 URL - */ - private String callbackUrl; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/mq/consumer/sms/SmsSendConsumer.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/mq/consumer/sms/SmsSendConsumer.java deleted file mode 100644 index 0987c87..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/mq/consumer/sms/SmsSendConsumer.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.iailab.module.system.mq.consumer.sms; - -import com.iailab.module.system.mq.message.sms.SmsSendMessage; -import com.iailab.module.system.service.sms.SmsSendService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link SmsSendMessage} 的消费者 - * - * @author zzf - */ -@Component -@Slf4j -public class SmsSendConsumer { - - @Resource - private SmsSendService smsSendService; - - @EventListener - @Async // Spring Event 默认在 Producer 发送的线程,通过 @Async 实现异步 - public void onMessage(SmsSendMessage message) { - log.info("[onMessage][消息内容({})]", message); - smsSendService.doSendSms(message); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/auth/AdminAuthService.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/auth/AdminAuthService.java index 6699da4..dbf21eb 100644 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/auth/AdminAuthService.java +++ b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/auth/AdminAuthService.java @@ -48,29 +48,6 @@ void logout(String token, Integer logType); /** - * 短信验证码发送 - * - * @param reqVO 发送请求 - */ - void sendSmsCode(AuthSmsSendReqVO reqVO); - - /** - * 短信登录 - * - * @param reqVO 登录信息 - * @return 登录结果 - */ - AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO) ; - - /** - * 社交快捷登录,使用 code 授权码 - * - * @param reqVO 登录信息 - * @return 登录结果 - */ - AuthLoginRespVO socialLogin(@Valid AuthSocialLoginReqVO reqVO); - - /** * 刷新访问令牌 * * @param refreshToken 刷新令牌 diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/auth/AdminAuthServiceImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/auth/AdminAuthServiceImpl.java index 1fa800b..115ad3e 100644 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/auth/AdminAuthServiceImpl.java +++ b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/auth/AdminAuthServiceImpl.java @@ -7,9 +7,6 @@ import com.iailab.framework.common.util.servlet.ServletUtils; import com.iailab.framework.common.util.validation.ValidationUtils; import com.iailab.module.system.api.logger.dto.LoginLogCreateReqDTO; -import com.iailab.module.system.api.sms.SmsCodeApi; -import com.iailab.module.system.api.social.dto.SocialUserBindReqDTO; -import com.iailab.module.system.api.social.dto.SocialUserRespDTO; import com.iailab.module.system.controller.admin.auth.vo.*; import com.iailab.module.system.convert.auth.AuthConvert; import com.iailab.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; @@ -21,7 +18,6 @@ import com.iailab.module.system.service.logger.LoginLogService; import com.iailab.module.system.service.member.MemberService; import com.iailab.module.system.service.oauth2.OAuth2TokenService; -import com.iailab.module.system.service.social.SocialUserService; import com.iailab.module.system.service.user.AdminUserService; import com.google.common.annotations.VisibleForTesting; import com.xingyuv.captcha.model.common.ResponseModel; @@ -36,7 +32,6 @@ import java.util.Objects; import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.iailab.framework.common.util.servlet.ServletUtils.getClientIP; import static com.iailab.module.system.enums.ErrorCodeConstants.*; /** @@ -55,15 +50,11 @@ @Resource private OAuth2TokenService oauth2TokenService; @Resource - private SocialUserService socialUserService; - @Resource private MemberService memberService; @Resource private Validator validator; @Resource private CaptchaService captchaService; - @Resource - private SmsCodeApi smsCodeApi; /** * 验证码的开关,默认为 true @@ -106,38 +97,8 @@ // 使用账号密码,进行登录 AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword()); - // 如果 socialType 非空,说明需要绑定社交用户 - if (reqVO.getSocialType() != null) { - socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), - reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())); - } // 创建 Token 令牌,记录登录日志 return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); - } - - @Override - public void sendSmsCode(AuthSmsSendReqVO reqVO) { - // 登录场景,验证是否存在 - if (userService.getUserByMobile(reqVO.getMobile()) == null) { - throw exception(AUTH_MOBILE_NOT_EXISTS); - } - // 发送验证码 - smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP())); - } - - @Override - public AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO) { - // 校验验证码 - smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), getClientIP())).getCheckedData(); - - // 获得用户信息 - AdminUserDO user = userService.getUserByMobile(reqVO.getMobile()); - if (user == null) { - throw exception(USER_NOT_EXISTS); - } - - // 创建 Token 令牌,记录登录日志 - return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE); } private void createLoginLog(Long userId, String username, @@ -157,25 +118,6 @@ if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { userService.updateUserLogin(userId, ServletUtils.getClientIP()); } - } - - @Override - public AuthLoginRespVO socialLogin(AuthSocialLoginReqVO reqVO) { - // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 - SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(UserTypeEnum.ADMIN.getValue(), reqVO.getType(), - reqVO.getCode(), reqVO.getState()); - if (socialUser == null || socialUser.getUserId() == null) { - throw exception(AUTH_THIRD_LOGIN_NOT_BIND); - } - - // 获得用户 - AdminUserDO user = userService.getUser(socialUser.getUserId()); - if (user == null) { - throw exception(USER_NOT_EXISTS); - } - - // 创建 Token 令牌,记录登录日志 - return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL); } @VisibleForTesting diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsChannelService.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsChannelService.java deleted file mode 100644 index 047e0ac..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsChannelService.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.iailab.module.system.service.sms; - -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.module.system.framework.sms.core.client.SmsClient; -import com.iailab.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; -import com.iailab.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO; -import com.iailab.module.system.dal.dataobject.sms.SmsChannelDO; - -import javax.validation.Valid; -import java.util.List; - -/** - * 短信渠道 Service 接口 - * - * @author zzf - * @since 2021/1/25 9:24 - */ -public interface SmsChannelService { - - /** - * 创建短信渠道 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createSmsChannel(@Valid SmsChannelSaveReqVO createReqVO); - - /** - * 更新短信渠道 - * - * @param updateReqVO 更新信息 - */ - void updateSmsChannel(@Valid SmsChannelSaveReqVO updateReqVO); - - /** - * 删除短信渠道 - * - * @param id 编号 - */ - void deleteSmsChannel(Long id); - - /** - * 获得短信渠道 - * - * @param id 编号 - * @return 短信渠道 - */ - SmsChannelDO getSmsChannel(Long id); - - /** - * 获得所有短信渠道列表 - * - * @return 短信渠道列表 - */ - List<SmsChannelDO> getSmsChannelList(); - - /** - * 获得短信渠道分页 - * - * @param pageReqVO 分页查询 - * @return 短信渠道分页 - */ - PageResult<SmsChannelDO> getSmsChannelPage(SmsChannelPageReqVO pageReqVO); - - /** - * 获得短信客户端 - * - * @param id 编号 - * @return 短信客户端 - */ - SmsClient getSmsClient(Long id); - - /** - * 获得短信客户端 - * - * @param code 编码 - * @return 短信客户端 - */ - SmsClient getSmsClient(String code); - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsChannelServiceImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsChannelServiceImpl.java deleted file mode 100644 index 1e9db0a..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsChannelServiceImpl.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.iailab.module.system.service.sms; - -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.common.util.object.BeanUtils; -import com.iailab.module.system.framework.sms.core.client.SmsClient; -import com.iailab.module.system.framework.sms.core.client.SmsClientFactory; -import com.iailab.module.system.framework.sms.core.property.SmsChannelProperties; -import com.iailab.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; -import com.iailab.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO; -import com.iailab.module.system.dal.dataobject.sms.SmsChannelDO; -import com.iailab.module.system.dal.mysql.sms.SmsChannelMapper; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.time.Duration; -import java.util.List; - -import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.iailab.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache; -import static com.iailab.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN; -import static com.iailab.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS; - -/** - * 短信渠道 Service 实现类 - * - * @author zzf - */ -@Service -@Slf4j -public class SmsChannelServiceImpl implements SmsChannelService { - - /** - * {@link SmsClient} 缓存,通过它异步刷新 smsClientFactory - */ - @Getter - private final LoadingCache<Long, SmsClient> idClientCache = buildAsyncReloadingCache(Duration.ofSeconds(10L), - new CacheLoader<Long, SmsClient>() { - - @Override - public SmsClient load(Long id) { - // 查询,然后尝试刷新 - SmsChannelDO channel = smsChannelMapper.selectById(id); - if (channel != null) { - SmsChannelProperties properties = BeanUtils.toBean(channel, SmsChannelProperties.class); - smsClientFactory.createOrUpdateSmsClient(properties); - } - return smsClientFactory.getSmsClient(id); - } - - }); - - /** - * {@link SmsClient} 缓存,通过它异步刷新 smsClientFactory - */ - @Getter - private final LoadingCache<String, SmsClient> codeClientCache = buildAsyncReloadingCache(Duration.ofSeconds(60L), - new CacheLoader<String, SmsClient>() { - - @Override - public SmsClient load(String code) { - // 查询,然后尝试刷新 - SmsChannelDO channel = smsChannelMapper.selectByCode(code); - if (channel != null) { - SmsChannelProperties properties = BeanUtils.toBean(channel, SmsChannelProperties.class); - smsClientFactory.createOrUpdateSmsClient(properties); - } - return smsClientFactory.getSmsClient(code); - } - - }); - - @Resource - private SmsClientFactory smsClientFactory; - - @Resource - private SmsChannelMapper smsChannelMapper; - - @Resource - private SmsTemplateService smsTemplateService; - - @Override - public Long createSmsChannel(SmsChannelSaveReqVO createReqVO) { - SmsChannelDO channel = BeanUtils.toBean(createReqVO, SmsChannelDO.class); - smsChannelMapper.insert(channel); - return channel.getId(); - } - - @Override - public void updateSmsChannel(SmsChannelSaveReqVO updateReqVO) { - // 校验存在 - SmsChannelDO channel = validateSmsChannelExists(updateReqVO.getId()); - // 更新 - SmsChannelDO updateObj = BeanUtils.toBean(updateReqVO, SmsChannelDO.class); - smsChannelMapper.updateById(updateObj); - - // 清空缓存 - clearCache(updateReqVO.getId(), channel.getCode()); - } - - @Override - public void deleteSmsChannel(Long id) { - // 校验存在 - SmsChannelDO channel = validateSmsChannelExists(id); - // 校验是否有在使用该账号的模版 - if (smsTemplateService.getSmsTemplateCountByChannelId(id) > 0) { - throw exception(SMS_CHANNEL_HAS_CHILDREN); - } - // 删除 - smsChannelMapper.deleteById(id); - - // 清空缓存 - clearCache(id, channel.getCode()); - } - - /** - * 清空指定渠道编号的缓存 - * - * @param id 渠道编号 - * @param code 渠道编码 - */ - private void clearCache(Long id, String code) { - idClientCache.invalidate(id); - if (StrUtil.isNotEmpty(code)) { - codeClientCache.invalidate(code); - } - } - - private SmsChannelDO validateSmsChannelExists(Long id) { - SmsChannelDO channel = smsChannelMapper.selectById(id); - if (channel == null) { - throw exception(SMS_CHANNEL_NOT_EXISTS); - } - return channel; - } - - @Override - public SmsChannelDO getSmsChannel(Long id) { - return smsChannelMapper.selectById(id); - } - - @Override - public List<SmsChannelDO> getSmsChannelList() { - return smsChannelMapper.selectList(); - } - - @Override - public PageResult<SmsChannelDO> getSmsChannelPage(SmsChannelPageReqVO pageReqVO) { - return smsChannelMapper.selectPage(pageReqVO); - } - - @Override - public SmsClient getSmsClient(Long id) { - return idClientCache.getUnchecked(id); - } - - @Override - public SmsClient getSmsClient(String code) { - return codeClientCache.getUnchecked(code); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsCodeService.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsCodeService.java deleted file mode 100644 index ce5ae1e..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsCodeService.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.iailab.module.system.service.sms; - -import com.iailab.framework.common.exception.ServiceException; -import com.iailab.module.system.api.sms.dto.code.SmsCodeSendReqDTO; -import com.iailab.module.system.api.sms.dto.code.SmsCodeUseReqDTO; -import com.iailab.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; - -import javax.validation.Valid; - -/** - * 短信验证码 Service 接口 - * - * @author iailab - */ -public interface SmsCodeService { - - /** - * 创建短信验证码,并进行发送 - * - * @param reqDTO 发送请求 - */ - void sendSmsCode(@Valid SmsCodeSendReqDTO reqDTO); - - /** - * 验证短信验证码,并进行使用 - * 如果正确,则将验证码标记成已使用 - * 如果错误,则抛出 {@link ServiceException} 异常 - * - * @param reqDTO 使用请求 - */ - void useSmsCode(@Valid SmsCodeUseReqDTO reqDTO); - - /** - * 检查验证码是否有效 - * - * @param reqDTO 校验请求 - */ - void validateSmsCode(@Valid SmsCodeValidateReqDTO reqDTO); - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsCodeServiceImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsCodeServiceImpl.java deleted file mode 100644 index d7dd35e..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsCodeServiceImpl.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.iailab.module.system.service.sms; - -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.map.MapUtil; -import com.iailab.module.system.api.sms.dto.code.SmsCodeSendReqDTO; -import com.iailab.module.system.api.sms.dto.code.SmsCodeUseReqDTO; -import com.iailab.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; -import com.iailab.module.system.dal.dataobject.sms.SmsCodeDO; -import com.iailab.module.system.dal.mysql.sms.SmsCodeMapper; -import com.iailab.module.system.enums.sms.SmsSceneEnum; -import com.iailab.module.system.framework.sms.config.SmsCodeProperties; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.randomInt; -import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.iailab.framework.common.util.date.DateUtils.isToday; -import static com.iailab.module.system.enums.ErrorCodeConstants.*; - -/** - * 短信验证码 Service 实现类 - * - * @author iailab - */ -@Service -@Validated -public class SmsCodeServiceImpl implements SmsCodeService { - - @Resource - private SmsCodeProperties smsCodeProperties; - - @Resource - private SmsCodeMapper smsCodeMapper; - - @Resource - private SmsSendService smsSendService; - - @Override - public void sendSmsCode(SmsCodeSendReqDTO reqDTO) { - SmsSceneEnum sceneEnum = SmsSceneEnum.getCodeByScene(reqDTO.getScene()); - Assert.notNull(sceneEnum, "验证码场景({}) 查找不到配置", reqDTO.getScene()); - // 创建验证码 - String code = createSmsCode(reqDTO.getMobile(), reqDTO.getScene(), reqDTO.getCreateIp()); - // 发送验证码 - smsSendService.sendSingleSms(reqDTO.getMobile(), null, null, - sceneEnum.getTemplateCode(), MapUtil.of("code", code)); - } - - private String createSmsCode(String mobile, Integer scene, String ip) { - // 校验是否可以发送验证码,不用筛选场景 - SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null, null); - if (lastSmsCode != null) { - if (LocalDateTimeUtil.between(lastSmsCode.getCreateTime(), LocalDateTime.now()).toMillis() - < smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁 - throw exception(SMS_CODE_SEND_TOO_FAST); - } - if (isToday(lastSmsCode.getCreateTime()) && // 必须是今天,才能计算超过当天的上限 - lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。 - throw exception(SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY); - } - // TODO iailab:提升,每个 IP 每天可发送数量 - // TODO iailab:提升,每个 IP 每小时可发送数量 - } - - // 创建验证码记录 - String code = String.format("%0" + smsCodeProperties.getEndCode().toString().length() + "d", - randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1)); - SmsCodeDO newSmsCode = SmsCodeDO.builder().mobile(mobile).code(code).scene(scene) - .todayIndex(lastSmsCode != null && isToday(lastSmsCode.getCreateTime()) ? lastSmsCode.getTodayIndex() + 1 : 1) - .createIp(ip).used(false).build(); - smsCodeMapper.insert(newSmsCode); - return code; - } - - @Override - public void useSmsCode(SmsCodeUseReqDTO reqDTO) { - // 检测验证码是否有效 - SmsCodeDO lastSmsCode = validateSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene()); - // 使用验证码 - smsCodeMapper.updateById(SmsCodeDO.builder().id(lastSmsCode.getId()) - .used(true).usedTime(LocalDateTime.now()).usedIp(reqDTO.getUsedIp()).build()); - } - - @Override - public void validateSmsCode(SmsCodeValidateReqDTO reqDTO) { - validateSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene()); - } - - private SmsCodeDO validateSmsCode0(String mobile, String code, Integer scene) { - // 校验验证码 - SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, code, scene); - // 若验证码不存在,抛出异常 - if (lastSmsCode == null) { - throw exception(SMS_CODE_NOT_FOUND); - } - // 超过时间 - if (LocalDateTimeUtil.between(lastSmsCode.getCreateTime(), LocalDateTime.now()).toMillis() - >= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期 - throw exception(SMS_CODE_EXPIRED); - } - // 判断验证码是否已被使用 - if (Boolean.TRUE.equals(lastSmsCode.getUsed())) { - throw exception(SMS_CODE_USED); - } - return lastSmsCode; - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsLogService.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsLogService.java deleted file mode 100644 index 55f81f8..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsLogService.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.iailab.module.system.service.sms; - -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; -import com.iailab.module.system.dal.dataobject.sms.SmsLogDO; -import com.iailab.module.system.dal.dataobject.sms.SmsTemplateDO; - -import java.time.LocalDateTime; -import java.util.Map; - -/** - * 短信日志 Service 接口 - * - * @author zzf - * @date 13:48 2021/3/2 - */ -public interface SmsLogService { - - /** - * 创建短信日志 - * - * @param mobile 手机号 - * @param userId 用户编号 - * @param userType 用户类型 - * @param isSend 是否发送 - * @param template 短信模板 - * @param templateContent 短信内容 - * @param templateParams 短信参数 - * @return 发送日志编号 - */ - Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend, - SmsTemplateDO template, String templateContent, Map<String, Object> templateParams); - - /** - * 更新日志的发送结果 - * - * @param id 日志编号 - * @param success 发送是否成功 - * @param apiSendCode 短信 API 发送结果的编码 - * @param apiSendMsg 短信 API 发送失败的提示 - * @param apiRequestId 短信 API 发送返回的唯一请求 ID - * @param apiSerialNo 短信 API 发送返回的序号 - */ - void updateSmsSendResult(Long id, Boolean success, - String apiSendCode, String apiSendMsg, - String apiRequestId, String apiSerialNo); - - /** - * 更新日志的接收结果 - * - * @param id 日志编号 - * @param success 是否接收成功 - * @param receiveTime 用户接收时间 - * @param apiReceiveCode API 接收结果的编码 - * @param apiReceiveMsg API 接收结果的说明 - */ - void updateSmsReceiveResult(Long id, Boolean success, - LocalDateTime receiveTime, String apiReceiveCode, String apiReceiveMsg); - - /** - * 获得短信日志分页 - * - * @param pageReqVO 分页查询 - * @return 短信日志分页 - */ - PageResult<SmsLogDO> getSmsLogPage(SmsLogPageReqVO pageReqVO); - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsLogServiceImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsLogServiceImpl.java deleted file mode 100644 index 268a4d0..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsLogServiceImpl.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.iailab.module.system.service.sms; - -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; -import com.iailab.module.system.dal.dataobject.sms.SmsLogDO; -import com.iailab.module.system.dal.dataobject.sms.SmsTemplateDO; -import com.iailab.module.system.dal.mysql.sms.SmsLogMapper; -import com.iailab.module.system.enums.sms.SmsReceiveStatusEnum; -import com.iailab.module.system.enums.sms.SmsSendStatusEnum; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.Map; -import java.util.Objects; - -/** - * 短信日志 Service 实现类 - * - * @author zzf - */ -@Slf4j -@Service -public class SmsLogServiceImpl implements SmsLogService { - - @Resource - private SmsLogMapper smsLogMapper; - - @Override - public Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend, - SmsTemplateDO template, String templateContent, Map<String, Object> templateParams) { - SmsLogDO.SmsLogDOBuilder logBuilder = SmsLogDO.builder(); - // 根据是否要发送,设置状态 - logBuilder.sendStatus(Objects.equals(isSend, true) ? SmsSendStatusEnum.INIT.getStatus() - : SmsSendStatusEnum.IGNORE.getStatus()); - // 设置手机相关字段 - logBuilder.mobile(mobile).userId(userId).userType(userType); - // 设置模板相关字段 - logBuilder.templateId(template.getId()).templateCode(template.getCode()).templateType(template.getType()); - logBuilder.templateContent(templateContent).templateParams(templateParams) - .apiTemplateId(template.getApiTemplateId()); - // 设置渠道相关字段 - logBuilder.channelId(template.getChannelId()).channelCode(template.getChannelCode()); - // 设置接收相关字段 - logBuilder.receiveStatus(SmsReceiveStatusEnum.INIT.getStatus()); - - // 插入数据库 - SmsLogDO logDO = logBuilder.build(); - smsLogMapper.insert(logDO); - return logDO.getId(); - } - - @Override - public void updateSmsSendResult(Long id, Boolean success, - String apiSendCode, String apiSendMsg, - String apiRequestId, String apiSerialNo) { - SmsSendStatusEnum sendStatus = success ? SmsSendStatusEnum.SUCCESS : SmsSendStatusEnum.FAILURE; - smsLogMapper.updateById(SmsLogDO.builder().id(id) - .sendStatus(sendStatus.getStatus()).sendTime(LocalDateTime.now()) - .apiSendCode(apiSendCode).apiSendMsg(apiSendMsg) - .apiRequestId(apiRequestId).apiSerialNo(apiSerialNo).build()); - } - - @Override - public void updateSmsReceiveResult(Long id, Boolean success, LocalDateTime receiveTime, - String apiReceiveCode, String apiReceiveMsg) { - SmsReceiveStatusEnum receiveStatus = Objects.equals(success, true) ? - SmsReceiveStatusEnum.SUCCESS : SmsReceiveStatusEnum.FAILURE; - smsLogMapper.updateById(SmsLogDO.builder().id(id).receiveStatus(receiveStatus.getStatus()) - .receiveTime(receiveTime).apiReceiveCode(apiReceiveCode).apiReceiveMsg(apiReceiveMsg).build()); - } - - @Override - public PageResult<SmsLogDO> getSmsLogPage(SmsLogPageReqVO pageReqVO) { - return smsLogMapper.selectPage(pageReqVO); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsSendService.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsSendService.java deleted file mode 100644 index db29516..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsSendService.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.iailab.module.system.service.sms; - -import com.iailab.module.system.mq.message.sms.SmsSendMessage; - -import java.util.List; -import java.util.Map; - -/** - * 短信发送 Service 接口 - * - * @author iailab - */ -public interface SmsSendService { - - /** - * 发送单条短信给管理后台的用户 - * - * 在 mobile 为空时,使用 userId 加载对应管理员的手机号 - * - * @param mobile 手机号 - * @param userId 用户编号 - * @param templateCode 短信模板编号 - * @param templateParams 短信模板参数 - * @return 发送日志编号 - */ - Long sendSingleSmsToAdmin(String mobile, Long userId, - String templateCode, Map<String, Object> templateParams); - - /** - * 发送单条短信给用户 APP 的用户 - * - * 在 mobile 为空时,使用 userId 加载对应会员的手机号 - * - * @param mobile 手机号 - * @param userId 用户编号 - * @param templateCode 短信模板编号 - * @param templateParams 短信模板参数 - * @return 发送日志编号 - */ - Long sendSingleSmsToMember(String mobile, Long userId, - String templateCode, Map<String, Object> templateParams); - - /** - * 发送单条短信给用户 - * - * @param mobile 手机号 - * @param userId 用户编号 - * @param userType 用户类型 - * @param templateCode 短信模板编号 - * @param templateParams 短信模板参数 - * @return 发送日志编号 - */ - Long sendSingleSms(String mobile, Long userId, Integer userType, - String templateCode, Map<String, Object> templateParams); - - default void sendBatchSms(List<String> mobiles, List<Long> userIds, Integer userType, - String templateCode, Map<String, Object> templateParams) { - throw new UnsupportedOperationException("暂时不支持该操作,感兴趣可以实现该功能哟!"); - } - - /** - * 执行真正的短信发送 - * 注意,该方法仅仅提供给 MQ Consumer 使用 - * - * @param message 短信 - */ - void doSendSms(SmsSendMessage message); - - /** - * 接收短信的接收结果 - * - * @param channelCode 渠道编码 - * @param text 结果内容 - * @throws Throwable 处理失败时,抛出异常 - */ - void receiveSmsStatus(String channelCode, String text) throws Throwable; - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsSendServiceImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsSendServiceImpl.java deleted file mode 100644 index aeb0f14..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsSendServiceImpl.java +++ /dev/null @@ -1,191 +0,0 @@ -package com.iailab.module.system.service.sms; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.exceptions.ExceptionUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.core.KeyValue; -import com.iailab.framework.common.enums.CommonStatusEnum; -import com.iailab.framework.common.enums.UserTypeEnum; -import com.iailab.framework.datapermission.core.annotation.DataPermission; -import com.iailab.module.system.framework.sms.core.client.SmsClient; -import com.iailab.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import com.iailab.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import com.iailab.module.system.dal.dataobject.sms.SmsChannelDO; -import com.iailab.module.system.dal.dataobject.sms.SmsTemplateDO; -import com.iailab.module.system.dal.dataobject.user.AdminUserDO; -import com.iailab.module.system.mq.message.sms.SmsSendMessage; -import com.iailab.module.system.mq.producer.sms.SmsProducer; -import com.iailab.module.system.service.member.MemberService; -import com.iailab.module.system.service.user.AdminUserService; -import com.google.common.annotations.VisibleForTesting; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.iailab.module.system.enums.ErrorCodeConstants.*; - -/** - * 短信发送 Service 发送的实现 - * - * @author iailab - */ -@Service -@Slf4j -public class SmsSendServiceImpl implements SmsSendService { - - @Resource - private AdminUserService adminUserService; - @Resource - private MemberService memberService; - @Resource - private SmsChannelService smsChannelService; - @Resource - private SmsTemplateService smsTemplateService; - @Resource - private SmsLogService smsLogService; - - @Resource - private SmsProducer smsProducer; - - @Override - @DataPermission(enable = false) // 发送短信时,无需考虑数据权限 - public Long sendSingleSmsToAdmin(String mobile, Long userId, String templateCode, Map<String, Object> templateParams) { - // 如果 mobile 为空,则加载用户编号对应的手机号 - if (StrUtil.isEmpty(mobile)) { - AdminUserDO user = adminUserService.getUser(userId); - if (user != null) { - mobile = user.getMobile(); - } - } - // 执行发送 - return sendSingleSms(mobile, userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams); - } - - @Override - public Long sendSingleSmsToMember(String mobile, Long userId, String templateCode, Map<String, Object> templateParams) { - // 如果 mobile 为空,则加载用户编号对应的手机号 - if (StrUtil.isEmpty(mobile)) { - mobile = memberService.getMemberUserMobile(userId); - } - // 执行发送 - return sendSingleSms(mobile, userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams); - } - - @Override - public Long sendSingleSms(String mobile, Long userId, Integer userType, - String templateCode, Map<String, Object> templateParams) { - // 校验短信模板是否合法 - SmsTemplateDO template = validateSmsTemplate(templateCode); - // 校验短信渠道是否合法 - SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId()); - - // 校验手机号码是否存在 - mobile = validateMobile(mobile); - // 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志 - List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams); - - // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志 - Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus()) - && CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus()); - String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams); - Long sendLogId = smsLogService.createSmsLog(mobile, userId, userType, isSend, template, content, templateParams); - - // 发送 MQ 消息,异步执行发送短信 - if (isSend) { - smsProducer.sendSmsSendMessage(sendLogId, mobile, template.getChannelId(), - template.getApiTemplateId(), newTemplateParams); - } - return sendLogId; - } - - @VisibleForTesting - SmsChannelDO validateSmsChannel(Long channelId) { - // 获得短信模板。考虑到效率,从缓存中获取 - SmsChannelDO channelDO = smsChannelService.getSmsChannel(channelId); - // 短信模板不存在 - if (channelDO == null) { - throw exception(SMS_CHANNEL_NOT_EXISTS); - } - return channelDO; - } - - @VisibleForTesting - SmsTemplateDO validateSmsTemplate(String templateCode) { - // 获得短信模板。考虑到效率,从缓存中获取 - SmsTemplateDO template = smsTemplateService.getSmsTemplateByCodeFromCache(templateCode); - // 短信模板不存在 - if (template == null) { - throw exception(SMS_SEND_TEMPLATE_NOT_EXISTS); - } - return template; - } - - /** - * 将参数模板,处理成有序的 KeyValue 数组 - * <p> - * 原因是,部分短信平台并不是使用 key 作为参数,而是数组下标,例如说 <a href="https://cloud.tencent.com/document/product/382/39023">腾讯云</a> - * - * @param template 短信模板 - * @param templateParams 原始参数 - * @return 处理后的参数 - */ - @VisibleForTesting - List<KeyValue<String, Object>> buildTemplateParams(SmsTemplateDO template, Map<String, Object> templateParams) { - return template.getParams().stream().map(key -> { - Object value = templateParams.get(key); - if (value == null) { - throw exception(SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, key); - } - return new KeyValue<>(key, value); - }).collect(Collectors.toList()); - } - - @VisibleForTesting - public String validateMobile(String mobile) { - if (StrUtil.isEmpty(mobile)) { - throw exception(SMS_SEND_MOBILE_NOT_EXISTS); - } - return mobile; - } - - @Override - public void doSendSms(SmsSendMessage message) { - // 获得渠道对应的 SmsClient 客户端 - SmsClient smsClient = smsChannelService.getSmsClient(message.getChannelId()); - Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId()); - // 发送短信 - try { - SmsSendRespDTO sendResponse = smsClient.sendSms(message.getLogId(), message.getMobile(), - message.getApiTemplateId(), message.getTemplateParams()); - smsLogService.updateSmsSendResult(message.getLogId(), sendResponse.getSuccess(), - sendResponse.getApiCode(), sendResponse.getApiMsg(), - sendResponse.getApiRequestId(), sendResponse.getSerialNo()); - } catch (Throwable ex) { - log.error("[doSendSms][发送短信异常,日志编号({})]", message.getLogId(), ex); - smsLogService.updateSmsSendResult(message.getLogId(), false, - "EXCEPTION", ExceptionUtil.getRootCauseMessage(ex), null, null); - } - } - - @Override - public void receiveSmsStatus(String channelCode, String text) throws Throwable { - // 获得渠道对应的 SmsClient 客户端 - SmsClient smsClient = smsChannelService.getSmsClient(channelCode); - Assert.notNull(smsClient, "短信客户端({}) 不存在", channelCode); - // 解析内容 - List<SmsReceiveRespDTO> receiveResults = smsClient.parseSmsReceiveStatus(text); - if (CollUtil.isEmpty(receiveResults)) { - return; - } - // 更新短信日志的接收结果. 因为量一般不大,所以先使用 for 循环更新 - receiveResults.forEach(result -> smsLogService.updateSmsReceiveResult(result.getLogId(), - result.getSuccess(), result.getReceiveTime(), result.getErrorCode(), result.getErrorMsg())); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsTemplateService.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsTemplateService.java deleted file mode 100644 index 9179dc1..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsTemplateService.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.iailab.module.system.service.sms; - -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; -import com.iailab.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO; -import com.iailab.module.system.dal.dataobject.sms.SmsTemplateDO; - -import javax.validation.Valid; -import java.util.Map; - -/** - * 短信模板 Service 接口 - * - * @author zzf - * @since 2021/1/25 9:24 - */ -public interface SmsTemplateService { - - /** - * 创建短信模板 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createSmsTemplate(@Valid SmsTemplateSaveReqVO createReqVO); - - /** - * 更新短信模板 - * - * @param updateReqVO 更新信息 - */ - void updateSmsTemplate(@Valid SmsTemplateSaveReqVO updateReqVO); - - /** - * 删除短信模板 - * - * @param id 编号 - */ - void deleteSmsTemplate(Long id); - - /** - * 获得短信模板 - * - * @param id 编号 - * @return 短信模板 - */ - SmsTemplateDO getSmsTemplate(Long id); - - /** - * 获得短信模板,从缓存中 - * - * @param code 模板编码 - * @return 短信模板 - */ - SmsTemplateDO getSmsTemplateByCodeFromCache(String code); - - /** - * 获得短信模板分页 - * - * @param pageReqVO 分页查询 - * @return 短信模板分页 - */ - PageResult<SmsTemplateDO> getSmsTemplatePage(SmsTemplatePageReqVO pageReqVO); - - /** - * 获得指定短信渠道下的短信模板数量 - * - * @param channelId 短信渠道编号 - * @return 数量 - */ - Long getSmsTemplateCountByChannelId(Long channelId); - - /** - * 格式化短信内容 - * - * @param content 短信模板的内容 - * @param params 内容的参数 - * @return 格式化后的内容 - */ - String formatSmsTemplateContent(String content, Map<String, Object> params); - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsTemplateServiceImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsTemplateServiceImpl.java deleted file mode 100644 index 8297cee..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/sms/SmsTemplateServiceImpl.java +++ /dev/null @@ -1,199 +0,0 @@ -package com.iailab.module.system.service.sms; - -import cn.hutool.core.exceptions.ExceptionUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ReUtil; -import cn.hutool.core.util.StrUtil; -import com.iailab.framework.common.enums.CommonStatusEnum; -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.common.util.object.BeanUtils; -import com.iailab.module.system.framework.sms.core.client.SmsClient; -import com.iailab.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import com.iailab.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import com.iailab.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; -import com.iailab.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO; -import com.iailab.module.system.dal.dataobject.sms.SmsChannelDO; -import com.iailab.module.system.dal.dataobject.sms.SmsTemplateDO; -import com.iailab.module.system.dal.mysql.sms.SmsTemplateMapper; -import com.iailab.module.system.dal.redis.RedisKeyConstants; -import com.google.common.annotations.VisibleForTesting; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.regex.Pattern; - -import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.iailab.module.system.enums.ErrorCodeConstants.*; - -/** - * 短信模板 Service 实现类 - * - * @author zzf - * @since 2021/1/25 9:25 - */ -@Service -@Slf4j -public class SmsTemplateServiceImpl implements SmsTemplateService { - - /** - * 正则表达式,匹配 {} 中的变量 - */ - private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{(.*?)}"); - - @Resource - private SmsTemplateMapper smsTemplateMapper; - - @Resource - private SmsChannelService smsChannelService; - - @Override - public Long createSmsTemplate(SmsTemplateSaveReqVO createReqVO) { - // 校验短信渠道 - SmsChannelDO channelDO = validateSmsChannel(createReqVO.getChannelId()); - // 校验短信编码是否重复 - validateSmsTemplateCodeDuplicate(null, createReqVO.getCode()); - // 校验短信模板 - validateApiTemplate(createReqVO.getChannelId(), createReqVO.getApiTemplateId()); - - // 插入 - SmsTemplateDO template = BeanUtils.toBean(createReqVO, SmsTemplateDO.class); - template.setParams(parseTemplateContentParams(template.getContent())); - template.setChannelCode(channelDO.getCode()); - smsTemplateMapper.insert(template); - // 返回 - return template.getId(); - } - - @Override - @CacheEvict(cacheNames = RedisKeyConstants.SMS_TEMPLATE, - allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理 - public void updateSmsTemplate(SmsTemplateSaveReqVO updateReqVO) { - // 校验存在 - validateSmsTemplateExists(updateReqVO.getId()); - // 校验短信渠道 - SmsChannelDO channelDO = validateSmsChannel(updateReqVO.getChannelId()); - // 校验短信编码是否重复 - validateSmsTemplateCodeDuplicate(updateReqVO.getId(), updateReqVO.getCode()); - // 校验短信模板 - validateApiTemplate(updateReqVO.getChannelId(), updateReqVO.getApiTemplateId()); - - // 更新 - SmsTemplateDO updateObj = BeanUtils.toBean(updateReqVO, SmsTemplateDO.class); - updateObj.setParams(parseTemplateContentParams(updateObj.getContent())); - updateObj.setChannelCode(channelDO.getCode()); - smsTemplateMapper.updateById(updateObj); - } - - @Override - @CacheEvict(cacheNames = RedisKeyConstants.SMS_TEMPLATE, - allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理 - public void deleteSmsTemplate(Long id) { - // 校验存在 - validateSmsTemplateExists(id); - // 更新 - smsTemplateMapper.deleteById(id); - } - - private void validateSmsTemplateExists(Long id) { - if (smsTemplateMapper.selectById(id) == null) { - throw exception(SMS_TEMPLATE_NOT_EXISTS); - } - } - - @Override - public SmsTemplateDO getSmsTemplate(Long id) { - return smsTemplateMapper.selectById(id); - } - - @Override - @Cacheable(cacheNames = RedisKeyConstants.SMS_TEMPLATE, key = "#code", - unless = "#result == null") - public SmsTemplateDO getSmsTemplateByCodeFromCache(String code) { - return smsTemplateMapper.selectByCode(code); - } - - @Override - public PageResult<SmsTemplateDO> getSmsTemplatePage(SmsTemplatePageReqVO pageReqVO) { - return smsTemplateMapper.selectPage(pageReqVO); - } - - @Override - public Long getSmsTemplateCountByChannelId(Long channelId) { - return smsTemplateMapper.selectCountByChannelId(channelId); - } - - @VisibleForTesting - public SmsChannelDO validateSmsChannel(Long channelId) { - SmsChannelDO channelDO = smsChannelService.getSmsChannel(channelId); - if (channelDO == null) { - throw exception(SMS_CHANNEL_NOT_EXISTS); - } - if (CommonStatusEnum.isDisable(channelDO.getStatus())) { - throw exception(SMS_CHANNEL_DISABLE); - } - return channelDO; - } - - @VisibleForTesting - public void validateSmsTemplateCodeDuplicate(Long id, String code) { - SmsTemplateDO template = smsTemplateMapper.selectByCode(code); - if (template == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 - if (id == null) { - throw exception(SMS_TEMPLATE_CODE_DUPLICATE, code); - } - if (!template.getId().equals(id)) { - throw exception(SMS_TEMPLATE_CODE_DUPLICATE, code); - } - } - - /** - * 校验 API 短信平台的模板是否有效 - * - * @param channelId 渠道编号 - * @param apiTemplateId API 模板编号 - */ - @VisibleForTesting - void validateApiTemplate(Long channelId, String apiTemplateId) { - // 获得短信模板 - SmsClient smsClient = smsChannelService.getSmsClient(channelId); - Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", channelId)); - SmsTemplateRespDTO template; - try { - template = smsClient.getSmsTemplate(apiTemplateId); - } catch (Throwable ex) { - throw exception(SMS_TEMPLATE_API_ERROR, ExceptionUtil.getRootCauseMessage(ex)); - } - // 校验短信模版 - if (template == null) { - throw exception(SMS_TEMPLATE_API_NOT_FOUND); - } - if (Objects.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.CHECKING.getStatus())) { - throw exception(SMS_TEMPLATE_API_AUDIT_CHECKING); - } - if (Objects.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.FAIL.getStatus())) { - throw exception(SMS_TEMPLATE_API_AUDIT_FAIL, template.getAuditReason()); - } - Assert.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), - String.format("短信模板(%s) 审核状态(%d) 不正确", apiTemplateId, template.getAuditStatus())); - } - - @Override - public String formatSmsTemplateContent(String content, Map<String, Object> params) { - return StrUtil.format(content, params); - } - - @VisibleForTesting - List<String> parseTemplateContentParams(String content) { - return ReUtil.findAllGroup1(PATTERN_PARAMS, content); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/social/SocialClientService.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/social/SocialClientService.java deleted file mode 100644 index 7fc5afc..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/social/SocialClientService.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.iailab.module.system.service.social; - -import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; -import com.iailab.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; -import com.iailab.module.system.dal.dataobject.social.SocialClientDO; -import com.iailab.module.system.enums.social.SocialTypeEnum; -import com.xingyuv.jushauth.model.AuthUser; -import me.chanjar.weixin.common.bean.WxJsapiSignature; - -import javax.validation.Valid; - -/** - * 社交应用 Service 接口 - * - * @author iailab - */ -public interface SocialClientService { - - /** - * 获得社交平台的授权 URL - * - * @param socialType 社交平台的类型 {@link SocialTypeEnum} - * @param userType 用户类型 - * @param redirectUri 重定向 URL - * @return 社交平台的授权 URL - */ - String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri); - - /** - * 请求社交平台,获得授权的用户 - * - * @param socialType 社交平台的类型 - * @param userType 用户类型 - * @param code 授权码 - * @param state 授权 state - * @return 授权的用户 - */ - AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state); - - // =================== 微信公众号独有 =================== - - /** - * 创建微信公众号的 JS SDK 初始化所需的签名 - * - * @param userType 用户类型 - * @param url 访问的 URL 地址 - * @return 签名 - */ - WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url); - - // =================== 微信小程序独有 =================== - - /** - * 获得微信小程序的手机信息 - * - * @param userType 用户类型 - * @param phoneCode 手机授权码 - * @return 手机信息 - */ - WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(Integer userType, String phoneCode); - - // =================== 客户端管理 =================== - - /** - * 创建社交客户端 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createSocialClient(@Valid SocialClientSaveReqVO createReqVO); - - /** - * 更新社交客户端 - * - * @param updateReqVO 更新信息 - */ - void updateSocialClient(@Valid SocialClientSaveReqVO updateReqVO); - - /** - * 删除社交客户端 - * - * @param id 编号 - */ - void deleteSocialClient(Long id); - - /** - * 获得社交客户端 - * - * @param id 编号 - * @return 社交客户端 - */ - SocialClientDO getSocialClient(Long id); - - /** - * 获得社交客户端分页 - * - * @param pageReqVO 分页查询 - * @return 社交客户端分页 - */ - PageResult<SocialClientDO> getSocialClientPage(SocialClientPageReqVO pageReqVO); - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/social/SocialClientServiceImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/social/SocialClientServiceImpl.java deleted file mode 100644 index c0a400f..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/social/SocialClientServiceImpl.java +++ /dev/null @@ -1,351 +0,0 @@ -package com.iailab.module.system.service.social; - -import cn.binarywang.wx.miniapp.api.WxMaService; -import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; -import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; -import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl; -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.ReflectUtil; -import com.iailab.framework.common.enums.CommonStatusEnum; -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.framework.common.util.cache.CacheUtils; -import com.iailab.framework.common.util.http.HttpUtils; -import com.iailab.framework.common.util.object.BeanUtils; -import com.iailab.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; -import com.iailab.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; -import com.iailab.module.system.dal.dataobject.social.SocialClientDO; -import com.iailab.module.system.dal.mysql.social.SocialClientMapper; -import com.iailab.module.system.enums.social.SocialTypeEnum; -import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties; -import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.xingyuv.jushauth.config.AuthConfig; -import com.xingyuv.jushauth.model.AuthCallback; -import com.xingyuv.jushauth.model.AuthResponse; -import com.xingyuv.jushauth.model.AuthUser; -import com.xingyuv.jushauth.request.AuthRequest; -import com.xingyuv.jushauth.utils.AuthStateUtils; -import com.xingyuv.justauth.AuthRequestFactory; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import me.chanjar.weixin.common.bean.WxJsapiSignature; -import me.chanjar.weixin.common.error.WxErrorException; -import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps; -import me.chanjar.weixin.mp.api.WxMpService; -import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; -import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.time.Duration; -import java.util.Objects; - -import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.iailab.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache; -import static com.iailab.framework.common.util.json.JsonUtils.toJsonString; -import static com.iailab.module.system.enums.ErrorCodeConstants.*; - -/** - * 社交应用 Service 实现类 - * - * @author iailab - */ -@Service -@Slf4j -public class SocialClientServiceImpl implements SocialClientService { - - /** - * 小程序版本 - * - * 1. release:正式版 - * 2. trial:体验版 - * 3. developer:开发版 - */ - @Value("${yudao.wxa-code.env-version:release}") - public String envVersion; - - @Resource - private AuthRequestFactory authRequestFactory; - - @Resource - private WxMpService wxMpService; - @Resource - private WxMpProperties wxMpProperties; - @Resource - private StringRedisTemplate stringRedisTemplate; // WxMpService 需要使用到,所以在 Service 注入了它 - /** - * 缓存 WxMpService 对象 - * - * key:使用微信公众号的 appId + secret 拼接,即 {@link SocialClientDO} 的 clientId 和 clientSecret 属性。 - * 为什么 key 使用这种格式?因为 {@link SocialClientDO} 在管理后台可以变更,通过这个 key 存储它的单例。 - * - * 为什么要做 WxMpService 缓存?因为 WxMpService 构建成本比较大,所以尽量保证它是单例。 - */ - private final LoadingCache<String, WxMpService> wxMpServiceCache = buildAsyncReloadingCache( - Duration.ofSeconds(10L), - new CacheLoader<String, WxMpService>() { - - @Override - public WxMpService load(String key) { - String[] keys = key.split(":"); - return buildWxMpService(keys[0], keys[1]); - } - - }); - - @Resource - private WxMaService wxMaService; - @Resource - private WxMaProperties wxMaProperties; - /** - * 缓存 WxMaService 对象 - * - * 说明同 {@link #wxMpServiceCache} 变量 - */ - private final LoadingCache<String, WxMaService> wxMaServiceCache = buildAsyncReloadingCache( - Duration.ofSeconds(10L), - new CacheLoader<String, WxMaService>() { - - @Override - public WxMaService load(String key) { - String[] keys = key.split(":"); - return buildWxMaService(keys[0], keys[1]); - } - - }); - - @Resource - private SocialClientMapper socialClientMapper; - - @Override - public String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) { - // 获得对应的 AuthRequest 实现 - AuthRequest authRequest = buildAuthRequest(socialType, userType); - // 生成跳转地址 - String authorizeUri = authRequest.authorize(AuthStateUtils.createState()); - return HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri); - } - - @Override - public AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state) { - // 构建请求 - AuthRequest authRequest = buildAuthRequest(socialType, userType); - AuthCallback authCallback = AuthCallback.builder().code(code).state(state).build(); - // 执行请求 - AuthResponse<?> authResponse = authRequest.login(authCallback); - log.info("[getAuthUser][请求社交平台 type({}) request({}) response({})]", socialType, - toJsonString(authCallback), toJsonString(authResponse)); - if (!authResponse.ok()) { - throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg()); - } - return (AuthUser) authResponse.getData(); - } - - /** - * 构建 AuthRequest 对象,支持多租户配置 - * - * @param socialType 社交类型 - * @param userType 用户类型 - * @return AuthRequest 对象 - */ - @VisibleForTesting - AuthRequest buildAuthRequest(Integer socialType, Integer userType) { - // 1. 先查找默认的配置项,从 application-*.yaml 中读取 - AuthRequest request = authRequestFactory.get(SocialTypeEnum.valueOfType(socialType).getSource()); - Assert.notNull(request, String.format("社交平台(%d) 不存在", socialType)); - // 2. 查询 DB 的配置项,如果存在则进行覆盖 - SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(socialType, userType); - if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { - // 2.1 构造新的 AuthConfig 对象 - AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(request, "config"); - AuthConfig newAuthConfig = ReflectUtil.newInstance(authConfig.getClass()); - BeanUtil.copyProperties(authConfig, newAuthConfig); - // 2.2 修改对应的 clientId + clientSecret 密钥 - newAuthConfig.setClientId(client.getClientId()); - newAuthConfig.setClientSecret(client.getClientSecret()); - if (client.getAgentId() != null) { // 如果有 agentId 则修改 agentId - newAuthConfig.setAgentId(client.getAgentId()); - } - // 2.3 设置会 request 里,进行后续使用 - ReflectUtil.setFieldValue(request, "config", newAuthConfig); - } - return request; - } - - // =================== 微信公众号独有 =================== - - @Override - @SneakyThrows - public WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url) { - WxMpService service = getWxMpService(userType); - return service.createJsapiSignature(url); - } - - /** - * 获得 clientId + clientSecret 对应的 WxMpService 对象 - * - * @param userType 用户类型 - * @return WxMpService 对象 - */ - @VisibleForTesting - WxMpService getWxMpService(Integer userType) { - // 第一步,查询 DB 的配置项,获得对应的 WxMpService 对象 - SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType( - SocialTypeEnum.WECHAT_MP.getType(), userType); - if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { - return wxMpServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret()); - } - // 第二步,不存在 DB 配置项,则使用 application-*.yaml 对应的 WxMpService 对象 - return wxMpService; - } - - /** - * 创建 clientId + clientSecret 对应的 WxMpService 对象 - * - * @param clientId 微信公众号 appId - * @param clientSecret 微信公众号 secret - * @return WxMpService 对象 - */ - public WxMpService buildWxMpService(String clientId, String clientSecret) { - // 第一步,创建 WxMpRedisConfigImpl 对象 - WxMpRedisConfigImpl configStorage = new WxMpRedisConfigImpl( - new RedisTemplateWxRedisOps(stringRedisTemplate), - wxMpProperties.getConfigStorage().getKeyPrefix()); - configStorage.setAppId(clientId); - configStorage.setSecret(clientSecret); - - // 第二步,创建 WxMpService 对象 - WxMpService service = new WxMpServiceImpl(); - service.setWxMpConfigStorage(configStorage); - return service; - } - - // =================== 微信小程序独有 =================== - - @Override - public WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(Integer userType, String phoneCode) { - WxMaService service = getWxMaService(userType); - try { - return service.getUserService().getPhoneNoInfo(phoneCode); - } catch (WxErrorException e) { - log.error("[getPhoneNoInfo][userType({}) phoneCode({}) 获得手机号失败]", userType, phoneCode, e); - throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR); - } - } - - /** - * 获得 clientId + clientSecret 对应的 WxMpService 对象 - * - * @param userType 用户类型 - * @return WxMpService 对象 - */ - @VisibleForTesting - WxMaService getWxMaService(Integer userType) { - // 第一步,查询 DB 的配置项,获得对应的 WxMaService 对象 - SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType( - SocialTypeEnum.WECHAT_MINI_APP.getType(), userType); - if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { - return wxMaServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret()); - } - // 第二步,不存在 DB 配置项,则使用 application-*.yaml 对应的 WxMaService 对象 - return wxMaService; - } - - /** - * 创建 clientId + clientSecret 对应的 WxMaService 对象 - * - * @param clientId 微信小程序 appId - * @param clientSecret 微信小程序 secret - * @return WxMaService 对象 - */ - private WxMaService buildWxMaService(String clientId, String clientSecret) { - // 第一步,创建 WxMaRedisBetterConfigImpl 对象 - WxMaRedisBetterConfigImpl configStorage = new WxMaRedisBetterConfigImpl( - new RedisTemplateWxRedisOps(stringRedisTemplate), - wxMaProperties.getConfigStorage().getKeyPrefix()); - configStorage.setAppid(clientId); - configStorage.setSecret(clientSecret); - - // 第二步,创建 WxMpService 对象 - WxMaService service = new WxMaServiceImpl(); - service.setWxMaConfig(configStorage); - return service; - } - - // =================== 客户端管理 =================== - - @Override - public Long createSocialClient(SocialClientSaveReqVO createReqVO) { - // 校验重复 - validateSocialClientUnique(null, createReqVO.getUserType(), createReqVO.getSocialType()); - - // 插入 - SocialClientDO client = BeanUtils.toBean(createReqVO, SocialClientDO.class); - socialClientMapper.insert(client); - return client.getId(); - } - - @Override - public void updateSocialClient(SocialClientSaveReqVO updateReqVO) { - // 校验存在 - validateSocialClientExists(updateReqVO.getId()); - // 校验重复 - validateSocialClientUnique(updateReqVO.getId(), updateReqVO.getUserType(), updateReqVO.getSocialType()); - - // 更新 - SocialClientDO updateObj = BeanUtils.toBean(updateReqVO, SocialClientDO.class); - socialClientMapper.updateById(updateObj); - } - - @Override - public void deleteSocialClient(Long id) { - // 校验存在 - validateSocialClientExists(id); - // 删除 - socialClientMapper.deleteById(id); - } - - private void validateSocialClientExists(Long id) { - if (socialClientMapper.selectById(id) == null) { - throw exception(SOCIAL_CLIENT_NOT_EXISTS); - } - } - - /** - * 校验社交应用是否重复,需要保证 userType + socialType 唯一 - * - * 原因是,不同端(userType)选择某个社交登录(socialType)时,需要通过 {@link #buildAuthRequest(Integer, Integer)} 构建对应的请求 - * - * @param id 编号 - * @param userType 用户类型 - * @param socialType 社交类型 - */ - private void validateSocialClientUnique(Long id, Integer userType, Integer socialType) { - SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType( - socialType, userType); - if (client == null) { - return; - } - if (id == null // 新增时,说明重复 - || ObjUtil.notEqual(id, client.getId())) { // 更新时,如果 id 不一致,说明重复 - throw exception(SOCIAL_CLIENT_UNIQUE); - } - } - - @Override - public SocialClientDO getSocialClient(Long id) { - return socialClientMapper.selectById(id); - } - - @Override - public PageResult<SocialClientDO> getSocialClientPage(SocialClientPageReqVO pageReqVO) { - return socialClientMapper.selectPage(pageReqVO); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/social/SocialUserService.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/social/SocialUserService.java deleted file mode 100644 index f017f8d..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/social/SocialUserService.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.iailab.module.system.service.social; - -import com.iailab.framework.common.exception.ServiceException; -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.module.system.api.social.dto.SocialUserBindReqDTO; -import com.iailab.module.system.api.social.dto.SocialUserRespDTO; -import com.iailab.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; -import com.iailab.module.system.dal.dataobject.social.SocialUserDO; -import com.iailab.module.system.enums.social.SocialTypeEnum; - -import javax.validation.Valid; -import java.util.List; - -/** - * 社交用户 Service 接口,例如说社交平台的授权登录 - * - * @author iailab - */ -public interface SocialUserService { - - /** - * 获得指定用户的社交用户列表 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @return 社交用户列表 - */ - List<SocialUserDO> getSocialUserList(Long userId, Integer userType); - - /** - * 绑定社交用户 - * - * @param reqDTO 绑定信息 - * @return 社交用户 openid - */ - String bindSocialUser(@Valid SocialUserBindReqDTO reqDTO); - - /** - * 取消绑定社交用户 - * - * @param userId 用户编号 - * @param userType 全局用户类型 - * @param socialType 社交平台的类型 {@link SocialTypeEnum} - * @param openid 社交平台的 openid - */ - void unbindSocialUser(Long userId, Integer userType, Integer socialType, String openid); - - /** - * 获得社交用户,基于 userId - * - * @param userType 用户类型 - * @param userId 用户编号 - * @param socialType 社交平台的类型 - * @return 社交用户 - */ - SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType); - - /** - * 获得社交用户 - * - * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常 - * - * @param userType 用户类型 - * @param socialType 社交平台的类型 - * @param code 授权码 - * @param state state - * @return 社交用户 - */ - SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state); - - // ==================== 社交用户 CRUD ==================== - - /** - * 获得社交用户 - * - * @param id 编号 - * @return 社交用户 - */ - SocialUserDO getSocialUser(Long id); - - /** - * 获得社交用户分页 - * - * @param pageReqVO 分页查询 - * @return 社交用户分页 - */ - PageResult<SocialUserDO> getSocialUserPage(SocialUserPageReqVO pageReqVO); - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/social/SocialUserServiceImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/social/SocialUserServiceImpl.java deleted file mode 100644 index 177b1b6..0000000 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/social/SocialUserServiceImpl.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.iailab.module.system.service.social; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import com.iailab.framework.common.exception.ServiceException; -import com.iailab.framework.common.pojo.PageResult; -import com.iailab.module.system.api.social.dto.SocialUserBindReqDTO; -import com.iailab.module.system.api.social.dto.SocialUserRespDTO; -import com.iailab.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; -import com.iailab.module.system.dal.dataobject.social.SocialUserBindDO; -import com.iailab.module.system.dal.dataobject.social.SocialUserDO; -import com.iailab.module.system.dal.mysql.social.SocialUserBindMapper; -import com.iailab.module.system.dal.mysql.social.SocialUserMapper; -import com.iailab.module.system.enums.social.SocialTypeEnum; -import com.xingyuv.jushauth.model.AuthUser; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import javax.validation.constraints.NotNull; -import java.util.Collections; -import java.util.List; - -import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.iailab.framework.common.util.collection.CollectionUtils.convertSet; -import static com.iailab.framework.common.util.json.JsonUtils.toJsonString; -import static com.iailab.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND; - -/** - * 社交用户 Service 实现类 - * - * @author iailab - */ -@Service -@Validated -@Slf4j -public class SocialUserServiceImpl implements SocialUserService { - - @Resource - private SocialUserBindMapper socialUserBindMapper; - @Resource - private SocialUserMapper socialUserMapper; - - @Resource - private SocialClientService socialClientService; - - @Override - public List<SocialUserDO> getSocialUserList(Long userId, Integer userType) { - // 获得绑定 - List<SocialUserBindDO> socialUserBinds = socialUserBindMapper.selectListByUserIdAndUserType(userId, userType); - if (CollUtil.isEmpty(socialUserBinds)) { - return Collections.emptyList(); - } - // 获得社交用户 - return socialUserMapper.selectBatchIds(convertSet(socialUserBinds, SocialUserBindDO::getSocialUserId)); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public String bindSocialUser(SocialUserBindReqDTO reqDTO) { - // 获得社交用户 - SocialUserDO socialUser = authSocialUser(reqDTO.getSocialType(), reqDTO.getUserType(), - reqDTO.getCode(), reqDTO.getState()); - Assert.notNull(socialUser, "社交用户不能为空"); - - // 社交用户可能之前绑定过别的用户,需要进行解绑 - socialUserBindMapper.deleteByUserTypeAndSocialUserId(reqDTO.getUserType(), socialUser.getId()); - - // 用户可能之前已经绑定过该社交类型,需要进行解绑 - socialUserBindMapper.deleteByUserTypeAndUserIdAndSocialType(reqDTO.getUserType(), reqDTO.getUserId(), - socialUser.getType()); - - // 绑定当前登录的社交用户 - SocialUserBindDO socialUserBind = SocialUserBindDO.builder() - .userId(reqDTO.getUserId()).userType(reqDTO.getUserType()) - .socialUserId(socialUser.getId()).socialType(socialUser.getType()).build(); - socialUserBindMapper.insert(socialUserBind); - return socialUser.getOpenid(); - } - - @Override - public void unbindSocialUser(Long userId, Integer userType, Integer socialType, String openid) { - // 获得 openid 对应的 SocialUserDO 社交用户 - SocialUserDO socialUser = socialUserMapper.selectByTypeAndOpenid(socialType, openid); - if (socialUser == null) { - throw exception(SOCIAL_USER_NOT_FOUND); - } - - // 获得对应的社交绑定关系 - socialUserBindMapper.deleteByUserTypeAndUserIdAndSocialType(userType, userId, socialUser.getType()); - } - - @Override - public SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType) { - // 获得绑定用户 - SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserIdAndUserTypeAndSocialType(userId, userType, socialType); - if (socialUserBind == null) { - return null; - } - // 获得社交用户 - SocialUserDO socialUser = socialUserMapper.selectById(socialUserBind.getSocialUserId()); - Assert.notNull(socialUser, "社交用户不能为空"); - return new SocialUserRespDTO(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getAvatar(), - socialUserBind.getUserId()); - } - - @Override - public SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state) { - // 获得社交用户 - SocialUserDO socialUser = authSocialUser(socialType, userType, code, state); - Assert.notNull(socialUser, "社交用户不能为空"); - - // 获得绑定用户 - SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserTypeAndSocialUserId(userType, - socialUser.getId()); - return new SocialUserRespDTO(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getAvatar(), - socialUserBind != null ? socialUserBind.getUserId() : null); - } - - /** - * 授权获得对应的社交用户 - * 如果授权失败,则会抛出 {@link ServiceException} 异常 - * - * @param socialType 社交平台的类型 {@link SocialTypeEnum} - * @param userType 用户类型 - * @param code 授权码 - * @param state state - * @return 授权用户 - */ - @NotNull - public SocialUserDO authSocialUser(Integer socialType, Integer userType, String code, String state) { - // 优先从 DB 中获取,因为 code 有且可以使用一次。 - // 在社交登录时,当未绑定 User 时,需要绑定登录,此时需要 code 使用两次 - SocialUserDO socialUser = socialUserMapper.selectByTypeAndCodeAnState(socialType, code, state); - if (socialUser != null) { - return socialUser; - } - - // 请求获取 - AuthUser authUser = socialClientService.getAuthUser(socialType, userType, code, state); - Assert.notNull(authUser, "三方用户不能为空"); - - // 保存到 DB 中 - socialUser = socialUserMapper.selectByTypeAndOpenid(socialType, authUser.getUuid()); - if (socialUser == null) { - socialUser = new SocialUserDO(); - } - socialUser.setType(socialType).setCode(code).setState(state) // 需要保存 code + state 字段,保证后续可查询 - .setOpenid(authUser.getUuid()).setToken(authUser.getToken().getAccessToken()).setRawTokenInfo((toJsonString(authUser.getToken()))) - .setNickname(authUser.getNickname()).setAvatar(authUser.getAvatar()).setRawUserInfo(toJsonString(authUser.getRawUserInfo())); - if (socialUser.getId() == null) { - socialUserMapper.insert(socialUser); - } else { - socialUserMapper.updateById(socialUser); - } - return socialUser; - } - - // ==================== 社交用户 CRUD ==================== - - @Override - public SocialUserDO getSocialUser(Long id) { - return socialUserMapper.selectById(id); - } - - @Override - public PageResult<SocialUserDO> getSocialUserPage(SocialUserPageReqVO pageReqVO) { - return socialUserMapper.selectPage(pageReqVO); - } - -} diff --git a/iailab-module-system/iailab-module-system-biz/src/main/resources/application-dev.yaml b/iailab-module-system/iailab-module-system-biz/src/main/resources/application-dev.yaml index 4188c19..3d08576 100644 --- a/iailab-module-system/iailab-module-system-biz/src/main/resources/application-dev.yaml +++ b/iailab-module-system/iailab-module-system-biz/src/main/resources/application-dev.yaml @@ -39,7 +39,7 @@ primary: master datasource: master: - url: jdbc:mysql://172.16.8.100:3306/iailab_plat_system?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + url: jdbc:mysql://localhost:3306/iailab_plat_system?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 username: root password: 123456 slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 @@ -64,7 +64,7 @@ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: - host: 172.16.8.200 # RabbitMQ 服务的地址 + host: 172.16.1.221 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: admin # RabbitMQ 服务的账号 password: admin123 # RabbitMQ 服务的密码 @@ -72,6 +72,13 @@ kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 +--- #################### 定时任务相关配置 #################### +xxl: + job: + enabled: true # 是否开启调度中心,默认为 true 开启 + admin: + addresses: http://172.16.216.135:9090/xxl-job-admin # 调度中心部署跟地址 + --- #################### 服务保障相关配置 #################### # Lock4j 配置项 diff --git a/iailab-module-system/iailab-module-system-biz/src/main/resources/application.yaml b/iailab-module-system/iailab-module-system-biz/src/main/resources/application.yaml index c2167c8..15f0ab1 100644 --- a/iailab-module-system/iailab-module-system-biz/src/main/resources/application.yaml +++ b/iailab-module-system/iailab-module-system-biz/src/main/resources/application.yaml @@ -12,6 +12,7 @@ username: @nacos.username@ password: @nacos.password@ discovery: # 【配置中心】配置项 +# ip: 172.16.8.200 namespace: @profiles.active@ group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP metadata: @@ -155,6 +156,16 @@ req-check-minute-limit: 60 # check 接口一分钟内请求数限制 req-verify-minute-limit: 60 # verify 接口一分钟内请求数限制 +--- #################### 定时任务相关配置 #################### + +xxl: + job: + executor: +# ip: 172.16.8.200 + appname: ${spring.application.name} # 执行器 AppName + logpath: D:/DLUT/IailabPlat/webapp/infra/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径 + accessToken: default_token # 执行器通讯TOKEN + --- #################### 平台相关配置 #################### iailab: diff --git a/iailab-plat-sdk/src/main/java/com/iailab/sdk/auth/client/IailabClient.java b/iailab-plat-sdk/src/main/java/com/iailab/sdk/auth/client/IailabClient.java index 96b3342..ce84464 100644 --- a/iailab-plat-sdk/src/main/java/com/iailab/sdk/auth/client/IailabClient.java +++ b/iailab-plat-sdk/src/main/java/com/iailab/sdk/auth/client/IailabClient.java @@ -80,7 +80,7 @@ private static final String RESP_DATA = "data"; /** - * 用户名密码方式获取平台token + * 客户端方式获取平台token */ public static synchronized TokenDTO authenticate() { System.out.println("登录获取平台token"); diff --git a/pom.xml b/pom.xml index fb75d8b..c396f1a 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,6 @@ <version>${revision}</version> <packaging>pom</packaging> <modules> - <module>iailab-framework</module> <module>iailab-cloud</module> <!-- 各种 module 拓展 --> <module>iailab-module-system</module> @@ -17,6 +16,7 @@ <module>iailab-module-report</module> <module>iailab-module-data</module> <module>iailab-module-model</module> + <module>iailab-plat-sdk</module> </modules> <name>${project.artifactId}</name> @@ -97,7 +97,7 @@ <okhttp3.version>4.11.0</okhttp3.version> <commons-io.version>2.15.1</commons-io.version> <minio.version>8.5.7</minio.version> - <aliyun-java-sdk-core.version>4.6.4</aliyun-java-sdk-core.version> +<!-- <aliyun-java-sdk-core.version>4.6.4</aliyun-java-sdk-core.version>--> <aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version> <tencentcloud-sdk-java.version>3.1.880</tencentcloud-sdk-java.version> <justauth.version>1.0.8</justauth.version> @@ -114,7 +114,7 @@ <properties> <!-- 环境标识,需要与配置文件的名称相对应 --> <profiles.active>dev</profiles.active> - <nacos.server>127.0.0.1:8848</nacos.server> + <nacos.server>localhost:8848</nacos.server> <nacos.discovery.group>DEFAULT_GROUP</nacos.discovery.group> <nacos.config.group>DEFAULT_GROUP</nacos.config.group> <nacos.username>nacos</nacos.username> @@ -122,7 +122,7 @@ <nacos.metadata.version>1.0.0</nacos.metadata.version> <log.path>D:/DLUT/iailab-plat</log.path> <logstash.address>127.0.0.1:4560</logstash.address> - <deploy.server>127.0.0.1</deploy.server> + <deploy.server>192.168.56.1</deploy.server> </properties> <activation> <!-- 默认环境 --> @@ -154,25 +154,25 @@ <nacos.discovery.group>DEFAULT_GROUP</nacos.discovery.group> <nacos.config.group>DEFAULT_GROUP</nacos.config.group> <nacos.username>nacos</nacos.username> - <nacos.password>nacos</nacos.password> + <nacos.password>fG2@jH6#kI</nacos.password> <nacos.metadata.version>1.0.0</nacos.metadata.version> <log.path>.</log.path> <logstash.address>127.0.0.1:4560</logstash.address> - <deploy.server>10.88.4.131</deploy.server> + <deploy.server>10.200.25.49</deploy.server> </properties> </profile> </profiles> <dependencyManagement> <dependencies> - <!-- 统一依赖管理 --> - <dependency> - <groupId>io.netty</groupId> - <artifactId>netty-bom</artifactId> - <version>${netty.version}</version> - <type>pom</type> - <scope>import</scope> - </dependency> +<!-- <!– 统一依赖管理 –>--> +<!-- <dependency>--> +<!-- <groupId>io.netty</groupId>--> +<!-- <artifactId>netty-bom</artifactId>--> +<!-- <version>${netty.version}</version>--> +<!-- <type>pom</type>--> +<!-- <scope>import</scope>--> +<!-- </dependency>--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-framework-bom</artifactId> <!-- JDK8 版本独有:保证 Spring Framework 尽量高 --> @@ -209,18 +209,18 @@ <scope>import</scope> </dependency> - <!-- 业务组件 --> - <dependency> - <groupId>io.github.mouzt</groupId> - <artifactId>bizlog-sdk</artifactId> - <version>${bizlog-sdk.version}</version> - <exclusions> - <exclusion> <!-- 排除掉springboot依赖使用项目的 --> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter</artifactId> - </exclusion> - </exclusions> - </dependency> +<!-- <!– 业务组件 –>--> +<!-- <dependency>--> +<!-- <groupId>io.github.mouzt</groupId>--> +<!-- <artifactId>bizlog-sdk</artifactId>--> +<!-- <version>${bizlog-sdk.version}</version>--> +<!-- <exclusions>--> +<!-- <exclusion> <!– 排除掉springboot依赖使用项目的 –>--> +<!-- <groupId>org.springframework.boot</groupId>--> +<!-- <artifactId>spring-boot-starter</artifactId>--> +<!-- </exclusion>--> +<!-- </exclusions>--> +<!-- </dependency>--> <dependency> <groupId>com.iailab</groupId> <artifactId>iailab-common-biz-tenant</artifactId> @@ -237,13 +237,13 @@ <version>${revision}</version> </dependency> - <!-- Spring 核心 --> - <dependency> - <!-- 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 --> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-configuration-processor</artifactId> - <version>${spring.boot.version}</version> - </dependency> +<!-- <!– Spring 核心 –>--> +<!-- <dependency>--> +<!-- <!– 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 –>--> +<!-- <groupId>org.springframework.boot</groupId>--> +<!-- <artifactId>spring-boot-configuration-processor</artifactId>--> +<!-- <version>${spring.boot.version}</version>--> +<!-- </dependency>--> <dependency> <groupId>com.iailab</groupId> @@ -286,48 +286,48 @@ <version>${knife4j.version}</version> </dependency> - <!-- DB 相关 --> - <dependency> - <groupId>org.postgresql</groupId> - <artifactId>postgresql</artifactId> - <version>${postgresql.version}</version> - </dependency> +<!-- <!– DB 相关 –>--> +<!-- <dependency>--> +<!-- <groupId>org.postgresql</groupId>--> +<!-- <artifactId>postgresql</artifactId>--> +<!-- <version>${postgresql.version}</version>--> +<!-- </dependency>--> <dependency> <groupId>com.iailab</groupId> <artifactId>iailab-common-mybatis</artifactId> <version>${revision}</version> </dependency> - <dependency> - <groupId>org.springdoc</groupId> - <artifactId>springdoc-openapi-webflux-ui</artifactId> - <version>${springdoc.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>org.springdoc</groupId>--> +<!-- <artifactId>springdoc-openapi-webflux-ui</artifactId>--> +<!-- <version>${springdoc.version}</version>--> +<!-- </dependency>--> - <dependency> - <groupId>com.alibaba</groupId> - <artifactId>druid-spring-boot-starter</artifactId> - <version>${druid.version}</version> - </dependency> - <dependency> - <groupId>com.baomidou</groupId> - <artifactId>mybatis-plus-boot-starter</artifactId> - <version>${mybatis-plus.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>com.alibaba</groupId>--> +<!-- <artifactId>druid-spring-boot-starter</artifactId>--> +<!-- <version>${druid.version}</version>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>com.baomidou</groupId>--> +<!-- <artifactId>mybatis-plus-boot-starter</artifactId>--> +<!-- <version>${mybatis-plus.version}</version>--> +<!-- </dependency>--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器,使用它解析表结构 --> <version>${mybatis-plus-generator.version}</version> </dependency> - <dependency> - <groupId>com.baomidou</groupId> - <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 --> - <version>${dynamic-datasource.version}</version> - </dependency> - <dependency> - <groupId>com.github.yulichang</groupId> - <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 --> - <version>${mybatis-plus-join.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>com.baomidou</groupId>--> +<!-- <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!– 多数据源 –>--> +<!-- <version>${dynamic-datasource.version}</version>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>com.github.yulichang</groupId>--> +<!-- <artifactId>mybatis-plus-join-boot-starter</artifactId> <!– MyBatis 联表查询 –>--> +<!-- <version>${mybatis-plus-join.version}</version>--> +<!-- </dependency>--> <dependency> <groupId>com.iailab</groupId> @@ -335,43 +335,43 @@ <version>${revision}</version> </dependency> - <dependency> - <groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 --> - <artifactId>easy-trans-spring-boot-starter</artifactId> - <version>${easy-trans.version}</version> - <exclusions> - <exclusion> - <groupId>org.springframework</groupId> - <artifactId>spring-context</artifactId> - </exclusion> - <exclusion> - <groupId>org.springframework.cloud</groupId> - <artifactId>spring-cloud-commons</artifactId> - </exclusion> - </exclusions> - </dependency> - <dependency> - <groupId>com.fhs-opensource</groupId> - <artifactId>easy-trans-mybatis-plus-extend</artifactId> - <version>${easy-trans.version}</version> - </dependency> - <dependency> - <groupId>com.fhs-opensource</groupId> - <artifactId>easy-trans-anno</artifactId> - <version>${easy-trans.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>com.fhs-opensource</groupId> <!– VO 数据翻译 –>--> +<!-- <artifactId>easy-trans-spring-boot-starter</artifactId>--> +<!-- <version>${easy-trans.version}</version>--> +<!-- <exclusions>--> +<!-- <exclusion>--> +<!-- <groupId>org.springframework</groupId>--> +<!-- <artifactId>spring-context</artifactId>--> +<!-- </exclusion>--> +<!-- <exclusion>--> +<!-- <groupId>org.springframework.cloud</groupId>--> +<!-- <artifactId>spring-cloud-commons</artifactId>--> +<!-- </exclusion>--> +<!-- </exclusions>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>com.fhs-opensource</groupId>--> +<!-- <artifactId>easy-trans-mybatis-plus-extend</artifactId>--> +<!-- <version>${easy-trans.version}</version>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>com.fhs-opensource</groupId>--> +<!-- <artifactId>easy-trans-anno</artifactId>--> +<!-- <version>${easy-trans.version}</version>--> +<!-- </dependency>--> - <dependency> - <groupId>org.redisson</groupId> - <artifactId>redisson-spring-boot-starter</artifactId> - <version>${redisson.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>org.redisson</groupId>--> +<!-- <artifactId>redisson-spring-boot-starter</artifactId>--> +<!-- <version>${redisson.version}</version>--> +<!-- </dependency>--> - <dependency> - <groupId>com.dameng</groupId> - <artifactId>DmJdbcDriver18</artifactId> - <version>${dm8.jdbc.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>com.dameng</groupId>--> +<!-- <artifactId>DmJdbcDriver18</artifactId>--> +<!-- <version>${dm8.jdbc.version}</version>--> +<!-- </dependency>--> <!-- RPC 远程调用相关 --> <dependency> @@ -380,9 +380,9 @@ <version>${revision}</version> </dependency> - <!-- Registry 注册中心相关 --> +<!-- <!– Registry 注册中心相关 –>--> - <!-- Config 配置中心相关 --> +<!-- <!– Config 配置中心相关 –>--> <!-- Job 定时任务相关 --> <dependency> @@ -403,11 +403,11 @@ <version>${revision}</version> </dependency> - <dependency> - <groupId>org.apache.rocketmq</groupId> - <artifactId>rocketmq-spring-boot-starter</artifactId> - <version>${rocketmq-spring.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>org.apache.rocketmq</groupId>--> +<!-- <artifactId>rocketmq-spring-boot-starter</artifactId>--> +<!-- <version>${rocketmq-spring.version}</version>--> +<!-- </dependency>--> <!-- 服务保障相关 --> <dependency> @@ -416,17 +416,17 @@ <version>${revision}</version> </dependency> - <dependency> - <groupId>com.baomidou</groupId> - <artifactId>lock4j-redisson-spring-boot-starter</artifactId> - <version>${lock4j.version}</version> - <exclusions> - <exclusion> - <artifactId>redisson-spring-boot-starter</artifactId> - <groupId>org.redisson</groupId> - </exclusion> - </exclusions> - </dependency> +<!-- <dependency>--> +<!-- <groupId>com.baomidou</groupId>--> +<!-- <artifactId>lock4j-redisson-spring-boot-starter</artifactId>--> +<!-- <version>${lock4j.version}</version>--> +<!-- <exclusions>--> +<!-- <exclusion>--> +<!-- <artifactId>redisson-spring-boot-starter</artifactId>--> +<!-- <groupId>org.redisson</groupId>--> +<!-- </exclusion>--> +<!-- </exclusions>--> +<!-- </dependency>--> <!-- 监控相关 --> <dependency> @@ -435,57 +435,57 @@ <version>${revision}</version> </dependency> - <dependency> - <groupId>org.apache.skywalking</groupId> - <artifactId>apm-toolkit-trace</artifactId> - <version>${skywalking.version}</version> - </dependency> - <dependency> - <groupId>org.apache.skywalking</groupId> - <artifactId>apm-toolkit-logback-1.x</artifactId> - <version>${skywalking.version}</version> - </dependency> - <dependency> - <groupId>org.apache.skywalking</groupId> - <artifactId>apm-toolkit-opentracing</artifactId> - <version>${skywalking.version}</version> - <!-- <exclusions>--> - <!-- <exclusion>--> - <!-- <artifactId>opentracing-api</artifactId>--> - <!-- <groupId>io.opentracing</groupId>--> - <!-- </exclusion>--> - <!-- <exclusion>--> - <!-- <artifactId>opentracing-util</artifactId>--> - <!-- <groupId>io.opentracing</groupId>--> - <!-- </exclusion>--> - <!-- </exclusions>--> - </dependency> - <dependency> - <groupId>io.opentracing</groupId> - <artifactId>opentracing-api</artifactId> - <version>${opentracing.version}</version> - </dependency> - <dependency> - <groupId>io.opentracing</groupId> - <artifactId>opentracing-util</artifactId> - <version>${opentracing.version}</version> - </dependency> - <dependency> - <groupId>io.opentracing</groupId> - <artifactId>opentracing-noop</artifactId> - <version>${opentracing.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>org.apache.skywalking</groupId>--> +<!-- <artifactId>apm-toolkit-trace</artifactId>--> +<!-- <version>${skywalking.version}</version>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>org.apache.skywalking</groupId>--> +<!-- <artifactId>apm-toolkit-logback-1.x</artifactId>--> +<!-- <version>${skywalking.version}</version>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>org.apache.skywalking</groupId>--> +<!-- <artifactId>apm-toolkit-opentracing</artifactId>--> +<!-- <version>${skywalking.version}</version>--> +<!-- <!– <exclusions>–>--> +<!-- <!– <exclusion>–>--> +<!-- <!– <artifactId>opentracing-api</artifactId>–>--> +<!-- <!– <groupId>io.opentracing</groupId>–>--> +<!-- <!– </exclusion>–>--> +<!-- <!– <exclusion>–>--> +<!-- <!– <artifactId>opentracing-util</artifactId>–>--> +<!-- <!– <groupId>io.opentracing</groupId>–>--> +<!-- <!– </exclusion>–>--> +<!-- <!– </exclusions>–>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>io.opentracing</groupId>--> +<!-- <artifactId>opentracing-api</artifactId>--> +<!-- <version>${opentracing.version}</version>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>io.opentracing</groupId>--> +<!-- <artifactId>opentracing-util</artifactId>--> +<!-- <version>${opentracing.version}</version>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>io.opentracing</groupId>--> +<!-- <artifactId>opentracing-noop</artifactId>--> +<!-- <version>${opentracing.version}</version>--> +<!-- </dependency>--> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-server</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 --> <version>${spring-boot-admin.version}</version> </dependency> - <dependency> - <groupId>de.codecentric</groupId> - <artifactId>spring-boot-admin-starter-client</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 --> - <version>${spring-boot-admin.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>de.codecentric</groupId>--> +<!-- <artifactId>spring-boot-admin-starter-client</artifactId> <!– 实现 Spring Boot Admin Server 服务端 –>--> +<!-- <version>${spring-boot-admin.version}</version>--> +<!-- </dependency>--> <!-- Test 测试相关 --> <dependency> @@ -495,39 +495,39 @@ <scope>test</scope> </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-inline</artifactId> - <version>${mockito-inline.version}</version> <!-- 支持 Mockito 的 final 类与 static 方法的 mock --> - </dependency> +<!-- <dependency>--> +<!-- <groupId>org.mockito</groupId>--> +<!-- <artifactId>mockito-inline</artifactId>--> +<!-- <version>${mockito-inline.version}</version> <!– 支持 Mockito 的 final 类与 static 方法的 mock –>--> +<!-- </dependency>--> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-test</artifactId> - <version>${spring.boot.version}</version> - <exclusions> - <exclusion> - <artifactId>asm</artifactId> - <groupId>org.ow2.asm</groupId> - </exclusion> - <exclusion> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - </exclusion> - </exclusions> - </dependency> +<!-- <dependency>--> +<!-- <groupId>org.springframework.boot</groupId>--> +<!-- <artifactId>spring-boot-starter-test</artifactId>--> +<!-- <version>${spring.boot.version}</version>--> +<!-- <exclusions>--> +<!-- <exclusion>--> +<!-- <artifactId>asm</artifactId>--> +<!-- <groupId>org.ow2.asm</groupId>--> +<!-- </exclusion>--> +<!-- <exclusion>--> +<!-- <groupId>org.mockito</groupId>--> +<!-- <artifactId>mockito-core</artifactId>--> +<!-- </exclusion>--> +<!-- </exclusions>--> +<!-- </dependency>--> - <dependency> - <groupId>com.github.fppt</groupId> <!-- 单元测试,我们采用内嵌的 Redis 数据库 --> - <artifactId>jedis-mock</artifactId> - <version>${jedis-mock.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>com.github.fppt</groupId> <!– 单元测试,我们采用内嵌的 Redis 数据库 –>--> +<!-- <artifactId>jedis-mock</artifactId>--> +<!-- <version>${jedis-mock.version}</version>--> +<!-- </dependency>--> - <dependency> - <groupId>uk.co.jemos.podam</groupId> <!-- 单元测试,随机生成 POJO 类 --> - <artifactId>podam</artifactId> - <version>${podam.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>uk.co.jemos.podam</groupId> <!– 单元测试,随机生成 POJO 类 –>--> +<!-- <artifactId>podam</artifactId>--> +<!-- <version>${podam.version}</version>--> +<!-- </dependency>--> <!-- 工作流相关 --> <dependency> @@ -555,51 +555,51 @@ <version>${revision}</version> </dependency> - <dependency> - <groupId>org.projectlombok</groupId> - <artifactId>lombok</artifactId> - <version>${lombok.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>org.projectlombok</groupId>--> +<!-- <artifactId>lombok</artifactId>--> +<!-- <version>${lombok.version}</version>--> +<!-- </dependency>--> - <dependency> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher --> - <version>${mapstruct.version}</version> - </dependency> - <dependency> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct-jdk8</artifactId> - <version>${mapstruct.version}</version> - </dependency> - <dependency> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct-processor</artifactId> - <version>${mapstruct.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>org.mapstruct</groupId>--> +<!-- <artifactId>mapstruct</artifactId> <!– use mapstruct-jdk8 for Java 8 or higher –>--> +<!-- <version>${mapstruct.version}</version>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>org.mapstruct</groupId>--> +<!-- <artifactId>mapstruct-jdk8</artifactId>--> +<!-- <version>${mapstruct.version}</version>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>org.mapstruct</groupId>--> +<!-- <artifactId>mapstruct-processor</artifactId>--> +<!-- <version>${mapstruct.version}</version>--> +<!-- </dependency>--> - <dependency> - <groupId>cn.hutool</groupId> - <artifactId>hutool-all</artifactId> - <version>${hutool.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>cn.hutool</groupId>--> +<!-- <artifactId>hutool-all</artifactId>--> +<!-- <version>${hutool.version}</version>--> +<!-- </dependency>--> - <dependency> - <groupId>joda-time</groupId> - <artifactId>joda-time</artifactId> - <version>${joda.time.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>joda-time</groupId>--> +<!-- <artifactId>joda-time</artifactId>--> +<!-- <version>${joda.time.version}</version>--> +<!-- </dependency>--> - <dependency> - <groupId>com.alibaba</groupId> - <artifactId>easyexcel</artifactId> - <version>${easyexcel.verion}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>com.alibaba</groupId>--> +<!-- <artifactId>easyexcel</artifactId>--> +<!-- <version>${easyexcel.verion}</version>--> +<!-- </dependency>--> - <dependency> - <groupId>commons-io</groupId> - <artifactId>commons-io</artifactId> - <version>${commons-io.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>commons-io</groupId>--> +<!-- <artifactId>commons-io</artifactId>--> +<!-- <version>${commons-io.version}</version>--> +<!-- </dependency>--> <dependency> <groupId>org.apache.tika</groupId> @@ -613,27 +613,27 @@ <version>${velocity.version}</version> </dependency> - <dependency> - <groupId>com.alibaba</groupId> - <artifactId>fastjson</artifactId> - <version>${fastjson.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>com.alibaba</groupId>--> +<!-- <artifactId>fastjson</artifactId>--> +<!-- <version>${fastjson.version}</version>--> +<!-- </dependency>--> - <dependency> - <groupId>cn.smallbun.screw</groupId> - <artifactId>screw-core</artifactId> <!-- 实现数据库文档 --> - <version>${screw.version}</version> - <exclusions> - <exclusion> - <groupId>org.freemarker</groupId> - <artifactId>freemarker</artifactId> <!-- 移除 Freemarker 依赖,采用 Velocity 作为模板引擎 --> - </exclusion> - <exclusion> - <groupId>com.alibaba</groupId> - <artifactId>fastjson</artifactId> <!-- 最新版screw-core1.0.5依赖fastjson1.2.73存在漏洞,移除。 --> - </exclusion> - </exclusions> - </dependency> +<!-- <dependency>--> +<!-- <groupId>cn.smallbun.screw</groupId>--> +<!-- <artifactId>screw-core</artifactId> <!– 实现数据库文档 –>--> +<!-- <version>${screw.version}</version>--> +<!-- <exclusions>--> +<!-- <exclusion>--> +<!-- <groupId>org.freemarker</groupId>--> +<!-- <artifactId>freemarker</artifactId> <!– 移除 Freemarker 依赖,采用 Velocity 作为模板引擎 –>--> +<!-- </exclusion>--> +<!-- <exclusion>--> +<!-- <groupId>com.alibaba</groupId>--> +<!-- <artifactId>fastjson</artifactId> <!– 最新版screw-core1.0.5依赖fastjson1.2.73存在漏洞,移除。 –>--> +<!-- </exclusion>--> +<!-- </exclusions>--> +<!-- </dependency>--> <dependency> <groupId>com.google.guava</groupId> @@ -641,17 +641,17 @@ <version>${guava.version}</version> </dependency> - <dependency> - <groupId>com.google.inject</groupId> - <artifactId>guice</artifactId> - <version>${guice.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>com.google.inject</groupId>--> +<!-- <artifactId>guice</artifactId>--> +<!-- <version>${guice.version}</version>--> +<!-- </dependency>--> - <dependency> - <groupId>com.alibaba</groupId> - <artifactId>transmittable-thread-local</artifactId> <!-- 解决 ThreadLocal 父子线程的传值问题 --> - <version>${transmittable-thread-local.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>com.alibaba</groupId>--> +<!-- <artifactId>transmittable-thread-local</artifactId> <!– 解决 ThreadLocal 父子线程的传值问题 –>--> +<!-- <version>${transmittable-thread-local.version}</version>--> +<!-- </dependency>--> <dependency> <groupId>commons-net</groupId> @@ -670,84 +670,68 @@ <version>${captcha-plus.version}</version> </dependency> - <dependency> - <groupId>org.lionsoul</groupId> - <artifactId>ip2region</artifactId> - <version>${ip2region.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>org.lionsoul</groupId>--> +<!-- <artifactId>ip2region</artifactId>--> +<!-- <version>${ip2region.version}</version>--> +<!-- </dependency>--> - <dependency> - <groupId>org.jsoup</groupId> - <artifactId>jsoup</artifactId> - <version>${jsoup.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>org.jsoup</groupId>--> +<!-- <artifactId>jsoup</artifactId>--> +<!-- <version>${jsoup.version}</version>--> +<!-- </dependency>--> - <dependency> - <groupId>org.reflections</groupId> - <artifactId>reflections</artifactId> - <version>${reflections.version}</version> - </dependency> +<!-- <dependency>--> +<!-- <groupId>org.reflections</groupId>--> +<!-- <artifactId>reflections</artifactId>--> +<!-- <version>${reflections.version}</version>--> +<!-- </dependency>--> - <!-- 三方云服务相关 --> - <dependency> - <groupId>com.squareup.okio</groupId> - <artifactId>okio</artifactId> - <version>${okio.version}</version> - </dependency> - <dependency> - <groupId>com.squareup.okhttp3</groupId> - <artifactId>okhttp</artifactId> - <version>${okhttp3.version}</version> - </dependency> +<!-- <!– 三方云服务相关 –>--> +<!-- <dependency>--> +<!-- <groupId>com.squareup.okio</groupId>--> +<!-- <artifactId>okio</artifactId>--> +<!-- <version>${okio.version}</version>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>com.squareup.okhttp3</groupId>--> +<!-- <artifactId>okhttp</artifactId>--> +<!-- <version>${okhttp3.version}</version>--> +<!-- </dependency>--> <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>${minio.version}</version> </dependency> - <dependency> - <groupId>com.github.binarywang</groupId> - <artifactId>weixin-java-pay</artifactId> - <version>${weixin-java.version}</version> - </dependency> - <dependency> - <groupId>com.github.binarywang</groupId> - <artifactId>wx-java-mp-spring-boot-starter</artifactId> - <version>${weixin-java.version}</version> - </dependency> - <dependency> - <groupId>com.github.binarywang</groupId> - <artifactId>wx-java-miniapp-spring-boot-starter</artifactId> - <version>${weixin-java.version}</version> - </dependency> - - <!-- SMS SDK begin --> - <dependency> - <groupId>com.aliyun</groupId> - <artifactId>aliyun-java-sdk-core</artifactId> - <version>${aliyun-java-sdk-core.version}</version> - <exclusions> - <exclusion> - <artifactId>opentracing-api</artifactId> - <groupId>io.opentracing</groupId> - </exclusion> - <exclusion> - <artifactId>opentracing-util</artifactId> - <groupId>io.opentracing</groupId> - </exclusion> - </exclusions> - </dependency> - <dependency> - <groupId>com.aliyun</groupId> - <artifactId>aliyun-java-sdk-dysmsapi</artifactId> - <version>${aliyun-java-sdk-dysmsapi.version}</version> - </dependency> - <dependency> - <groupId>com.tencentcloudapi</groupId> - <artifactId>tencentcloud-sdk-java-sms</artifactId> - <version>${tencentcloud-sdk-java.version}</version> - </dependency> - <!-- SMS SDK end --> +<!-- <!– SMS SDK begin –>--> +<!-- <dependency>--> +<!-- <groupId>com.aliyun</groupId>--> +<!-- <artifactId>aliyun-java-sdk-core</artifactId>--> +<!-- <version>${aliyun-java-sdk-core.version}</version>--> +<!-- <exclusions>--> +<!-- <exclusion>--> +<!-- <artifactId>opentracing-api</artifactId>--> +<!-- <groupId>io.opentracing</groupId>--> +<!-- </exclusion>--> +<!-- <exclusion>--> +<!-- <artifactId>opentracing-util</artifactId>--> +<!-- <groupId>io.opentracing</groupId>--> +<!-- </exclusion>--> +<!-- </exclusions>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>com.aliyun</groupId>--> +<!-- <artifactId>aliyun-java-sdk-dysmsapi</artifactId>--> +<!-- <version>${aliyun-java-sdk-dysmsapi.version}</version>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>com.tencentcloudapi</groupId>--> +<!-- <artifactId>tencentcloud-sdk-java-sms</artifactId>--> +<!-- <version>${tencentcloud-sdk-java.version}</version>--> +<!-- </dependency>--> +<!-- <!– SMS SDK end –>--> <dependency> <groupId>com.xingyuv</groupId> @@ -898,14 +882,5 @@ <url>http://172.16.8.100:8090/repository/iailab-snapshots/</url> </snapshotRepository> </distributionManagement> - - <!-- 使用 iailab 私有云 --> -<!-- <repositories>--> -<!-- <repository>--> -<!-- <id>iailab</id>--> -<!-- <name>iailab</name>--> -<!-- <url>http://172.16.8.100:8090/repository/iailab</url>--> -<!-- </repository>--> -<!-- </repositories>--> </project> -- Gitblit v1.9.3