From 325d2fbc3d907dcf3f574d8e1f30d0269ddc1937 Mon Sep 17 00:00:00 2001 From: houzhongjian <houzhongyi@126.com> Date: 星期三, 24 七月 2024 08:28:47 +0800 Subject: [PATCH] 1、请求租户不存在时,记录访问日志、操作日志异常的问题 2、LoginUser 新增过期时间,方便判断 token 过期 3、增加 ConfigApi,支持参数配置的读取 --- iailab-cloud/iailab-gateway/src/main/java/com/iailab/gateway/filter/security/TokenAuthenticationFilter.java | 7 iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java | 26 ++ iailab-cloud/iailab-xxl-job/src/main/resources/templates/jobinfo/jobinfo.index.ftl | 6 iailab-cloud/iailab-xxl-job/src/main/resources/templates/index.ftl | 6 iailab-cloud/iailab-gateway/src/main/resources/application-local.yaml | 7 iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-local.yaml | 14 - iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/handler/GlobalExceptionHandler.java | 40 ++- iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/job/task/DeviceHealthTask.java | 2 iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/config/IailabDataPermissionRpcAutoConfiguration.java | 35 +++ iailab-framework/iailab-common-biz-data-permission/pom.xml | 7 iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rpc/DataPermissionRpcWebFilter.java | 37 ++++ iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/job/task/PointCollectTaskNet10.java | 2 iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/NumberSerializer.java | 37 ++++ iailab-framework/iailab-common-biz-data-permission/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports | 1 iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rpc/DataPermissionRequestInterceptor.java | 27 +++ iailab-cloud/iailab-gateway/src/main/java/com/iailab/gateway/filter/security/LoginUser.java | 5 iailab-cloud/iailab-nacos/src/main/resources/application.yaml | 2 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/logger/ApiErrorLogServiceImpl.java | 11 + iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java | 5 iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/user/AdminUserServiceImpl.java | 15 + iailab-module-bpm/iailab-module-bpm-biz/src/main/resources/application-local.yaml | 7 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/logger/ApiAccessLogServiceImpl.java | 9 iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/LoginUser.java | 5 iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java | 27 +++ iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java | 9 iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/social/SocialClientServiceImpl.java | 11 + iailab-framework/iailab-common-security/src/main/java/com/iailab/framework/security/core/filter/TokenAuthenticationFilter.java | 3 iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/oauth2/OAuth2TokenApiImpl.java | 1 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/api/config/ConfigApiImpl.java | 54 ----- iailab-cloud/iailab-gateway/src/main/resources/application.yaml | 10 iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/config/ConfigApi.java | 13 iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiAccessLogFrameworkServiceImpl.java | 9 iailab-module-system/iailab-module-system-biz/src/main/resources/application-local.yaml | 6 iailab-module-infra/iailab-module-infra-biz/src/main/resources/application.yaml | 6 iailab-cloud/pom.xml | 9 - 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-api/src/main/java/com/iailab/module/system/enums/ErrorCodeConstants.java | 7 iailab-cloud/iailab-xxl-job/src/main/resources/templates/joblog/joblog.index.ftl | 2 iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/config/IailabJacksonAutoConfiguration.java | 6 iailab-module-bpm/iailab-module-bpm-biz/src/main/resources/application.yaml | 9 iailab-module-system/iailab-module-system-biz/src/main/resources/application.yaml | 7 iailab-cloud/iailab-xxl-job/src/main/resources/static/js/jobinfo.index.1.js | 2 42 files changed, 361 insertions(+), 146 deletions(-) diff --git a/iailab-cloud/iailab-gateway/src/main/java/com/iailab/gateway/filter/security/LoginUser.java b/iailab-cloud/iailab-gateway/src/main/java/com/iailab/gateway/filter/security/LoginUser.java index d80ead2..c8d13a8 100644 --- a/iailab-cloud/iailab-gateway/src/main/java/com/iailab/gateway/filter/security/LoginUser.java +++ b/iailab-cloud/iailab-gateway/src/main/java/com/iailab/gateway/filter/security/LoginUser.java @@ -2,6 +2,7 @@ import lombok.Data; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; @@ -35,5 +36,9 @@ * 授权范围 */ private List<String> scopes; + /** + * 过期时间 + */ + private LocalDateTime expiresTime; } diff --git a/iailab-cloud/iailab-gateway/src/main/java/com/iailab/gateway/filter/security/TokenAuthenticationFilter.java b/iailab-cloud/iailab-gateway/src/main/java/com/iailab/gateway/filter/security/TokenAuthenticationFilter.java index 7fe2959..499145f 100644 --- a/iailab-cloud/iailab-gateway/src/main/java/com/iailab/gateway/filter/security/TokenAuthenticationFilter.java +++ b/iailab-cloud/iailab-gateway/src/main/java/com/iailab/gateway/filter/security/TokenAuthenticationFilter.java @@ -3,6 +3,7 @@ import cn.hutool.core.util.StrUtil; import com.iailab.framework.common.core.KeyValue; import com.iailab.framework.common.pojo.CommonResult; +import com.iailab.framework.common.util.date.LocalDateTimeUtils; import com.iailab.framework.common.util.json.JsonUtils; import com.iailab.gateway.util.SecurityFrameworkUtils; import com.iailab.gateway.util.WebFrameworkUtils; @@ -94,7 +95,8 @@ // 重要说明:defaultIfEmpty 作用,保证 Mono.empty() 情况,可以继续执行 `flatMap 的 chain.filter(exchange)` 逻辑,避免返回给前端空的 Response!! return getLoginUser(exchange, token).defaultIfEmpty(LOGIN_USER_EMPTY).flatMap(user -> { // 1. 无用户,直接 filter 继续请求 - if (user == LOGIN_USER_EMPTY) { + if (user == LOGIN_USER_EMPTY || // 下面 expiresTime 的判断,为了解决 token 实际已经过期的情况 + user.getExpiresTime() == null || LocalDateTimeUtils.afterNow(user.getExpiresTime())) { return chain.filter(exchange); } @@ -153,7 +155,8 @@ OAuth2AccessTokenCheckRespDTO tokenInfo = result.getData(); return new LoginUser().setId(tokenInfo.getUserId()).setUserType(tokenInfo.getUserType()) .setInfo(tokenInfo.getUserInfo()) // 额外的用户信息 - .setTenantId(tokenInfo.getTenantId()).setScopes(tokenInfo.getScopes()); + .setTenantId(tokenInfo.getTenantId()).setScopes(tokenInfo.getScopes()) + .setExpiresTime(tokenInfo.getExpiresTime()); } @Override diff --git a/iailab-cloud/iailab-gateway/src/main/resources/application-local.yaml b/iailab-cloud/iailab-gateway/src/main/resources/application-local.yaml index c9c6e4a..9d47283 100644 --- a/iailab-cloud/iailab-gateway/src/main/resources/application-local.yaml +++ b/iailab-cloud/iailab-gateway/src/main/resources/application-local.yaml @@ -10,4 +10,9 @@ group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP config: # 【注册中心】配置项 namespace: a7112341-c9e2-4177-bc5b-0d2e8cf0b3bb # 命名空间。这里使用 dev 开发环境 - group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP \ No newline at end of file + group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + +# 日志文件配置 +logging: + level: + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 diff --git a/iailab-cloud/iailab-gateway/src/main/resources/application.yaml b/iailab-cloud/iailab-gateway/src/main/resources/application.yaml index 28a5cb6..846d4de 100644 --- a/iailab-cloud/iailab-gateway/src/main/resources/application.yaml +++ b/iailab-cloud/iailab-gateway/src/main/resources/application.yaml @@ -77,12 +77,14 @@ - Path=/admin-api/statistics/** filters: - RewritePath=/admin-api/statistics/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs - - id: xxl-job-server - uri: grayLb://iailab-job + ## xxl-job + - id: xxl-job-admin + uri: grayLb://xxl-job-server predicates: - - Path=/iailab-job/** + - Path=/xxl-job-admin/** + ## monitor - id: monitor-server - uri: grayLb://iailab-monitor + uri: grayLb://monitor-server predicates: - Path=/iailab-monitor/** ## data-server 服务 diff --git a/iailab-cloud/iailab-nacos/src/main/resources/application.yaml b/iailab-cloud/iailab-nacos/src/main/resources/application.yaml index 89c04a6..91b9139 100644 --- a/iailab-cloud/iailab-nacos/src/main/resources/application.yaml +++ b/iailab-cloud/iailab-nacos/src/main/resources/application.yaml @@ -1,7 +1,7 @@ db: num: 1 url: - '0': jdbc:mysql://172.16.8.100:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true + '0': jdbc:mysql://localhost:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true password: '0': 123456 user: diff --git a/iailab-cloud/iailab-xxl-job/src/main/resources/static/js/jobinfo.index.1.js b/iailab-cloud/iailab-xxl-job/src/main/resources/static/js/jobinfo.index.1.js index b479e97..f22bbaa 100644 --- a/iailab-cloud/iailab-xxl-job/src/main/resources/static/js/jobinfo.index.1.js +++ b/iailab-cloud/iailab-xxl-job/src/main/resources/static/js/jobinfo.index.1.js @@ -10,7 +10,7 @@ type:"post", data : function ( d ) { var obj = {}; - obj.jobGroup = $('#jobGroup').val(); + obj.jobGroup = Number($('#jobGroup').val()); obj.triggerStatus = $('#triggerStatus').val(); obj.jobDesc = $('#jobDesc').val(); obj.executorHandler = $('#executorHandler').val(); diff --git a/iailab-cloud/iailab-xxl-job/src/main/resources/templates/index.ftl b/iailab-cloud/iailab-xxl-job/src/main/resources/templates/index.ftl index d642f4e..a836942 100644 --- a/iailab-cloud/iailab-xxl-job/src/main/resources/templates/index.ftl +++ b/iailab-cloud/iailab-xxl-job/src/main/resources/templates/index.ftl @@ -41,7 +41,7 @@ <div class="info-box-content"> <span class="info-box-text">${I18n.job_dashboard_job_num}</span> - <span class="info-box-number">${jobInfoCount}</span> + <span class="info-box-number">${jobInfoCount?number?string(',##0')}</span> <div class="progress"> <div class="progress-bar" style="width: 100%"></div> @@ -58,7 +58,7 @@ <div class="info-box-content"> <span class="info-box-text">${I18n.job_dashboard_trigger_num}</span> - <span class="info-box-number">${jobLogCount}</span> + <span class="info-box-number">${jobLogCount?number?string(',##0')}</span> <div class="progress"> <div class="progress-bar" style="width: 100%" ></div> @@ -80,7 +80,7 @@ <div class="info-box-content"> <span class="info-box-text">${I18n.job_dashboard_jobgroup_num}</span> - <span class="info-box-number">${executorCount}</span> + <span class="info-box-number">${executorCount?number?string(',##0')}</span> <div class="progress"> <div class="progress-bar" style="width: 100%"></div> diff --git a/iailab-cloud/iailab-xxl-job/src/main/resources/templates/jobinfo/jobinfo.index.ftl b/iailab-cloud/iailab-xxl-job/src/main/resources/templates/jobinfo/jobinfo.index.ftl index 3a5d7d8..570ba93 100644 --- a/iailab-cloud/iailab-xxl-job/src/main/resources/templates/jobinfo/jobinfo.index.ftl +++ b/iailab-cloud/iailab-xxl-job/src/main/resources/templates/jobinfo/jobinfo.index.ftl @@ -30,7 +30,7 @@ <span class="input-group-addon">${I18n.jobinfo_field_jobgroup}</span> <select class="form-control" id="jobGroup" > <#list JobGroupList as group> - <option value="${group.id}" <#if jobGroup==group.id>selected</#if> >${group.title}</option> + <option value="${group.id?number?string(',##0')}" <#if jobGroup==group.id>selected</#if> >${group.title}</option> </#list> </select> </div> @@ -121,7 +121,7 @@ <div class="col-sm-4"> <select class="form-control" name="jobGroup" > <#list JobGroupList as group> - <option value="${group.id}" <#if jobGroup==group.id>selected</#if> >${group.title}</option> + <option value="${group.id?number?string(',##0')}" <#if jobGroup==group.id>selected</#if> >${group.title}</option> </#list> </select> </div> @@ -366,7 +366,7 @@ <div class="col-sm-4"> <select class="form-control" name="jobGroup" > <#list JobGroupList as group> - <option value="${group.id}" >${group.title}</option> + <option value="${group.id?number?string(',##0')}" >${group.title}</option> </#list> </select> </div> diff --git a/iailab-cloud/iailab-xxl-job/src/main/resources/templates/joblog/joblog.index.ftl b/iailab-cloud/iailab-xxl-job/src/main/resources/templates/joblog/joblog.index.ftl index a2e983d..aea954e 100644 --- a/iailab-cloud/iailab-xxl-job/src/main/resources/templates/joblog/joblog.index.ftl +++ b/iailab-cloud/iailab-xxl-job/src/main/resources/templates/joblog/joblog.index.ftl @@ -34,7 +34,7 @@ <option value="0" >${I18n.system_all}</option> <#-- 仅管理员支持查询全部;普通用户仅支持查询有权限的 jobGroup --> </#if> <#list JobGroupList as group> - <option value="${group.id}" >${group.title}</option> + <option value="${group.id?number?string(',##0')}" >${group.title}</option> </#list> </select> </div> diff --git a/iailab-cloud/pom.xml b/iailab-cloud/pom.xml index d421ea6..5a9a261 100644 --- a/iailab-cloud/pom.xml +++ b/iailab-cloud/pom.xml @@ -11,19 +11,10 @@ <modules> <module>iailab-gateway</module> <module>iailab-xxl-job</module> - <module>iailab-monitor</module> <module>iailab-nacos</module> </modules> <artifactId>iailab-cloud</artifactId> <packaging>pom</packaging> <name>${project.artifactId}</name> - - - <properties> - <maven.compiler.source>8</maven.compiler.source> - <maven.compiler.target>8</maven.compiler.target> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - </properties> - </project> \ No newline at end of file diff --git a/iailab-framework/iailab-common-biz-data-permission/pom.xml b/iailab-framework/iailab-common-biz-data-permission/pom.xml index 9a9ae23..09b553f 100644 --- a/iailab-framework/iailab-common-biz-data-permission/pom.xml +++ b/iailab-framework/iailab-common-biz-data-permission/pom.xml @@ -34,6 +34,13 @@ <artifactId>iailab-common-mybatis</artifactId> </dependency> + <!-- RPC 远程调用相关 --> + <dependency> + <groupId>com.iailab</groupId> + <artifactId>iailab-common-rpc</artifactId> + <optional>true</optional> + </dependency> + <!-- 业务组件 --> <dependency> <groupId>com.iailab</groupId> 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 new file mode 100644 index 0000000..53dde0a --- /dev/null +++ b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/config/IailabDataPermissionRpcAutoConfiguration.java @@ -0,0 +1,35 @@ +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 芋道源码 + */ +@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/core/rpc/DataPermissionRequestInterceptor.java b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rpc/DataPermissionRequestInterceptor.java new file mode 100644 index 0000000..da72be7 --- /dev/null +++ b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rpc/DataPermissionRequestInterceptor.java @@ -0,0 +1,27 @@ +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 芋道源码 + */ +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 new file mode 100644 index 0000000..a7fb202 --- /dev/null +++ b/iailab-framework/iailab-common-biz-data-permission/src/main/java/com/iailab/framework/datapermission/core/rpc/DataPermissionRpcWebFilter.java @@ -0,0 +1,37 @@ +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 芋道源码 + */ +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/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 index b880d8e..5499998 100644 --- 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 @@ -1,2 +1,3 @@ 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-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 index e1eeb07..fe0cce1 100644 --- 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 @@ -5,6 +5,7 @@ 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; @@ -42,6 +43,10 @@ * 授权范围 */ private List<String> scopes; + /** + * 过期时间 + */ + private LocalDateTime expiresTime; // ========== 上下文 ========== /** 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 index f094e7d..161e12f 100644 --- 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 @@ -97,7 +97,8 @@ // 构建登录用户 return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType()) .setInfo(accessToken.getUserInfo()) // 额外的用户信息 - .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes()); + .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes()) + .setExpiresTime(accessToken.getExpiresTime()); } catch (ServiceException serviceException) { // 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可 return null; 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 index 84c5c3d..82501a5 100644 --- 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 @@ -3,6 +3,7 @@ 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; /** @@ -13,6 +14,7 @@ * @author iailab */ @RequiredArgsConstructor +@Slf4j public class ApiAccessLogFrameworkServiceImpl implements ApiAccessLogFrameworkService { private final ApiAccessLogApi apiAccessLogApi; @@ -20,7 +22,12 @@ @Override @Async public void createApiAccessLog(ApiAccessLogCreateReqDTO reqDTO) { - apiAccessLogApi.createApiAccessLog(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/ApiErrorLogFrameworkServiceImpl.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java index 3fc2814..87055ce 100644 --- 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 @@ -3,6 +3,7 @@ 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; /** @@ -13,6 +14,7 @@ * @author iailab */ @RequiredArgsConstructor +@Slf4j public class ApiErrorLogFrameworkServiceImpl implements ApiErrorLogFrameworkService { private final ApiErrorLogApi apiErrorLogApi; @@ -20,7 +22,12 @@ @Override @Async public void createApiErrorLog(ApiErrorLogCreateReqDTO reqDTO) { - apiErrorLogApi.createApiErrorLog(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/jackson/config/IailabJacksonAutoConfiguration.java b/iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/jackson/config/IailabJacksonAutoConfiguration.java index 68f6aae..713525c 100644 --- 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 @@ -2,15 +2,15 @@ import cn.hutool.core.collection.CollUtil; import com.iailab.framework.common.util.json.JsonUtils; -import com.iailab.framework.jackson.core.databind.NumberSerializer; -import com.iailab.framework.jackson.core.databind.TimestampLocalDateTimeDeserializer; -import com.iailab.framework.jackson.core.databind.TimestampLocalDateTimeSerializer; 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; 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 index 688fbd2..958589c 100644 --- 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 @@ -205,9 +205,11 @@ */ @ExceptionHandler(value = ServiceException.class) public CommonResult<?> serviceExceptionHandler(ServiceException ex) { + // 不包含的时候,才进行打印,避免 ex 堆栈过多 if (!IGNORE_ERROR_MESSAGES.contains(ex.getMessage())) { - // 不包含的时候,才进行打印,避免 ex 堆栈过多 - log.info("[serviceExceptionHandler]", ex); + // 即使打印,也只打印第一层 StackTraceElement,并且使用 warn 在控制台输出,更容易看到 + StackTraceElement[] stackTrace = ex.getStackTrace(); + log.warn("[serviceExceptionHandler]\n\t{}", stackTrace[0]); } return CommonResult.error(ex.getCode(), ex.getMessage()); } @@ -287,45 +289,51 @@ } // 1. 数据报表 if (message.contains("report_")) { - log.error("[报表模块 iailab-module-report - 表结构未导入][参考 https://doc.iocoder.cn/report/ 开启]"); + log.error("[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]"); return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[报表模块 iailab-module-report - 表结构未导入][参考 https://doc.iocoder.cn/report/ 开启]"); + "[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]"); } // 2. 工作流 if (message.contains("bpm_")) { - log.error("[工作流模块 iailab-module-bpm - 表结构未导入][参考 https://doc.iocoder.cn/bpm/ 开启]"); + log.error("[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]"); return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[工作流模块 iailab-module-bpm - 表结构未导入][参考 https://doc.iocoder.cn/bpm/ 开启]"); + "[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]"); } // 3. 微信公众号 if (message.contains("mp_")) { - log.error("[微信公众号 iailab-module-mp - 表结构未导入][参考 https://doc.iocoder.cn/mp/build/ 开启]"); + log.error("[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]"); return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[微信公众号 iailab-module-mp - 表结构未导入][参考 https://doc.iocoder.cn/mp/build/ 开启]"); + "[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]"); } // 4. 商城系统 if (StrUtil.containsAny(message, "product_", "promotion_", "trade_")) { - log.error("[商城系统 iailab-module-mall - 已禁用][参考 https://doc.iocoder.cn/mall/build/ 开启]"); + log.error("[商城系统 yudao-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]"); return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[商城系统 iailab-module-mall - 已禁用][参考 https://doc.iocoder.cn/mall/build/ 开启]"); + "[商城系统 yudao-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]"); } // 5. ERP 系统 if (message.contains("erp_")) { - log.error("[ERP 系统 iailab-module-erp - 表结构未导入][参考 https://doc.iocoder.cn/erp/build/ 开启]"); + log.error("[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]"); return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[ERP 系统 iailab-module-erp - 表结构未导入][参考 https://doc.iocoder.cn/erp/build/ 开启]"); + "[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]"); } // 6. CRM 系统 if (message.contains("crm_")) { - log.error("[CRM 系统 iailab-module-crm - 表结构未导入][参考 https://doc.iocoder.cn/crm/build/ 开启]"); + log.error("[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]"); return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[CRM 系统 iailab-module-crm - 表结构未导入][参考 https://doc.iocoder.cn/crm/build/ 开启]"); + "[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]"); } // 7. 支付平台 if (message.contains("pay_")) { - log.error("[支付模块 iailab-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]"); + log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]"); return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[支付模块 iailab-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]"); + "[支付模块 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/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 new file mode 100644 index 0000000..19d0591 --- /dev/null +++ b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/NumberSerializer.java @@ -0,0 +1,37 @@ +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 new file mode 100644 index 0000000..d024c92 --- /dev/null +++ b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java @@ -0,0 +1,27 @@ +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 new file mode 100644 index 0000000..6d43528 --- /dev/null +++ b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java @@ -0,0 +1,26 @@ +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-module-bpm/iailab-module-bpm-biz/src/main/resources/application-local.yaml b/iailab-module-bpm/iailab-module-bpm-biz/src/main/resources/application-local.yaml index ea1d3f2..829d98f 100644 --- a/iailab-module-bpm/iailab-module-bpm-biz/src/main/resources/application-local.yaml +++ b/iailab-module-bpm/iailab-module-bpm-biz/src/main/resources/application-local.yaml @@ -113,15 +113,8 @@ iailab: env: # 多环境的配置项 tag: ${HOSTNAME} - captcha: - enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试 security: mock-enable: true - xss: - enable: false - exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 - - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 - - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 access-log: # 访问日志的配置项 enable: false demo: false # 关闭演示模式 diff --git a/iailab-module-bpm/iailab-module-bpm-biz/src/main/resources/application.yaml b/iailab-module-bpm/iailab-module-bpm-biz/src/main/resources/application.yaml index 47c8830..93f0506 100644 --- a/iailab-module-bpm/iailab-module-bpm-biz/src/main/resources/application.yaml +++ b/iailab-module-bpm/iailab-module-bpm-biz/src/main/resources/application.yaml @@ -108,15 +108,14 @@ web: admin-ui: url: http://dashboard.iailab.iocoder.cn # Admin 管理后台 UI 的地址 + xss: + enable: false + exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系 + - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 swagger: title: 管理后台 description: 提供管理员管理的所有功能 version: ${iailab.info.version} - base-package: ${iailab.info.base-package} - captcha: - timeout: 5m - width: 160 - height: 60 tenant: # 多租户相关配置项 enable: true diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/job/task/PointCollectTaskNet10.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/job/task/PointCollectTaskNet10.java index df5fa11..ee24b22 100644 --- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/job/task/PointCollectTaskNet10.java +++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/job/task/PointCollectTaskNet10.java @@ -1,6 +1,7 @@ package com.iailab.module.data.job.task; import com.iailab.module.data.point.collection.PointCollector; +import com.xxl.job.core.handler.annotation.XxlJob; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Resource; @@ -27,6 +28,7 @@ private PointCollector pointCollector; @Override + @XxlJob("pointCollectTaskNet10") public void run(String params){ // 0/10 * * * * ? logger.debug("PointCollectTaskNet10定时任务正在执行,参数为:{}", params); diff --git a/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/config/ConfigApi.java b/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/config/ConfigApi.java index 2d41a26..d1f4dd2 100644 --- a/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/config/ConfigApi.java +++ b/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/config/ConfigApi.java @@ -1,17 +1,20 @@ package com.iailab.module.infra.api.config; +import com.iailab.framework.common.pojo.CommonResult; import com.iailab.module.infra.enums.ApiConstants; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.*; -@FeignClient(name = ApiConstants.NAME) -@Tag(name = "配置文件") +@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = +@Tag(name = "RPC 服务 - 参数配置") public interface ConfigApi { - @Operation(summary = "Feign接口-查询配置参数") - @GetMapping("/api/feign/infra-config/{configCode}") - String queryConfigByCode(@PathVariable("configCode") String configCode); + String PREFIX = ApiConstants.PREFIX + "/config"; + + @GetMapping(PREFIX + "/get-value-by-key") + @Operation(summary = "根据参数键查询参数值") + CommonResult<String> getConfigValueByKey(@RequestParam("key") String key); } diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/api/config/ConfigApiImpl.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/api/config/ConfigApiImpl.java index 521559f..9c1a38d 100644 --- a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/api/config/ConfigApiImpl.java +++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/api/config/ConfigApiImpl.java @@ -1,18 +1,12 @@ package com.iailab.module.infra.api.config; import com.iailab.framework.common.pojo.CommonResult; -import com.iailab.module.infra.api.file.FileApi; -import com.iailab.module.infra.api.file.dto.FileCreateReqDTO; +import com.iailab.module.infra.dal.dataobject.config.ConfigDO; import com.iailab.module.infra.service.config.ConfigService; -import com.iailab.module.infra.service.file.FileService; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; - -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; import static com.iailab.framework.common.pojo.CommonResult.success; @@ -23,49 +17,11 @@ @Resource private ConfigService configService; -// /** -// * 查月初或者年初(根据code) -// * -// * @return -// */ -// @Override -// public String queryConfigByCode(String configCode, String type){ -// String result = ""; -// String value = configService.getValue(configCode); -// Calendar calendar = Calendar.getInstance(); -// calendar.setTime(new Date()); -// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); -// if("month".equals(type)){ -// int nowDay = calendar.get(Calendar.DAY_OF_MONTH); -// if(nowDay < Integer.parseInt(value)){ -// calendar.add(Calendar.MONTH, -1); -// } -// calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(value)); -// } else { -// int month = Integer.parseInt(value.substring(0,value.indexOf("-"))); -// int day = Integer.parseInt(value.substring(value.indexOf("-") + 1)); -// int nowMonth = calendar.get(Calendar.MONTH) + 1; -// int nowDay = calendar.get(Calendar.DAY_OF_MONTH); -// -// if(nowMonth == month) { -// if(nowDay < day){ -// calendar.add(Calendar.YEAR, -1); -// } -// } else if(nowMonth < month) { -// calendar.add(Calendar.YEAR, -1); -// } -// calendar.set(Calendar.MONTH, month - 1); -// calendar.set(Calendar.DAY_OF_MONTH, day); -// } -// -// result = sdf.format(calendar.getTime()); -// -// return result; -// } - @Override - public String queryConfigByCode(String configCode) { - return configService.getValue(configCode); + public CommonResult<String> getConfigValueByKey(String key) { + ConfigDO config = configService.getConfigByKey(key); + return success(config != null ? config.getValue() : null); } + } diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/logger/ApiAccessLogServiceImpl.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/logger/ApiAccessLogServiceImpl.java index 513c84d..6fd58ff 100644 --- a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/logger/ApiAccessLogServiceImpl.java +++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/logger/ApiAccessLogServiceImpl.java @@ -3,6 +3,8 @@ import cn.hutool.core.util.StrUtil; import com.iailab.framework.common.pojo.PageResult; import com.iailab.framework.common.util.object.BeanUtils; +import com.iailab.framework.tenant.core.context.TenantContextHolder; +import com.iailab.framework.tenant.core.util.TenantUtils; import com.iailab.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; import com.iailab.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO; import com.iailab.module.infra.dal.dataobject.logger.ApiAccessLogDO; @@ -35,7 +37,12 @@ ApiAccessLogDO apiAccessLog = BeanUtils.toBean(createDTO, ApiAccessLogDO.class); apiAccessLog.setRequestParams(StrUtil.maxLength(apiAccessLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH)); apiAccessLog.setResultMsg(StrUtil.maxLength(apiAccessLog.getResultMsg(), RESULT_MSG_MAX_LENGTH)); - apiAccessLogMapper.insert(apiAccessLog); + if (TenantContextHolder.getTenantId() != null) { + apiAccessLogMapper.insert(apiAccessLog); + } else { + // 极端情况下,上下文中没有租户时,此时忽略租户上下文,避免插入失败! + TenantUtils.executeIgnore(() -> apiAccessLogMapper.insert(apiAccessLog)); + } } @Override diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/logger/ApiErrorLogServiceImpl.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/logger/ApiErrorLogServiceImpl.java index 807d142..5a28ef4 100644 --- a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/logger/ApiErrorLogServiceImpl.java +++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/logger/ApiErrorLogServiceImpl.java @@ -3,6 +3,8 @@ import cn.hutool.core.util.StrUtil; import com.iailab.framework.common.pojo.PageResult; import com.iailab.framework.common.util.object.BeanUtils; +import com.iailab.framework.tenant.core.context.TenantContextHolder; +import com.iailab.framework.tenant.core.util.TenantUtils; import com.iailab.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; import com.iailab.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO; import com.iailab.module.infra.dal.dataobject.logger.ApiErrorLogDO; @@ -25,9 +27,9 @@ * * @author iailab */ -@Slf4j @Service @Validated +@Slf4j public class ApiErrorLogServiceImpl implements ApiErrorLogService { @Resource @@ -38,7 +40,12 @@ ApiErrorLogDO apiErrorLog = BeanUtils.toBean(createDTO, ApiErrorLogDO.class) .setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus()); apiErrorLog.setRequestParams(StrUtil.maxLength(apiErrorLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH)); - apiErrorLogMapper.insert(apiErrorLog); + if (TenantContextHolder.getTenantId() != null) { + apiErrorLogMapper.insert(apiErrorLog); + } else { + // 极端情况下,上下文中没有租户时,此时忽略租户上下文,避免插入失败! + TenantUtils.executeIgnore(() -> apiErrorLogMapper.insert(apiErrorLog)); + } } @Override diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-local.yaml b/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-local.yaml index 861fa2e..cf43456 100644 --- a/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-local.yaml +++ b/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-local.yaml @@ -22,10 +22,10 @@ autoconfigure: exclude: - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 - - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 - - de.codecentric.boot.admin.server.cloud.config.AdminServerDiscoveryAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 - - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置 - - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 +# - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 +# - de.codecentric.boot.admin.server.cloud.config.AdminServerDiscoveryAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 +# - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置 +# - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: @@ -154,11 +154,5 @@ tag: ${HOSTNAME} security: mock-enable: true - xss: - enable: false - exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 - - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 - - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 access-log: # 访问日志的配置项 enable: false - demo: false # 关闭演示模式 diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application.yaml b/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application.yaml index a1b0c23..94fd5ec 100644 --- a/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application.yaml +++ b/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application.yaml @@ -138,6 +138,11 @@ web: admin-ui: url: http://dashboard.iailab.iocoder.cn # Admin 管理后台 UI 的地址 + xss: + enable: false + exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 + - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 + - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 websocket: enable: true # websocket的开关 path: /infra/ws # 路径 @@ -155,7 +160,6 @@ title: 管理后台 description: 提供管理员管理的所有功能 version: ${iailab.info.version} - base-package: ${iailab.info.base-package} codegen: base-package: com.iailab db-schemas: ${spring.datasource.dynamic.datasource.master.name} diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/job/task/DeviceHealthTask.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/job/task/DeviceHealthTask.java index 48d85ca..dd8e8b0 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/job/task/DeviceHealthTask.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/job/task/DeviceHealthTask.java @@ -15,6 +15,7 @@ import com.iailab.module.model.sample.constructor.SampleInfoConstructor; import com.iailab.module.model.sample.dto.ColumnItem; import com.iailab.module.model.sample.entity.DataEntity; +import com.xxl.job.core.handler.annotation.XxlJob; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -97,6 +98,7 @@ } @Override + @XxlJob("deviceHealthTask") public void run(String params) { Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.MILLISECOND, 0); diff --git a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java index 582dcbe..c6ddab6 100644 --- a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java +++ b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/api/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java @@ -4,6 +4,7 @@ import lombok.Data; import java.io.Serializable; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; @@ -26,4 +27,8 @@ @Schema(description = "授权范围的数组", example = "user_info") private List<String> scopes; + @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime expiresTime; + + } diff --git a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/ErrorCodeConstants.java b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/ErrorCodeConstants.java index 5e9af94..aa3103d 100644 --- a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/ErrorCodeConstants.java +++ b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/ErrorCodeConstants.java @@ -14,7 +14,6 @@ ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_002_000_001, "登录失败,账号被禁用"); ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_004, "验证码不正确,原因:{}"); ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_002_000_005, "未绑定账号,需要进行绑定"); - ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1_002_000_006, "Token 已经过期"); ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1_002_000_007, "手机号不存在"); // ========== 菜单模块 1-002-001-000 ========== @@ -42,6 +41,7 @@ ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1_002_003_005, "用户密码校验失败"); ErrorCode USER_IS_DISABLE = new ErrorCode(1_002_003_006, "名字为【{}】的用户已被禁用"); ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})!"); + ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空"); // ========== 部门模块 1-002-004-000 ========== ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门"); @@ -49,7 +49,6 @@ ErrorCode DEPT_NOT_FOUND = new ErrorCode(1_002_004_002, "当前部门不存在"); ErrorCode DEPT_EXITS_CHILDREN = new ErrorCode(1_002_004_003, "存在子部门,无法删除"); ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1_002_004_004, "不能设置自己为父部门"); - ErrorCode DEPT_EXISTS_USER = new ErrorCode(1_002_004_005, "部门中存在员工,无法删除"); ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1_002_004_006, "部门({})不处于开启状态,不允许选择"); ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1_002_004_007, "不能设置自己的子部门为父部门"); @@ -96,11 +95,8 @@ ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1_002_014_000, "验证码不存在"); ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1_002_014_001, "验证码已过期"); ErrorCode SMS_CODE_USED = new ErrorCode(1_002_014_002, "验证码已使用"); - ErrorCode SMS_CODE_NOT_CORRECT = new ErrorCode(1_002_014_003, "验证码不正确"); ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1_002_014_004, "超过每日短信发送数量"); ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1_002_014_005, "短信发送过于频繁"); - ErrorCode SMS_CODE_IS_EXISTS = new ErrorCode(1_002_014_006, "手机号已被使用"); - ErrorCode SMS_CODE_IS_UNUSED = new ErrorCode(1_002_014_007, "验证码未被使用"); // ========== 租户信息 1-002-015-000 ========== ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1_002_015_000, "租户不存在"); @@ -136,7 +132,6 @@ ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1_002_021_000, "client_id 不匹配"); ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1_002_021_001, "redirect_uri 不匹配"); ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1_002_021_002, "state 不匹配"); - ErrorCode OAUTH2_GRANT_CODE_NOT_EXISTS = new ErrorCode(1_002_021_003, "code 不存在"); // ========== OAuth2 授权 1-002-022-000 ========= ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1_002_022_000, "code 不存在"); diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/oauth2/OAuth2TokenApiImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/oauth2/OAuth2TokenApiImpl.java index 0d3d656..de16ef1 100644 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/oauth2/OAuth2TokenApiImpl.java +++ b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/api/oauth2/OAuth2TokenApiImpl.java @@ -23,7 +23,6 @@ private OAuth2TokenService oauth2TokenService; @Override - @Operation(description = "创建访问令牌") public CommonResult<OAuth2AccessTokenRespDTO> createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) { OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken( reqDTO.getUserId(), reqDTO.getUserType(), reqDTO.getClientId(), reqDTO.getScopes()); 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 38c67ad..a534dc8 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 @@ -1,11 +1,12 @@ package com.iailab.module.system.framework.rpc.config; +import com.iailab.module.infra.api.config.ConfigApi; import com.iailab.module.infra.api.file.FileApi; import com.iailab.module.infra.api.websocket.WebSocketSenderApi; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) -@EnableFeignClients(clients = {FileApi.class, WebSocketSenderApi.class}) +@EnableFeignClients(clients = {FileApi.class, WebSocketSenderApi.class, ConfigApi.class}) public class RpcConfiguration { } 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 index 6873b1c..c0a400f 100644 --- 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 @@ -38,6 +38,7 @@ 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; @@ -59,6 +60,16 @@ @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; diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/user/AdminUserServiceImpl.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/user/AdminUserServiceImpl.java index f954f50..0d1afc3 100644 --- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/user/AdminUserServiceImpl.java +++ b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/service/user/AdminUserServiceImpl.java @@ -10,6 +10,7 @@ import com.iailab.framework.common.util.collection.CollectionUtils; import com.iailab.framework.common.util.object.BeanUtils; import com.iailab.framework.datapermission.core.util.DataPermissionUtils; +import com.iailab.module.infra.api.config.ConfigApi; import com.iailab.module.infra.api.file.FileApi; import com.iailab.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO; import com.iailab.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; @@ -57,8 +58,7 @@ @Slf4j public class AdminUserServiceImpl implements AdminUserService { - @Value("${sys.user.init-password:iailabyuanma}") - private String userInitPassword; + static final String USER_INIT_PASSWORD_KEY = "system.user.init-password"; @Resource private AdminUserMapper userMapper; @@ -80,6 +80,9 @@ @Resource private FileApi fileApi; + + @Resource + private ConfigApi configApi; @Override @Transactional(rollbackFor = Exception.class) @@ -428,9 +431,17 @@ @Override @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入 public UserImportRespVO importUserList(List<UserImportExcelVO> importUsers, boolean isUpdateSupport) { + // 1.1 参数校验 if (CollUtil.isEmpty(importUsers)) { throw exception(USER_IMPORT_LIST_IS_EMPTY); } + // 1.2 初始化密码不能为空 + String initPassword = configApi.getConfigValueByKey(USER_INIT_PASSWORD_KEY).getCheckedData(); + if (StrUtil.isEmpty(initPassword)) { + throw exception(USER_IMPORT_INIT_PASSWORD); + } + + // 2. 遍历,逐个创建 or 更新 UserImportRespVO respVO = UserImportRespVO.builder().createUsernames(new ArrayList<>()) .updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build(); importUsers.forEach(importUser -> { diff --git a/iailab-module-system/iailab-module-system-biz/src/main/resources/application-local.yaml b/iailab-module-system/iailab-module-system-biz/src/main/resources/application-local.yaml index d24f9d6..3761309 100644 --- a/iailab-module-system/iailab-module-system-biz/src/main/resources/application-local.yaml +++ b/iailab-module-system/iailab-module-system-biz/src/main/resources/application-local.yaml @@ -173,14 +173,8 @@ enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试 security: mock-enable: true - xss: - enable: false - exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 - - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 - - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 access-log: # 访问日志的配置项 enable: false - demo: false # 关闭演示模式 justauth: enabled: true 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 deb9212..3ccc005 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 @@ -158,13 +158,14 @@ web: admin-ui: url: http://dashboard.iailab.iocoder.cn # Admin 管理后台 UI 的地址 + xss: + enable: false + exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系 + - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 swagger: title: 管理后台 description: 提供管理员管理的所有功能 version: ${iailab.info.version} - base-package: ${iailab.info.base-package} - captcha: - enable: false # 验证码的开关,默认为 true; tenant: # 多租户相关配置项 enable: true ignore-urls: -- Gitblit v1.9.3