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