From ca6ad5acfb389b852211355c4a56c71769a018c9 Mon Sep 17 00:00:00 2001
From: houzhongjian <houzhongyi@126.com>
Date: 星期四, 05 六月 2025 17:55:36 +0800
Subject: [PATCH] v2.0版本初始化

---
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiModelDO.java                                        |   79 +
 iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumValidator.java                                    |   15 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/mysql/monitordisk/MonitorDiskMapper.java              |   46 
 iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/config/IailabMybatisAutoConfiguration.java                |    1 
 iailab-system/log/system-server.log                                                                                                         |  188 ++
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskSaveReqVO.java |   41 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/MonitorMemController.java     |  126 +
 iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-dev.yaml                                                         |    4 
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatMessageSendRespDTO.java                         |   34 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/dataobject/monitordisk/MonitorDiskDO.java             |   72 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemRespVO.java      |   81 +
 iailab-module-ai/pom.xml                                                                                                                    |    4 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskPageReqVO.java |   47 
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatMessageSendReqDTO.java                          |   23 
 工业互联网平台鉴权功能.md                                                                                                                              |  269 +++
 iailab-module-ai/.idea/misc.xml                                                                                                             |   14 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskRespVO.java    |   57 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/dataobject/monitormem/MonitorMemDO.java               |  103 +
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatConversationRespDTO.java                        |   58 
 iailab-module-data/iailab-module-data-biz/src/main/resources/application-prod.yaml                                                          |  127 +
 .xcodemap/config/xcodemap-class-filter.yaml                                                                                                 |   26 
 iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/logger/ApiAccessLogApi.java                           |   10 
 iailab-module-ai/.idea/.gitignore                                                                                                           |    8 
 iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/job/monitor/MonitorMemJob.java                         |   48 
 iailab-module-ai/.idea/jarRepositories.xml                                                                                                  |   30 
 iailab-module-ai/iailab-module-ai-biz/pom.xml                                                                                               |    8 
 iailab-module-model/iailab-module-model-biz/src/main/resources/application-prod.yaml                                                        |  106 +
 iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/template/XMLParserUtils.java                                  |  296 +++
 iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/core/ArrayValuable.java                                            |   15 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitormem/MonitorMemService.java                 |   83 +
 iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/monitor/dto/MonitorDiskDTO.java                       |   55 
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/AiChatMessageApi.java                                     |   22 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/chat/AiChatConversionApiImpl.java                              |   34 
 iailab-module-ai/iailab-module-ai-biz/src/main/resources/application.yaml                                                                   |    5 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitormem/MonitorMemServiceImpl.java             |  108 +
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemPageReqVO.java   |   59 
 iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/monitor/dto/MonitorMemDTO.java                        |   79 +
 iailab-module-infra/iailab-module-infra-biz/src/main/resources/application.yaml                                                             |    4 
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/AiChatConversationApi.java                                |   20 
 iailab-module-ai/.idea/sonarlint.xml                                                                                                        |   11 
 iailab-module-ai/.idea/vcs.xml                                                                                                              |    6 
 iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/AreaConvert.java                                 |   92 
 iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnum.java                                             |    4 
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/ApiConstants.java                                            |   23 
 iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/permission/MenuMapper.java                   |    1 
 .gitignore                                                                                                                                  |    3 
 iailab-framework/iailab-common-web/src/main/java/com/iailab/framework/web/core/handler/GlobalExceptionHandler.java                          |   32 
 iailab-module-infra/iailab-module-infra-biz/src/main/resources/mapper/monitordisk/MonitorDiskMapper.xml                                     |   42 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatConversationServiceImpl.java                    |    2 
 iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/logger/ApiErrorLogApi.java                            |   11 
 iailab-module-ai/.idea/compiler.xml                                                                                                         |   35 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/api/monitor/MonitorApiImpl.java                           |   40 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/chat/AiChatMessageApiImpl.java                                 |   27 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/job/monitor/MonitorDiskJob.java                           |   47 
 pom.xml                                                                                                                                     |    2 
 iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/monitor/MonitorApi.java                               |   42 
 iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/util/ServerInfoCollector.java                             |  198 ++
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/package-info.java                                              |    1 
 iailab-module-ai/iailab-spring-boot-starter-ai/pom.xml                                                                                      |    2 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemSaveReqVO.java   |   60 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitordisk/MonitorDiskServiceImpl.java           |  180 ++
 iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/UserTypeEnum.java                                            |    9 
 iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/enums/AreaTypeEnum.java                                    |    8 
 iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/social/SocialTypeEnum.java                       |    8 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskReqVO.java     |   49 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/MonitorDiskController.java   |  126 +
 iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/permission/DataScopeEnum.java                    |    8 
 iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumCollectionValidator.java                          |   13 
 iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/job/monitor/MonitorDiskJob.java                        |   49 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/mysql/monitormem/MonitorMemMapper.java                |   47 
 iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/sms/SmsSceneEnum.java                            |    8 
 iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/TerminalEnum.java                                            |    8 
 iailab-module-data/iailab-module-data-biz/logs/log-debug.2024-11-06.log                                                                     |  262 +++
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemReqVO.java       |   58 
 iailab-module-ai/iailab-module-ai-api/pom.xml                                                                                               |   17 
 iailab-module-system/iailab-module-system-biz/src/main/resources/application-prod.yaml                                                      |  116 +
 iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/DateIntervalEnum.java                                        |    8 
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/job/monitor/MonitorMemJob.java                            |   50 
 iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-prod.yaml                                                        |  150 ++
 iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/CommonStatusEnum.java                                        |    8 
 iailab-module-system/iailab-module-system-biz/proguard.cfg                                                                                  |  132 +
 iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitordisk/MonitorDiskService.java               |   94 +
 82 files changed, 4,363 insertions(+), 131 deletions(-)

diff --git a/.gitignore b/.gitignore
index 3f7ae50..e310db0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-/iailab-module-ai/
 /*/*/target/
 /.idea/
 /derby.log
@@ -8,4 +7,4 @@
 /*/*/.flattened-pom.xml
 /*/replay_pid15548.log
 /logs/
-/*/*/*.iml
+/*/*/*.iml
\ No newline at end of file
diff --git a/.xcodemap/config/xcodemap-class-filter.yaml b/.xcodemap/config/xcodemap-class-filter.yaml
new file mode 100644
index 0000000..ab90a7c
--- /dev/null
+++ b/.xcodemap/config/xcodemap-class-filter.yaml
@@ -0,0 +1,26 @@
+autoDetectedPackages:
+- com.alibaba.nacos
+- com.fhs.trans.service
+- com.iailab
+- com.xxl.job.admin
+- com.xxl.job.executorbiz
+- iail.mdk.model.common
+- org.springframework.messaging.handler.invocation
+enableAutoDetect: true
+funcDisplayConfig:
+  skipConstructors: false
+  skipFieldAccess: true
+  skipFieldChange: true
+  skipGetters: true
+  skipNonProjectPackages: true
+  skipPrivateMethods: false
+  skipSetters: true
+ignoreSameClassCall: null
+ignoreSamePackageCall: null
+includedPackagePrefixes: null
+includedParentClasses: null
+name: xcodemap-filter
+recordMode: smart
+sourceDisplayConfig:
+  color: blue
+startOnDebug: false
diff --git a/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/enums/AreaTypeEnum.java b/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/enums/AreaTypeEnum.java
index ceed9b9..416d1cc 100644
--- a/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/enums/AreaTypeEnum.java
+++ b/iailab-framework/iailab-common-biz-ip/src/main/java/com/iailab/framework/ip/core/enums/AreaTypeEnum.java
@@ -1,6 +1,6 @@
 package com.iailab.framework.ip.core.enums;
 
-import com.iailab.framework.common.core.IntArrayValuable;
+import com.iailab.framework.common.core.ArrayValuable;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
@@ -13,7 +13,7 @@
  */
 @AllArgsConstructor
 @Getter
-public enum AreaTypeEnum implements IntArrayValuable {
+public enum AreaTypeEnum implements ArrayValuable {
 
     COUNTRY(1, "国家"),
     PROVINCE(2, "省份"),
@@ -21,7 +21,7 @@
     DISTRICT(4, "地区"), // 县、镇、区等
     ;
 
-    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AreaTypeEnum::getType).toArray();
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(AreaTypeEnum::getType).toArray(Integer[]::new);
 
     /**
      * 类型
@@ -33,7 +33,7 @@
     private final String name;
 
     @Override
-    public int[] array() {
+    public Integer[] array() {
         return ARRAYS;
     }
 }
diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/AreaConvert.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/AreaConvert.java
index 558c5f7..3be47dd 100644
--- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/AreaConvert.java
+++ b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/convert/AreaConvert.java
@@ -1,46 +1,46 @@
-package com.iailab.framework.excel.core.convert;
-
-import cn.hutool.core.convert.Convert;
-import com.iailab.framework.ip.core.Area;
-import com.iailab.framework.ip.core.utils.AreaUtils;
-import com.alibaba.excel.converters.Converter;
-import com.alibaba.excel.enums.CellDataTypeEnum;
-import com.alibaba.excel.metadata.GlobalConfiguration;
-import com.alibaba.excel.metadata.data.ReadCellData;
-import com.alibaba.excel.metadata.property.ExcelContentProperty;
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * Excel 数据地区转换器
- *
- * @author HUIHUI
- */
-@Slf4j
-public class AreaConvert implements Converter<Object> {
-
-    @Override
-    public Class<?> supportJavaTypeKey() {
-        throw new UnsupportedOperationException("暂不支持,也不需要");
-    }
-
-    @Override
-    public CellDataTypeEnum supportExcelTypeKey() {
-        throw new UnsupportedOperationException("暂不支持,也不需要");
-    }
-
-    @Override
-    public Object convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty,
-                                    GlobalConfiguration globalConfiguration) {
-        // 解析地区编号
-        String label = readCellData.getStringValue();
-        Area area = AreaUtils.parseArea(label);
-        if (area == null) {
-            log.error("[convertToJavaData][label({}) 解析不掉]", label);
-            return null;
-        }
-        // 将 value 转换成对应的属性
-        Class<?> fieldClazz = contentProperty.getField().getType();
-        return Convert.convert(fieldClazz, area.getId());
-    }
-
-}
+//package com.iailab.framework.excel.core.convert;
+//
+//import cn.hutool.core.convert.Convert;
+//import com.iailab.framework.ip.core.Area;
+//import com.iailab.framework.ip.core.utils.AreaUtils;
+//import com.alibaba.excel.converters.Converter;
+//import com.alibaba.excel.enums.CellDataTypeEnum;
+//import com.alibaba.excel.metadata.GlobalConfiguration;
+//import com.alibaba.excel.metadata.data.ReadCellData;
+//import com.alibaba.excel.metadata.property.ExcelContentProperty;
+//import lombok.extern.slf4j.Slf4j;
+//
+///**
+// * Excel 数据地区转换器
+// *
+// * @author HUIHUI
+// */
+//@Slf4j
+//public class AreaConvert implements Converter<Object> {
+//
+//    @Override
+//    public Class<?> supportJavaTypeKey() {
+//        throw new UnsupportedOperationException("暂不支持,也不需要");
+//    }
+//
+//    @Override
+//    public CellDataTypeEnum supportExcelTypeKey() {
+//        throw new UnsupportedOperationException("暂不支持,也不需要");
+//    }
+//
+//    @Override
+//    public Object convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty,
+//                                    GlobalConfiguration globalConfiguration) {
+//        // 解析地区编号
+//        String label = readCellData.getStringValue();
+//        Area area = AreaUtils.parseArea(label);
+//        if (area == null) {
+//            log.error("[convertToJavaData][label({}) 解析不掉]", label);
+//            return null;
+//        }
+//        // 将 value 转换成对应的属性
+//        Class<?> fieldClazz = contentProperty.getField().getType();
+//        return Convert.convert(fieldClazz, area.getId());
+//    }
+//
+//}
diff --git a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/config/IailabMybatisAutoConfiguration.java b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/config/IailabMybatisAutoConfiguration.java
index 5ee2990..74269b6 100644
--- a/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/config/IailabMybatisAutoConfiguration.java
+++ b/iailab-framework/iailab-common-mybatis/src/main/java/com/iailab/framework/mybatis/config/IailabMybatisAutoConfiguration.java
@@ -1,7 +1,6 @@
 package com.iailab.framework.mybatis.config;
 
 import cn.hutool.core.util.StrUtil;
-import com.iailab.framework.mybatis.core.enums.SqlConstants;
 import com.iailab.framework.mybatis.core.handler.DefaultDBFieldHandler;
 import com.baomidou.mybatisplus.annotation.DbType;
 import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
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 79930b8..ce679c1 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
@@ -304,51 +304,51 @@
         }
         // 1. 数据报表
         if (message.contains("report_")) {
-            log.error("[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]");
+            log.error("[报表模块 iailab-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]");
             return CommonResult.error(NOT_IMPLEMENTED.getCode(),
-                    "[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]");
+                    "[报表模块 iailab-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]");
         }
         // 2. 工作流
         if (message.contains("bpm_")) {
-            log.error("[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]");
+            log.error("[工作流模块 iailab-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]");
             return CommonResult.error(NOT_IMPLEMENTED.getCode(),
-                    "[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]");
+                    "[工作流模块 iailab-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]");
         }
         // 3. 微信公众号
         if (message.contains("mp_")) {
-            log.error("[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]");
+            log.error("[微信公众号 iailab-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]");
             return CommonResult.error(NOT_IMPLEMENTED.getCode(),
-                    "[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]");
+                    "[微信公众号 iailab-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]");
         }
         // 4. 商城系统
         if (StrUtil.containsAny(message, "product_", "promotion_", "trade_")) {
-            log.error("[商城系统 yudao-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]");
+            log.error("[商城系统 iailab-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]");
             return CommonResult.error(NOT_IMPLEMENTED.getCode(),
-                    "[商城系统 yudao-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]");
+                    "[商城系统 iailab-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]");
         }
         // 5. ERP 系统
         if (message.contains("erp_")) {
-            log.error("[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
+            log.error("[ERP 系统 iailab-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
             return CommonResult.error(NOT_IMPLEMENTED.getCode(),
-                    "[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
+                    "[ERP 系统 iailab-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
         }
         // 6. CRM 系统
         if (message.contains("crm_")) {
-            log.error("[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
+            log.error("[CRM 系统 iailab-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
             return CommonResult.error(NOT_IMPLEMENTED.getCode(),
-                    "[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
+                    "[CRM 系统 iailab-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
         }
         // 7. 支付平台
         if (message.contains("pay_")) {
-            log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]");
+            log.error("[支付模块 iailab-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]");
             return CommonResult.error(NOT_IMPLEMENTED.getCode(),
-                    "[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]");
+                    "[支付模块 iailab-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/ 开启]");
+            log.error("[AI 大模型 iailab-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]");
             return CommonResult.error(NOT_IMPLEMENTED.getCode(),
-                    "[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]");
+                    "[AI 大模型 iailab-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]");
         }
         return null;
     }
diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/core/ArrayValuable.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/core/ArrayValuable.java
new file mode 100644
index 0000000..ab2b3c3
--- /dev/null
+++ b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/core/ArrayValuable.java
@@ -0,0 +1,15 @@
+package com.iailab.framework.common.core;
+
+/**
+ * 可生成 T 数组的接口
+ *
+ * @author HUIHUI
+ */
+public interface ArrayValuable<T> {
+
+    /**
+     * @return 数组
+     */
+    T[] array();
+
+} 
\ No newline at end of file
diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/CommonStatusEnum.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/CommonStatusEnum.java
index a611b8b..b43e211 100644
--- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/CommonStatusEnum.java
+++ b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/CommonStatusEnum.java
@@ -1,7 +1,7 @@
 package com.iailab.framework.common.enums;
 
 import cn.hutool.core.util.ObjUtil;
-import com.iailab.framework.common.core.IntArrayValuable;
+import com.iailab.framework.common.core.ArrayValuable;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
@@ -14,12 +14,12 @@
  */
 @Getter
 @AllArgsConstructor
-public enum CommonStatusEnum implements IntArrayValuable {
+public enum CommonStatusEnum implements ArrayValuable {
 
     ENABLE(0, "开启"),
     DISABLE(1, "关闭");
 
-    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CommonStatusEnum::getStatus).toArray();
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CommonStatusEnum::getStatus).toArray(Integer[]::new);
 
     /**
      * 状态值
@@ -31,7 +31,7 @@
     private final String name;
 
     @Override
-    public int[] array() {
+    public Integer[] array() {
         return ARRAYS;
     }
 
diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/DateIntervalEnum.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/DateIntervalEnum.java
index af55211..6398f8a 100644
--- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/DateIntervalEnum.java
+++ b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/DateIntervalEnum.java
@@ -1,7 +1,7 @@
 package com.iailab.framework.common.enums;
 
 import cn.hutool.core.util.ArrayUtil;
-import com.iailab.framework.common.core.IntArrayValuable;
+import com.iailab.framework.common.core.ArrayValuable;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
@@ -14,7 +14,7 @@
  */
 @Getter
 @AllArgsConstructor
-public enum DateIntervalEnum implements IntArrayValuable {
+public enum DateIntervalEnum implements ArrayValuable {
 
     DAY(1, "天"),
     WEEK(2, "周"),
@@ -23,7 +23,7 @@
     YEAR(5, "年")
     ;
 
-    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getInterval).toArray();
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(DateIntervalEnum::getInterval).toArray(Integer[]::new);
 
     /**
      * 类型
@@ -35,7 +35,7 @@
     private final String name;
 
     @Override
-    public int[] array() {
+    public Integer[] array() {
         return ARRAYS;
     }
 
diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/TerminalEnum.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/TerminalEnum.java
index c49ae51..a3f8acb 100644
--- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/TerminalEnum.java
+++ b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/TerminalEnum.java
@@ -1,6 +1,6 @@
 package com.iailab.framework.common.enums;
 
-import com.iailab.framework.common.core.IntArrayValuable;
+import com.iailab.framework.common.core.ArrayValuable;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 
@@ -13,7 +13,7 @@
  */
 @RequiredArgsConstructor
 @Getter
-public enum TerminalEnum implements IntArrayValuable {
+public enum TerminalEnum implements ArrayValuable {
 
     UNKNOWN(0, "未知"), // 目的:在无法解析到 terminal 时,使用它
     WECHAT_MINI_PROGRAM(10, "微信小程序"),
@@ -22,7 +22,7 @@
     APP(31, "手机 App"),
     ;
 
-    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TerminalEnum::getTerminal).toArray();
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(TerminalEnum::getTerminal).toArray(Integer[]::new);
 
     /**
      * 终端
@@ -34,7 +34,7 @@
     private final String name;
 
     @Override
-    public int[] array() {
+    public Integer[] array() {
         return ARRAYS;
     }
 }
diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/UserTypeEnum.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/UserTypeEnum.java
index 560e02c..5ca540a 100644
--- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/UserTypeEnum.java
+++ b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/enums/UserTypeEnum.java
@@ -1,7 +1,7 @@
 package com.iailab.framework.common.enums;
 
 import cn.hutool.core.util.ArrayUtil;
-import com.iailab.framework.common.core.IntArrayValuable;
+import com.iailab.framework.common.core.ArrayValuable;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
@@ -12,13 +12,12 @@
  */
 @AllArgsConstructor
 @Getter
-public enum UserTypeEnum implements IntArrayValuable {
+public enum UserTypeEnum implements ArrayValuable {
 
     MEMBER(1, "会员"), // 面向 c 端,普通用户
     ADMIN(2, "管理员"); // 面向 b 端,管理后台
 
-    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(UserTypeEnum::getValue).toArray();
-
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(UserTypeEnum::getValue).toArray(Integer[]::new);
     /**
      * 类型
      */
@@ -33,7 +32,7 @@
     }
 
     @Override
-    public int[] array() {
+    public Integer[] array() {
         return ARRAYS;
     }
 }
diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/template/XMLParserUtils.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/template/XMLParserUtils.java
new file mode 100644
index 0000000..5e4fc79
--- /dev/null
+++ b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/template/XMLParserUtils.java
@@ -0,0 +1,296 @@
+package com.iailab.framework.common.util.template;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.ByteArrayInputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+public class XMLParserUtils {
+    private final Map<String, String> settings;
+    private final double[][] inputData;
+
+    public XMLParserUtils(Map<String, String> settings, double[][] inputData) {
+        this.settings = settings;
+        this.inputData = inputData;
+    }
+
+    public String parse(String xml) throws Exception {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder builder = factory.newDocumentBuilder();
+        Document doc = builder.parse(new ByteArrayInputStream(xml.getBytes()));
+        doc.getDocumentElement().normalize();
+
+        StringBuilder output = new StringBuilder();
+        processNode(doc.getDocumentElement(), output, -1);
+        return output.toString();
+    }
+
+    private void processNode(Node node, StringBuilder output, int index) {
+        if (node.getNodeType() == Node.TEXT_NODE) {
+            output.append(node.getTextContent().trim());
+        } else if (node.getNodeType() == Node.ELEMENT_NODE) {
+            Element element = (Element) node;
+            switch (element.getTagName()) {
+                case "simple-value":
+                    processSimpleValue(element, output, index);
+                    break;
+                case "setting-value":
+                    processSettingValue(element, output);
+                    break;
+                case "input-value":
+                    processInputValue(element, output, index);
+                    break;
+                case "foreach-value":
+                    processForeachValue(element, output);
+                    break;
+                default:
+                    processChildren(element, output, index);
+            }
+        }
+    }
+
+    private void processChildren(Element element, StringBuilder output, int index) {
+        NodeList children = element.getChildNodes();
+        for (int i = 0; i < children.getLength(); i++) {
+            processNode(children.item(i), output, index);
+        }
+    }
+
+    private void processSimpleValue(Element element, StringBuilder output, int index) {
+        processChildren(element, output, index);
+    }
+
+    private void processSettingValue(Element element, StringBuilder output) {
+        String key = element.getAttribute("key");
+        output.append(settings.getOrDefault(key, ""));
+    }
+
+    private void processInputValue(Element element, StringBuilder output, int index) {
+        // 优先使用:port属性(动态计算)
+        String portExpr = element.getAttribute(":port");
+        if (portExpr.isEmpty()) {
+            // 回退到port属性(静态值)
+            portExpr = element.getAttribute("port");
+        }
+
+        String column = element.getAttribute("column");
+        int col = Integer.parseInt(column);
+
+        // 动态计算端口值(考虑当前索引)
+        int port = evaluateExpression(portExpr, index);
+
+        if (port >= 0 && port < inputData.length &&
+                col >= 0 && col < inputData[port].length) {
+            double value = inputData[port][col];
+            output.append((int)value);
+        } else {
+            System.err.printf("Invalid data access: port=%d (max=%d), col=%d (max=%d)%n",
+                    port, inputData.length - 1, col,
+                    (port >= 0 && port < inputData.length) ? inputData[port].length - 1 : -1);
+            // 默认值
+            output.append("0");
+        }
+    }
+
+    private void processForeachValue(Element element, StringBuilder output) {
+        String lengthKey = element.getAttribute("length");
+        int length = Integer.parseInt(settings.getOrDefault(lengthKey, "0"));
+        String separator = element.getAttribute("separator");
+
+        StringBuilder loopResult = new StringBuilder();
+        for (int i = 0; i < length; i++) {
+            StringBuilder itemOutput = new StringBuilder();
+
+            // 直接处理所有子节点
+            NodeList children = element.getChildNodes();
+            for (int j = 0; j < children.getLength(); j++) {
+                Node child = children.item(j);
+
+                if (child.getNodeType() == Node.TEXT_NODE) {
+                    String text = child.getTextContent();
+
+                    // 动态确定转炉状态
+                    boolean isBlowing = isFurnaceBlowing(i);
+
+                    // 替换占位符并调整状态描述
+                    text = text.replace("{index}", String.valueOf(i + 1))
+                            .replace("未吹炼", isBlowing ? "正在吹炼" : "未吹炼")
+                            .replace("距离上次吹炼结束时间",
+                                    isBlowing ? "吹炼持续时间" : "距离上次吹炼结束时间");
+
+                    itemOutput.append(text);
+                } else if (child.getNodeType() == Node.ELEMENT_NODE) {
+                    // 传递当前循环索引
+                    processNode(child, itemOutput, i);
+                }
+            }
+
+            loopResult.append(itemOutput);
+            if (i < length - 1 && !separator.isEmpty()) {
+                loopResult.append(separator);
+            }
+        }
+        output.append(loopResult.toString().replace(" ", "").replace("\n", ""));
+    }
+
+    private boolean isFurnaceBlowing(int furnaceIndex) {
+        // 实际业务逻辑:根据输入数据判断转炉状态
+        // 这里简化处理:第三个转炉(index=2)正在吹炼
+        return furnaceIndex == 2;
+    }
+
+    // 自定义表达式计算器
+    private int evaluateExpression(String expr, int index) {
+        if (expr == null || expr.isEmpty()) {
+            System.err.println("Empty expression, returning 0");
+            return 0;
+        }
+        // 替换表达式中的{index}占位符
+        String processedExpr = expr.replace("{index}", String.valueOf(index));
+
+        // 移除所有空格确保表达式正确解析
+        processedExpr = processedExpr.replaceAll("\\s", "");
+
+        // 尝试直接解析为整数(优化常见情况)
+        try {
+            return Integer.parseInt(processedExpr);
+        } catch (NumberFormatException e) {
+            // 不是简单整数,继续尝试表达式计算
+        }
+        // 自定义简单表达式解析器
+        try {
+            // 处理加法表达式(如 "3+0")
+            if (processedExpr.contains("+")) {
+                String[] parts = processedExpr.split("\\+");
+                int result = 0;
+                for (String part : parts) {
+                    result += Integer.parseInt(part);
+                }
+                return result;
+            }
+            // 处理减法表达式(如 "5-2")
+            else if (processedExpr.contains("-")) {
+                String[] parts = processedExpr.split("-");
+                int result = Integer.parseInt(parts[0]);
+                for (int i = 1; i < parts.length; i++) {
+                    result -= Integer.parseInt(parts[i]);
+                }
+                return result;
+            }
+//            // 处理乘法表达式(如 "2 * 3")
+//            else if (processedExpr.contains("*")) {
+//                String[] parts = processedExpr.split("\\*");
+//                int result = 1;
+//                for (String part : parts) {
+//                    result *= Integer.parseInt(part);
+//                }
+//                return result;
+//            }
+//            // 处理除法表达式(如 "6/2")
+//            else if (processedExpr.contains("/")) {
+//                String[] parts = processedExpr.split("/");
+//                int result = Integer.parseInt(parts[0]);
+//                for (int i = 1; i < parts.length; i++) {
+//                    int divisor = Integer.parseInt(parts[i]);
+//                    if (divisor != 0) {
+//                        result /= divisor;
+//                    }
+//                }
+//                return result;
+//            }
+        } catch (NumberFormatException e) {
+            System.err.println("Error parsing expression: " + processedExpr);
+        }
+        // 无法解析的表达式
+        System.err.println("Unsupported expression format: " + processedExpr);
+        return 0;
+    }
+
+    // test
+    public static void main(String[] args) throws Exception {
+        String xmlContent = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<iquestion>\n" +
+                "    <simple-value>根据以下输入,</simple-value>\n" +
+                "    <simple-value>\n" +
+                "        根据未来<setting-value key=\"predictLength\"/>min的转炉煤气系统运行情况,对煤气调整用户进行调度分配,确保能源产消平衡与系统稳定运行。\n" +
+                "    </simple-value>\n" +
+                "    <simple-value>\n" +
+                "        该系统共包含<setting-value key=\"luShu\"/>座转炉。\n" +
+                "    </simple-value>\n" +
+                "    <foreach-value length=\"luShu\" index=\"index\" separator=\";\">\n" +
+                "        {index}#转炉当前状态未吹炼,\n" +
+                "        距离上次吹炼结束时间<input-value :port=\"3 + {index}\" column=\"0\" type=\"double\"/>min,\n" +
+                "        前一炉回收间隔<input-value :port=\"3 + {index}\" column=\"2\" type=\"double\"/>min\n" +
+                "    </foreach-value>\n" +
+                "    <simple-value>\n" +
+                "        日计划炉数:<input-value port=\"2\" column=\"0\" type=\"double\"/>,当前已吹炼炉数:<input-value port=\"3\" column=\"0\" type=\"double\"/>;\n" +
+                "    </simple-value>\n" +
+                "    <simple-value>\n" +
+                "        历史60min平均每炉煤气回收量:<input-value port=\"1\" column=\"0\" type=\"double\"/>Km3,\n" +
+                "    </simple-value>\n" +
+                "    <simple-value>\n" +
+                "        当前煤气消耗量<input-value port=\"1\" column=\"1\" type=\"double\"/>km3;\n" +
+                "    </simple-value>\n" +
+                "    <simple-value>\n" +
+                "        当前煤气柜容量<setting-value key=\"guiRongLiang\"/>km3;\n" +
+                "    </simple-value>\n" +
+                "    <simple-value>\n" +
+                "        煤气柜柜位安全区间为<setting-value key=\"anQuanQuJian\"/>km3,期望煤气柜位值在<setting-value key=\"qiWangZhi\"/>km3,\n" +
+                "    </simple-value>\n" +
+                "    <simple-value>\n" +
+                "        当前调度用户为电厂各机组:<setting-value key=\"jiZuMingCheng\"/>,\n" +
+                "    </simple-value>\n" +
+                "    <simple-value>\n" +
+                "        各机组当前使用转炉煤气量 [<input-value port=\"0\" column=\"0\" type=\"double\"/>, <input-value port=\"0\" column=\"1\" type=\"double\"/>, <input-value port=\"0\" column=\"2\" type=\"double\"/>, <input-value port=\"0\" column=\"3\" type=\"double\"/>, <input-value port=\"0\" column=\"4\" type=\"double\"/>, <input-value port=\"0\" column=\"5\" type=\"double\"/>];\n" +
+                "    </simple-value>\n" +
+                "    <simple-value>\n" +
+                "        各机组使用转炉煤气下限量 <setting-value key=\"jiZuXiaXian\"/>。\n" +
+                "    </simple-value>\n" +
+                "    <simple-value>\n" +
+                "        各机组使用转炉煤气上限量 <setting-value key=\"jiZuShangXian\"/>。\n" +
+                "    </simple-value>\n" +
+                "    <simple-value>\n" +
+                "        机组优先级顺序为:<setting-value key=\"jiZuYouXianJi\"/>。\n" +
+                "    </simple-value>\n" +
+                "    <simple-value>请根据优先级顺序确定调度方案。</simple-value>\n" +
+                "</iquestion>";
+
+        // 1. 准备配置数据
+        Map<String, String> settings = new HashMap<>();
+        settings.put("predictLength", "60");
+        settings.put("luShu", "3");
+        settings.put("guiRongLiang", "80");
+        settings.put("anQuanQuJian", "[30, 110]");
+        settings.put("qiWangZhi", "65");
+        settings.put("jiZuMingCheng", "1#135, 2#135, 1#BTG, 2#BTG, 3#BTG, 4#BTG");
+        settings.put("jiZuXiaXian", "[20, 1, 16, 17, 6, 11]");
+        settings.put("jiZuShangXian", "[92, 96, 74, 101, 86, 93]");
+        settings.put("jiZuYouXianJi", "1#135, 1#BTG, 3#BTG, 2#135, 2#BTG, 4#BTG");
+
+        // 示例输入数据 - 与期望输出完全匹配
+        double[][] inputData = {
+                // 机组当前用量
+                {39, 39, 32, 56, 38, 48},
+                // [0]:历史回收量, [1]:当前消耗量
+                {24, 115},
+                // [0]:日计划炉数, [1]:已吹炼炉数
+                {21, 10},
+                // 转炉1: [0]:结束时间, [2]:回收间隔
+                {15, 0, 18},
+                // 转炉2: [0]:结束时间, [2]:回收间隔
+                {9, 0, 24},
+                // 转炉3: [0]:吹炼持续时间, [2]:回收间隔
+                {7, 0, 25}
+        };
+
+        // 3. 创建解析器并执行
+        XMLParserUtils parser = new XMLParserUtils(settings, inputData);
+        String result = parser.parse(xmlContent); // 替换为你的XML内容
+        System.out.println(result);
+    }
+}
\ No newline at end of file
diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnum.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnum.java
index d7bc460..ebe4122 100644
--- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnum.java
+++ b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnum.java
@@ -1,6 +1,6 @@
 package com.iailab.framework.common.validation;
 
-import com.iailab.framework.common.core.IntArrayValuable;
+import com.iailab.framework.common.core.ArrayValuable;
 
 import javax.validation.Constraint;
 import javax.validation.Payload;
@@ -24,7 +24,7 @@
     /**
      * @return 实现 EnumValuable 接口的
      */
-    Class<? extends IntArrayValuable> value();
+    Class<? extends ArrayValuable> value();
 
     String message() default "必须在指定范围 {value}";
 
diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumCollectionValidator.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumCollectionValidator.java
index ce162b2..41a8d5d 100644
--- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumCollectionValidator.java
+++ b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumCollectionValidator.java
@@ -1,7 +1,7 @@
 package com.iailab.framework.common.validation;
 
 import cn.hutool.core.collection.CollUtil;
-import com.iailab.framework.common.core.IntArrayValuable;
+import com.iailab.framework.common.core.ArrayValuable;
 
 import javax.validation.ConstraintValidator;
 import javax.validation.ConstraintValidatorContext;
@@ -9,24 +9,23 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.stream.Collectors;
 
-public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<Integer>> {
+public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<?>> {
 
-    private List<Integer> values;
+    private List<?> values;
 
     @Override
     public void initialize(InEnum annotation) {
-        IntArrayValuable[] values = annotation.value().getEnumConstants();
+        ArrayValuable<?>[] values = annotation.value().getEnumConstants();
         if (values.length == 0) {
             this.values = Collections.emptyList();
         } else {
-            this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList());
+            this.values = Arrays.asList(values[0].array());
         }
     }
 
     @Override
-    public boolean isValid(Collection<Integer> list, ConstraintValidatorContext context) {
+    public boolean isValid(Collection<?> list, ConstraintValidatorContext context) {
         // 校验通过
         if (CollUtil.containsAll(values, list)) {
             return true;
diff --git a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumValidator.java b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumValidator.java
index f20b1fb..7614656 100644
--- a/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumValidator.java
+++ b/iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/validation/InEnumValidator.java
@@ -1,30 +1,29 @@
 package com.iailab.framework.common.validation;
 
-import com.iailab.framework.common.core.IntArrayValuable;
+import com.iailab.framework.common.core.ArrayValuable;
 
 import javax.validation.ConstraintValidator;
 import javax.validation.ConstraintValidatorContext;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.stream.Collectors;
 
-public class InEnumValidator implements ConstraintValidator<InEnum, Integer> {
+public class InEnumValidator implements ConstraintValidator<InEnum, Object> {
 
-    private List<Integer> values;
+    private List<?> values;
 
     @Override
     public void initialize(InEnum annotation) {
-        IntArrayValuable[] values = annotation.value().getEnumConstants();
+        ArrayValuable<?>[] values = annotation.value().getEnumConstants();
         if (values.length == 0) {
             this.values = Collections.emptyList();
         } else {
-            this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList());
+            this.values = Arrays.asList(values[0].array());
         }
     }
 
     @Override
-    public boolean isValid(Integer value, ConstraintValidatorContext context) {
+    public boolean isValid(Object value, ConstraintValidatorContext context) {
         // 为空时,默认不校验,即认为通过
         if (value == null) {
             return true;
@@ -33,7 +32,7 @@
         if (values.contains(value)) {
             return true;
         }
-        // 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
+        // 校验不通过,自定义提示语句
         context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
         context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
                 .replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句
diff --git a/iailab-module-ai/.idea/.gitignore b/iailab-module-ai/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/iailab-module-ai/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/iailab-module-ai/.idea/compiler.xml b/iailab-module-ai/.idea/compiler.xml
new file mode 100644
index 0000000..bfb449c
--- /dev/null
+++ b/iailab-module-ai/.idea/compiler.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <annotationProcessing>
+      <profile default="true" name="Default" enabled="true" />
+      <profile name="Maven default annotation processors profile" enabled="true">
+        <sourceOutputDir name="target/generated-sources/annotations" />
+        <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
+        <outputRelativeToContentRoot value="true" />
+        <module name="yudao-module-ai-api" />
+      </profile>
+      <profile name="Annotation profile for iailab-module-ai" enabled="true">
+        <sourceOutputDir name="target/generated-sources/annotations" />
+        <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
+        <outputRelativeToContentRoot value="true" />
+        <processorPath useClasspath="false">
+          <entry name="$PROJECT_DIR$/../../../../.m2/repository/org/springframework/boot/spring-boot-configuration-processor/3.4.1/spring-boot-configuration-processor-3.4.1.jar" />
+          <entry name="$PROJECT_DIR$/../../../../.m2/repository/org/projectlombok/lombok/1.18.36/lombok-1.18.36.jar" />
+          <entry name="$PROJECT_DIR$/../../../../.m2/repository/org/mapstruct/mapstruct-processor/1.6.3/mapstruct-processor-1.6.3.jar" />
+          <entry name="$PROJECT_DIR$/../../../../.m2/repository/org/mapstruct/mapstruct/1.6.3/mapstruct-1.6.3.jar" />
+        </processorPath>
+        <module name="iailab-module-ai-api" />
+        <module name="iailab-spring-boot-starter-ai" />
+        <module name="iailab-module-ai-biz" />
+      </profile>
+    </annotationProcessing>
+  </component>
+  <component name="JavacSettings">
+    <option name="ADDITIONAL_OPTIONS_OVERRIDE">
+      <module name="iailab-module-ai-api" options="-parameters" />
+      <module name="iailab-module-ai-biz" options="-parameters" />
+      <module name="iailab-spring-boot-starter-ai" options="-parameters" />
+    </option>
+  </component>
+</project>
\ No newline at end of file
diff --git a/iailab-module-ai/.idea/jarRepositories.xml b/iailab-module-ai/.idea/jarRepositories.xml
new file mode 100644
index 0000000..0edd925
--- /dev/null
+++ b/iailab-module-ai/.idea/jarRepositories.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RemoteRepositoriesConfiguration">
+    <remote-repository>
+      <option name="id" value="central" />
+      <option name="name" value="Central Repository" />
+      <option name="url" value="https://repo.maven.apache.org/maven2" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="iailab" />
+      <option name="name" value="iailab" />
+      <option name="url" value="http://172.16.8.100:8090/repository/iailab/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="central" />
+      <option name="name" value="Maven Central repository" />
+      <option name="url" value="https://repo1.maven.org/maven2" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="jboss.community" />
+      <option name="name" value="JBoss Community repository" />
+      <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="iailab" />
+      <option name="name" value="iailab" />
+      <option name="url" value="http://www.jira.dlindusit.com:30078/repository/iailab/" />
+    </remote-repository>
+  </component>
+</project>
\ No newline at end of file
diff --git a/iailab-module-ai/.idea/misc.xml b/iailab-module-ai/.idea/misc.xml
new file mode 100644
index 0000000..c5fe43c
--- /dev/null
+++ b/iailab-module-ai/.idea/misc.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
+  <component name="MavenProjectsManager">
+    <option name="originalFiles">
+      <list>
+        <option value="$PROJECT_DIR$/pom.xml" />
+        <option value="$PROJECT_DIR$/iailab-module-ai-api/pom.xml" />
+        <option value="$PROJECT_DIR$/iailab-spring-boot-starter-ai/pom.xml" />
+      </list>
+    </option>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="graalvm-jdk-17" project-jdk-type="JavaSDK" />
+</project>
\ No newline at end of file
diff --git a/iailab-module-ai/.idea/sonarlint.xml b/iailab-module-ai/.idea/sonarlint.xml
new file mode 100644
index 0000000..3309dbe
--- /dev/null
+++ b/iailab-module-ai/.idea/sonarlint.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="SonarLintProjectSettings">
+    <option name="moduleMapping">
+      <map>
+        <entry key="iailab-module-ai-biz" value="yudao-module-ai-biz" />
+        <entry key="iailab-spring-boot-starter-ai" value="yudao-spring-boot-starter-ai" />
+      </map>
+    </option>
+  </component>
+</project>
\ No newline at end of file
diff --git a/iailab-module-ai/.idea/vcs.xml b/iailab-module-ai/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/iailab-module-ai/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-api/pom.xml b/iailab-module-ai/iailab-module-ai-api/pom.xml
index 5799a96..e725d01 100644
--- a/iailab-module-ai/iailab-module-ai-api/pom.xml
+++ b/iailab-module-ai/iailab-module-ai-api/pom.xml
@@ -5,7 +5,7 @@
     <parent>
         <groupId>com.iailab</groupId>
         <artifactId>iailab-module-ai</artifactId>
-        <version>${ai.revision}</version>
+        <version>${revision}</version>
     </parent>
     <build>
         <plugins>
@@ -34,11 +34,26 @@
             <artifactId>iailab-common</artifactId>
         </dependency>
 
+        <!-- Web 相关 -->
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-ui</artifactId>
+            <version>1.8.0</version>
+            <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 -->
+        </dependency>
+
         <!-- 参数校验 -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-validation</artifactId>
             <optional>true</optional>
         </dependency>
+
+        <!-- RPC 远程调用相关 -->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+            <optional>true</optional>
+        </dependency>
     </dependencies>
 </project>
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/AiChatConversationApi.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/AiChatConversationApi.java
new file mode 100644
index 0000000..17ccb44
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/AiChatConversationApi.java
@@ -0,0 +1,20 @@
+package com.iailab.module.ai.api.chat;
+
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.module.ai.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.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+@FeignClient(name = ApiConstants.NAME)
+@Tag(name = "RPC 大模型会话")
+public interface AiChatConversationApi {
+
+    String PREFIX = ApiConstants.PREFIX + "/conversation";
+
+    @GetMapping(PREFIX + "/energy-conversation")
+    @Operation(summary = "会话列表", description = "查询会话列表,只有一条,首次查询先创建再查询")
+    Long chatEnergyConversation(@RequestParam("modelName") String modelName);
+}
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/AiChatMessageApi.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/AiChatMessageApi.java
new file mode 100644
index 0000000..9a71cab
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/AiChatMessageApi.java
@@ -0,0 +1,22 @@
+package com.iailab.module.ai.api.chat;
+
+import com.iailab.module.ai.api.chat.dto.AiChatMessageSendRespDTO;
+import com.iailab.module.ai.api.chat.dto.AiChatMessageSendReqDTO;
+import com.iailab.module.ai.enums.ApiConstants;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+@FeignClient(name = ApiConstants.NAME)
+@Tag(name = "RPC 大模型消息")
+public interface AiChatMessageApi {
+
+    String PREFIX = ApiConstants.PREFIX + "/message";
+
+    @PostMapping(PREFIX + "/send-message")
+    @Operation(summary = "发送消息(段式)", description = "一次性返回,响应较慢")
+    AiChatMessageSendRespDTO sendMessage(@Valid @RequestBody AiChatMessageSendReqDTO sendReqDTO);
+}
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatConversationRespDTO.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatConversationRespDTO.java
new file mode 100644
index 0000000..e3b8603
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatConversationRespDTO.java
@@ -0,0 +1,58 @@
+package com.iailab.module.ai.api.chat.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.time.LocalDateTime;
+
+public class AiChatConversationRespDTO {
+    @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Long id;
+
+    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
+    private Long userId;
+
+    @Schema(description = "对话标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是一个标题")
+    private String title;
+
+    @Schema(description = "是否置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+    private Boolean pinned;
+
+    @Schema(description = "角色编号", example = "1")
+    private Long roleId;
+
+    @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Long modelId;
+
+    @Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922")
+    private String model;
+
+    @Schema(description = "模型名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
+    private String modelName;
+
+    @Schema(description = "角色设定", example = "一个快乐的程序员")
+    private String systemMessage;
+
+    @Schema(description = "温度参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.8")
+    private Double temperature;
+
+    @Schema(description = "单条回复的最大 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
+    private Integer maxTokens;
+
+    @Schema(description = "上下文的最大 Message 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    private Integer maxContexts;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+    // ========== 关联 role 信息 ==========
+
+    @Schema(description = "角色头像", example = "https://www.Iailab.cn/1.png")
+    private String roleAvatar;
+
+    @Schema(description = "角色名字", example = "小黄")
+    private String roleName;
+
+    // ========== 仅在【对话管理】时加载 ==========
+
+    @Schema(description = "消息数量", example = "20")
+    private Integer messageCount;
+}
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatMessageSendReqDTO.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatMessageSendReqDTO.java
new file mode 100644
index 0000000..4c04f53
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatMessageSendReqDTO.java
@@ -0,0 +1,23 @@
+package com.iailab.module.ai.api.chat.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 聊天消息发送 Request VO")
+@Data
+public class AiChatMessageSendReqDTO {
+
+    @Schema(description = "聊天对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    @NotNull(message = "聊天对话编号不能为空")
+    private Long conversationId;
+
+    @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "帮我写个 Java 算法")
+    @NotEmpty(message = "聊天内容不能为空")
+    private String content;
+
+    @Schema(description = "是否携带上下文", example = "true")
+    private Boolean useContext;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatMessageSendRespDTO.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatMessageSendRespDTO.java
new file mode 100644
index 0000000..e58aa27
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatMessageSendRespDTO.java
@@ -0,0 +1,34 @@
+package com.iailab.module.ai.api.chat.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class AiChatMessageSendRespDTO {
+
+    @Schema(description = "发送消息", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Message send;
+
+    @Schema(description = "接收消息", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Message receive;
+
+    @Schema(description = "消息")
+    @Data
+    public static class Message {
+
+        @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+        private Long id;
+
+        @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "role")
+        private String type; // 参见 MessageType 枚举类
+
+        @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊")
+        private String content;
+
+        @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+        private LocalDateTime createTime;
+
+    }
+}
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiModelDO.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiModelDO.java
new file mode 100644
index 0000000..14332ae
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiModelDO.java
@@ -0,0 +1,79 @@
+package com.iailab.module.ai.api.chat.dto;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * AI 模型 DO
+ *
+ * 默认模型:{@link #status} 为开启,并且 {@link #sort} 排序第一
+ *
+ * @author houzhongjian
+ * @since 2025/6/33 11:39
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AiModelDO {
+
+    /**
+     * 编号
+     */
+    private Long id;
+    /**
+     * API 秘钥编号
+     *
+     */
+    private Long keyId;
+    /**
+     * 模型名称
+     */
+    private String name;
+    /**
+     * 模型标志
+     */
+    private String model;
+    /**
+     * 平台
+     *
+     */
+    private String platform;
+    /**
+     * 类型
+     *
+     */
+    private Integer type;
+
+    /**
+     * 排序值
+     */
+    private Integer sort;
+    /**
+     * 状态
+     *
+     * 枚举 {@link CommonStatusEnum}
+     */
+    private Integer status;
+
+    // ========== 对话配置 ==========
+
+    /**
+     * 温度参数
+     *
+     * 用于调整生成回复的随机性和多样性程度:较低的温度值会使输出更收敛于高频词汇,较高的则增加多样性
+     */
+    private Double temperature;
+    /**
+     * 单条回复的最大 Token 数量
+     */
+    private Integer maxTokens;
+    /**
+     * 上下文的最大 Message 数量
+     */
+    private Integer maxContexts;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/ApiConstants.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/ApiConstants.java
new file mode 100644
index 0000000..db17281
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/ApiConstants.java
@@ -0,0 +1,23 @@
+package com.iailab.module.ai.enums;
+
+import com.iailab.framework.common.enums.RpcConstants;
+
+/**
+ * API 相关的枚举
+ *
+ * @author iailab
+ */
+public class ApiConstants {
+
+    /**
+     * 服务名
+     *
+     * 注意,需要保证和 spring.application.name 保持一致
+     */
+    public static final String NAME = "ai-server";
+
+    public static final String PREFIX = RpcConstants.RPC_API_PREFIX +  "/ai";
+
+    public static final String VERSION = "1.0.0";
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/pom.xml b/iailab-module-ai/iailab-module-ai-biz/pom.xml
index a7958b1..395215c 100644
--- a/iailab-module-ai/iailab-module-ai-biz/pom.xml
+++ b/iailab-module-ai/iailab-module-ai-biz/pom.xml
@@ -5,7 +5,7 @@
     <parent>
         <groupId>com.iailab</groupId>
         <artifactId>iailab-module-ai</artifactId>
-        <version>${ai.revision}</version>
+        <version>${revision}</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>iailab-module-ai-biz</artifactId>
@@ -29,7 +29,7 @@
         <dependency>
             <groupId>com.iailab</groupId>
             <artifactId>iailab-module-ai-api</artifactId>
-            <version>${ai.revision}</version>
+            <version>${revision}</version>
         </dependency>
 
         <!-- 业务组件 -->
@@ -114,8 +114,8 @@
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
                 <configuration>
-                    <source>17</source>
-                    <target>17</target>
+                    <source>${java.version}</source>
+                    <target>${java.version}</target>
                 </configuration>
             </plugin>
         </plugins>
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/chat/AiChatConversionApiImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/chat/AiChatConversionApiImpl.java
new file mode 100644
index 0000000..80c3297
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/chat/AiChatConversionApiImpl.java
@@ -0,0 +1,34 @@
+package com.iailab.module.ai.api.chat;
+
+import com.iailab.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateEnergyReqVO;
+import com.iailab.module.ai.dal.dataobject.chat.AiChatConversationDO;
+import com.iailab.module.ai.service.chat.AiChatConversationService;
+import jakarta.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@RestController // 提供 RESTful API 接口,给 Feign 调用
+@Validated
+public class AiChatConversionApiImpl implements AiChatConversationApi {
+
+    @Resource
+    private AiChatConversationService chatConversationService;
+
+    @Override
+    public Long chatEnergyConversation(String modelName) {
+        Long conversationId;
+        List<AiChatConversationDO> list = chatConversationService.getChatConversationList(getLoginUserId(), modelName);
+        if(list.size() == 0) {
+            AiChatConversationCreateEnergyReqVO createReqVO = new AiChatConversationCreateEnergyReqVO();
+            createReqVO.setModelName(modelName);
+            conversationId = chatConversationService.createChatConversationEnergy(createReqVO);
+        } else {
+            conversationId = list.get(0).getId();
+        }
+        return conversationId;
+    }
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/chat/AiChatMessageApiImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/chat/AiChatMessageApiImpl.java
new file mode 100644
index 0000000..80c88af
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/chat/AiChatMessageApiImpl.java
@@ -0,0 +1,27 @@
+package com.iailab.module.ai.api.chat;
+
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.api.chat.dto.AiChatMessageSendRespDTO;
+import com.iailab.module.ai.api.chat.dto.AiChatMessageSendReqDTO;
+import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
+import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
+import com.iailab.module.ai.service.chat.AiChatMessageService;
+import jakarta.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RestController;
+
+import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@RestController // 提供 RESTful API 接口,给 Feign 调用
+@Validated
+public class AiChatMessageApiImpl implements AiChatMessageApi {
+
+    @Resource
+    private AiChatMessageService chatMessageService;
+
+    @Override
+    public AiChatMessageSendRespDTO sendMessage(AiChatMessageSendReqDTO sendReqDTO) {
+        AiChatMessageSendRespVO aiChatMessageSendRespVO = chatMessageService.sendMessage(BeanUtils.toBean(sendReqDTO, AiChatMessageSendReqVO.class), getLoginUserId());
+        return BeanUtils.toBean(aiChatMessageSendRespVO, AiChatMessageSendRespDTO.class);
+    }
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/package-info.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/package-info.java
new file mode 100644
index 0000000..68235f9
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/package-info.java
@@ -0,0 +1 @@
+package com.iailab.module.ai.api;
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatConversationServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatConversationServiceImpl.java
index f2a15e7..bf6e0bf 100644
--- a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatConversationServiceImpl.java
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatConversationServiceImpl.java
@@ -92,7 +92,7 @@
         AiChatConversationDO conversation = new AiChatConversationDO().setUserId(getLoginUserId()).setPinned(false)
                 .setModelId(model.getId()).setModel(model.getModel())
                 .setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts());
-        conversation.setTitle("新对话");
+        conversation.setTitle(createReqVO.getModelName());
         chatConversationMapper.insert(conversation);
         return conversation.getId();
     }
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application.yaml b/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application.yaml
index 90a8245..aaf5b53 100644
--- a/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application.yaml
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application.yaml
@@ -11,6 +11,7 @@
       username: @nacos.username@
       password: @nacos.password@
       discovery: # 【配置中心】配置项
+        ip: @deploy.server@
         namespace: @profiles.active@
         group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
         metadata:
@@ -59,7 +60,7 @@
 
 logging:
   file:
-    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径
+    name: @log.path@/logs/${spring.application.name}.log # 日志文件名,全路径
 
 --- #################### 接口文档配置 ####################
 
@@ -111,7 +112,7 @@
   job:
     executor:
       appname: ${spring.application.name} # 执行器 AppName
-      logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径
+      logpath: @log.path@/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径
     accessToken: default_token # 执行器通讯TOKEN
 
 --- #################### AI 相关配置 ####################
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/pom.xml b/iailab-module-ai/iailab-spring-boot-starter-ai/pom.xml
index ac3bd3e..497658d 100644
--- a/iailab-module-ai/iailab-spring-boot-starter-ai/pom.xml
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/pom.xml
@@ -5,7 +5,7 @@
     <parent>
         <groupId>com.iailab</groupId>
         <artifactId>iailab-module-ai</artifactId>
-        <version>${ai.revision}</version>
+        <version>${revision}</version>
     </parent>
     <build>
         <plugins>
diff --git a/iailab-module-ai/pom.xml b/iailab-module-ai/pom.xml
index 6972f1d..8bb5b06 100644
--- a/iailab-module-ai/pom.xml
+++ b/iailab-module-ai/pom.xml
@@ -10,7 +10,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.iailab</groupId>
     <artifactId>iailab-module-ai</artifactId>
-    <version>${ai.revision}</version>
+    <version>${revision}</version>
     <packaging>pom</packaging>
 
     <modules>
@@ -44,7 +44,7 @@
         <spring.cloud.version>2024.0.0</spring.cloud.version>
         <spring.cloud.alibaba.version>2023.0.3.2</spring.cloud.alibaba.version>
         <!-- Web 相关 -->
-        <springdoc.version>2.7.0</springdoc.version>
+        <springdoc.version>2.8.3</springdoc.version>
         <knife4j.version>4.6.0</knife4j.version>
         <!-- 工具类相关 -->
         <bizlog-sdk.version>3.0.6</bizlog-sdk.version>
diff --git a/iailab-module-data/iailab-module-data-biz/logs/log-debug.2024-11-06.log b/iailab-module-data/iailab-module-data-biz/logs/log-debug.2024-11-06.log
new file mode 100644
index 0000000..16f2e6c
--- /dev/null
+++ b/iailab-module-data/iailab-module-data-biz/logs/log-debug.2024-11-06.log
@@ -0,0 +1,262 @@
+[2024-11-06 09:06:36.026] | [main][INFO ] | [c.i.m.d.c.RabbitMQTest.logStarting,line : 55] Starting RabbitMQTest using Java 1.8.0_401 on Thinkpad-E14 with PID 5916 (started by houzh in D:\Work\Yingdashi\Project\iailab-plat\iailab-plat\iailab-module-data\iailab-module-data-biz)
+[2024-11-06 09:06:36.028] | [main][DEBUG] | [c.i.m.d.c.RabbitMQTest.logStarting,line : 56] Running with Spring Boot v2.7.18, Spring v5.3.31
+[2024-11-06 09:06:36.029] | [main][INFO ] | [c.i.m.d.c.RabbitMQTest.logStartupProfileInfo,line : 638] The following 1 profile is active: "dev"
+[2024-11-06 09:06:36.030] | [main][DEBUG] | [o.s.b.SpringApplication.load,line : 665] Loading source class com.iailab.DataWebApplication
+[2024-11-06 09:06:36.094] | [main][WARN ] | [c.a.c.n.c.NacosConfigDataLoader.logTo,line : 258] [Nacos Config] config[dataId=data-server-dev.yaml, group=DEFAULT_GROUP] is empty
+[2024-11-06 09:06:36.096] | [main][DEBUG] | [o.s.w.c.s.GenericWebApplicationContext.prepareRefresh,line : 637] Refreshing org.springframework.web.context.support.GenericWebApplicationContext@89caf47
+[2024-11-06 09:06:41.325] | [main][INFO ] | [o.s.c.c.s.GenericScope.setSerializationId,line : 283] BeanFactory id=3cae36f0-6484-3ade-8207-23df73e70f40
+[2024-11-06 09:06:42.336] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'com.mzt.logapi.starter.configuration.LogRecordProxyAutoConfiguration' of type [com.mzt.logapi.starter.configuration.LogRecordProxyAutoConfiguration$$EnhancerBySpringCGLIB$$d78165de] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:06:42.361] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'org.springframework.cloud.commons.config.CommonsConfigAutoConfiguration' of type [org.springframework.cloud.commons.config.CommonsConfigAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:06:42.369] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration' of type [org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:06:42.373] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'loadBalancerClientsDefaultsMappingsProvider' of type [org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration$$Lambda$810/1156038763] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:06:42.382] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'defaultsBindHandlerAdvisor' of type [org.springframework.cloud.commons.config.DefaultsBindHandlerAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:06:42.391] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'mzt.log.record-com.mzt.logapi.starter.configuration.LogRecordProperties' of type [com.mzt.logapi.starter.configuration.LogRecordProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:06:42.418] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'logRecordPerformanceMonitor' of type [com.mzt.logapi.service.impl.DefaultLogRecordPerformanceMonitor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:06:42.958] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'com.iailab.framework.datapermission.config.IailabDataPermissionAutoConfiguration' of type [com.iailab.framework.datapermission.config.IailabDataPermissionAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:06:42.984] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'dataPermissionAnnotationAdvisor' of type [com.iailab.framework.datapermission.core.aop.DataPermissionAnnotationAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:06:43.153] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'com.iailab.framework.tenant.config.IailabTenantAutoConfiguration' of type [com.iailab.framework.tenant.config.IailabTenantAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:06:43.262] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'dsProcessor' of type [com.iailab.framework.tenant.core.db.dynamic.TenantDsProcessor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:06:44.667] | [main][INFO ] | [c.a.d.p.DruidDataSource.init,line : 1002] {dataSource-1,master} inited
+[2024-11-06 09:06:44.668] | [main][INFO ] | [c.b.d.d.DynamicRoutingDataSource.addDataSource,line : 158] dynamic-datasource - add a datasource named [master] success
+[2024-11-06 09:06:44.670] | [main][INFO ] | [c.b.d.d.DynamicRoutingDataSource.afterPropertiesSet,line : 241] dynamic-datasource initial loaded [1] datasource,primary datasource named [master]
+[2024-11-06 09:06:49.393] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'infra-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:06:49.568] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:06:51.980] | [main][INFO ] | [o.q.i.StdSchedulerFactory.instantiate,line : 1220] Using default implementation for ThreadExecutor
+[2024-11-06 09:06:52.058] | [main][INFO ] | [o.q.c.SchedulerSignalerImpl.<init>,line : 61] Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
+[2024-11-06 09:06:52.058] | [main][INFO ] | [o.q.c.QuartzScheduler.<init>,line : 229] Quartz Scheduler v.2.3.2 created.
+[2024-11-06 09:06:52.093] | [main][INFO ] | [o.s.s.q.LocalDataSourceJobStore.initialize,line : 672] Using db table-based data access locking (synchronization).
+[2024-11-06 09:06:52.099] | [main][INFO ] | [o.s.s.q.LocalDataSourceJobStore.initialize,line : 145] JobStoreCMT initialized.
+[2024-11-06 09:06:52.101] | [main][INFO ] | [o.q.c.QuartzScheduler.initialize,line : 294] Scheduler meta-data: Quartz Scheduler (v2.3.2) 'IailabDataScheduler' with instanceId 'Thinkpad-E141730855212013'
+  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
+  NOT STARTED.
+  Currently in standby mode.
+  Number of jobs executed: 0
+  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 20 threads.
+  Using job-store 'org.springframework.scheduling.quartz.LocalDataSourceJobStore' - which supports persistence. and is clustered.
+
+[2024-11-06 09:06:52.101] | [main][INFO ] | [o.q.i.StdSchedulerFactory.instantiate,line : 1374] Quartz scheduler 'IailabDataScheduler' initialized from an externally provided properties instance.
+[2024-11-06 09:06:52.102] | [main][INFO ] | [o.q.i.StdSchedulerFactory.instantiate,line : 1378] Quartz scheduler version: 2.3.2
+[2024-11-06 09:06:52.107] | [main][INFO ] | [o.q.c.QuartzScheduler.setJobFactory,line : 2293] JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@26aecf31
+[2024-11-06 09:06:54.172] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'infra-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:06:54.397] | [main][INFO ] | [o.r.Reflections.scan,line : 219] Reflections took 99 ms to scan 1 urls, producing 38 keys and 321 values
+[2024-11-06 09:06:55.214] | [main][DEBUG] | [o.s.b.a.AutoConfigurationPackages.get,line : 196] @EnableAutoConfiguration was declared on a class in the package 'com.iailab'. Automatic @Repository and @Entity scanning is enabled.
+[2024-11-06 09:06:55.698] | [main][DEBUG] | [o.s.w.s.m.m.a.RequestMappingHandlerAdapter.initControllerAdviceCache,line : 625] ControllerAdvice beans: 1 @ModelAttribute, 0 @InitBinder, 1 RequestBodyAdvice, 3 ResponseBodyAdvice
+[2024-11-06 09:06:56.060] | [main][DEBUG] | [o.s.w.s.m.m.a.RequestMappingHandlerMapping.handlerMethodsInitialized,line : 367] 149 mappings in 'requestMappingHandlerMapping'
+[2024-11-06 09:06:56.340] | [main][DEBUG] | [o.s.w.s.h.SimpleUrlHandlerMapping.logMappings,line : 188] Patterns [/webjars/**, /**, /swagger-ui*/*swagger-initializer.js, /swagger-ui*/**] in 'resourceHandlerMapping'
+[2024-11-06 09:06:56.423] | [main][DEBUG] | [o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver.initExceptionHandlerAdviceCache,line : 307] ControllerAdvice beans: 2 @ExceptionHandler, 3 ResponseBodyAdvice
+[2024-11-06 09:06:56.674] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:06:56.721] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:06:58.761] | [main][INFO ] | [org.redisson.Version.logVersion,line : 41] Redisson 3.18.0
+[2024-11-06 09:07:00.360] | [redisson-netty-2-7][INFO ] | [o.r.c.p.MasterPubSubConnectionPool.lambda$createConnection$0,line : 162] 1 connections initialized for 172.16.8.100/172.16.8.100:6379
+[2024-11-06 09:07:00.457] | [redisson-netty-2-19][INFO ] | [o.r.c.p.MasterConnectionPool.lambda$createConnection$0,line : 162] 24 connections initialized for 172.16.8.100/172.16.8.100:6379
+[2024-11-06 09:07:00.651] | [main][INFO ] | [o.s.b.a.e.w.EndpointLinksResolver.<init>,line : 58] Exposing 1 endpoint(s) beneath base path '/actuator'
+[2024-11-06 09:07:00.742] | [main][INFO ] | [o.s.s.w.DefaultSecurityFilterChain.<init>,line : 55] Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@73852c7c, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@4b6fc615, org.springframework.security.web.context.SecurityContextPersistenceFilter@47609e3d, org.springframework.security.web.header.HeaderWriterFilter@28f307ca, org.springframework.web.filter.CorsFilter@70661538, org.springframework.security.web.authentication.logout.LogoutFilter@144d064a, com.iailab.framework.security.core.filter.TokenAuthenticationFilter@575b9b92, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@2a7c82e, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@22128b73, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@de7abc9, org.springframework.security.web.session.SessionManagementFilter@71103416, org.springframework.security.web.access.ExceptionTranslationFilter@1ab64f66, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@3e11cc3a]
+[2024-11-06 09:07:04.051] | [main][INFO ] | [c.f.c.s.SpringContextUtil.setApplicationContext,line : 38] ------SpringContextUtil setApplicationContext-------
+[2024-11-06 09:07:04.371] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'infra-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:07:04.462] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:07:04.478] | [main][INFO ] | [c.i.f.d.c.DictFrameworkUtils.init,line : 73] [init][初始化 DictFrameworkUtils 成功]
+[2024-11-06 09:07:04.507] | [main][INFO ] | [c.i.f.j.c.IailabJacksonAutoConfiguration.jsonUtils,line : 48] [init][初始化 JsonUtils 成功]
+[2024-11-06 09:07:04.649] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:07:05.471] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:07:10.281] | [main][INFO ] | [o.s.s.q.SchedulerFactoryBean.startScheduler,line : 734] Will start Quartz Scheduler [IailabDataScheduler] in 30 seconds
+[2024-11-06 09:07:10.301] | [main][INFO ] | [c.i.m.d.c.RabbitMQTest.logStarted,line : 61] Started RabbitMQTest in 42.46 seconds (JVM running for 44.048)
+[2024-11-06 09:07:10.311] | [main][DEBUG] | [o.s.b.a.ApplicationAvailabilityBean.onApplicationEvent,line : 77] Application availability state LivenessState changed to CORRECT
+[2024-11-06 09:07:10.579] | [main][DEBUG] | [c.i.m.d.j.d.S.selectList.debug,line : 135] ==>  Preparing: SELECT id, bean_name, params, cron_expression, status, remark, updater, update_date, tenant_id, creator, create_date FROM schedule_job
+[2024-11-06 09:07:10.612] | [main][DEBUG] | [c.i.m.d.j.d.S.selectList.debug,line : 135] ==> Parameters: 
+[2024-11-06 09:07:10.643] | [main][DEBUG] | [c.i.m.d.j.d.S.selectList.debug,line : 135] <==      Total: 1
+[2024-11-06 09:07:10.836] | [main][INFO ] | [c.a.c.n.r.NacosContextRefresher.registerNacosListener,line : 141] [Nacos Config] Listening config: dataId=data-server-dev.yaml, group=DEFAULT_GROUP
+[2024-11-06 09:07:10.976] | [main][DEBUG] | [o.s.b.a.ApplicationAvailabilityBean.onApplicationEvent,line : 77] Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
+[2024-11-06 09:07:11.801] | [pool-13-thread-1][INFO ] | [c.i.f.b.c.BannerApplicationRunner.lambda$run$0,line : 22] 
+----------------------------------------------------------
+	项目启动成功!
+	----------------------------------------------------------
+[2024-11-06 09:07:11.849] | [main][INFO ] | [o.s.a.r.c.CachingConnectionFactory.connectAddresses,line : 639] Attempting to connect to: [172.16.8.200:15672]
+[2024-11-06 09:07:16.914] | [Thread-7][WARN ] | [c.a.n.c.n.NotifyCenter.shutdown,line : 136] [NotifyCenter] Start destroying Publisher
+[2024-11-06 09:07:16.915] | [Thread-7][WARN ] | [c.a.n.c.n.NotifyCenter.shutdown,line : 153] [NotifyCenter] Destruction of the end
+[2024-11-06 09:07:16.915] | [Thread-2][WARN ] | [c.a.n.c.h.HttpClientBeanHolder.shutdown,line : 102] [HttpClientBeanHolder] Start destroying common HttpClient
+[2024-11-06 09:07:16.915] | [SpringApplicationShutdownHook][DEBUG] | [o.s.w.c.s.GenericWebApplicationContext.doClose,line : 1060] Closing org.springframework.web.context.support.GenericWebApplicationContext@89caf47, started on Wed Nov 06 09:06:36 CST 2024
+[2024-11-06 09:07:16.916] | [Thread-2][WARN ] | [c.a.n.c.h.HttpClientBeanHolder.shutdown,line : 111] [HttpClientBeanHolder] Destruction of the end
+[2024-11-06 09:07:16.966] | [SpringApplicationShutdownHook][INFO ] | [o.s.s.q.SchedulerFactoryBean.destroy,line : 847] Shutting down Quartz Scheduler
+[2024-11-06 09:07:16.966] | [SpringApplicationShutdownHook][INFO ] | [o.q.c.QuartzScheduler.shutdown,line : 666] Scheduler IailabDataScheduler_$_Thinkpad-E141730855212013 shutting down.
+[2024-11-06 09:07:16.966] | [SpringApplicationShutdownHook][INFO ] | [o.q.c.QuartzScheduler.standby,line : 585] Scheduler IailabDataScheduler_$_Thinkpad-E141730855212013 paused.
+[2024-11-06 09:07:16.966] | [SpringApplicationShutdownHook][INFO ] | [o.q.c.QuartzScheduler.shutdown,line : 740] Scheduler IailabDataScheduler_$_Thinkpad-E141730855212013 shutdown complete.
+[2024-11-06 09:07:16.971] | [SpringApplicationShutdownHook][INFO ] | [c.b.d.d.DynamicRoutingDataSource.destroy,line : 215] dynamic-datasource start closing ....
+[2024-11-06 09:07:16.974] | [SpringApplicationShutdownHook][INFO ] | [c.a.d.p.DruidDataSource.close,line : 2204] {dataSource-1} closing ...
+[2024-11-06 09:07:17.013] | [SpringApplicationShutdownHook][INFO ] | [c.a.d.p.DruidDataSource.close,line : 2277] {dataSource-1} closed
+[2024-11-06 09:07:17.013] | [SpringApplicationShutdownHook][INFO ] | [c.b.d.d.d.DefaultDataSourceDestroyer.destroy,line : 98] dynamic-datasource close the datasource named [master] success,
+[2024-11-06 09:07:17.013] | [SpringApplicationShutdownHook][INFO ] | [c.b.d.d.DynamicRoutingDataSource.destroy,line : 219] dynamic-datasource all closed success,bye
+[2024-11-06 09:08:13.680] | [main][INFO ] | [c.i.m.d.c.RabbitMQTest.logStarting,line : 55] Starting RabbitMQTest using Java 1.8.0_401 on Thinkpad-E14 with PID 25204 (started by houzh in D:\Work\Yingdashi\Project\iailab-plat\iailab-plat\iailab-module-data\iailab-module-data-biz)
+[2024-11-06 09:08:13.683] | [main][DEBUG] | [c.i.m.d.c.RabbitMQTest.logStarting,line : 56] Running with Spring Boot v2.7.18, Spring v5.3.31
+[2024-11-06 09:08:13.684] | [main][INFO ] | [c.i.m.d.c.RabbitMQTest.logStartupProfileInfo,line : 638] The following 1 profile is active: "dev"
+[2024-11-06 09:08:13.685] | [main][DEBUG] | [o.s.b.SpringApplication.load,line : 665] Loading source class com.iailab.DataWebApplication
+[2024-11-06 09:08:13.805] | [main][WARN ] | [c.a.c.n.c.NacosConfigDataLoader.logTo,line : 258] [Nacos Config] config[dataId=data-server-dev.yaml, group=DEFAULT_GROUP] is empty
+[2024-11-06 09:08:13.815] | [main][DEBUG] | [o.s.w.c.s.GenericWebApplicationContext.prepareRefresh,line : 637] Refreshing org.springframework.web.context.support.GenericWebApplicationContext@586495f1
+[2024-11-06 09:08:19.665] | [main][INFO ] | [o.s.c.c.s.GenericScope.setSerializationId,line : 283] BeanFactory id=3cae36f0-6484-3ade-8207-23df73e70f40
+[2024-11-06 09:08:20.707] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'com.mzt.logapi.starter.configuration.LogRecordProxyAutoConfiguration' of type [com.mzt.logapi.starter.configuration.LogRecordProxyAutoConfiguration$$EnhancerBySpringCGLIB$$30a6dd7c] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:08:20.743] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'org.springframework.cloud.commons.config.CommonsConfigAutoConfiguration' of type [org.springframework.cloud.commons.config.CommonsConfigAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:08:20.752] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration' of type [org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:08:20.759] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'loadBalancerClientsDefaultsMappingsProvider' of type [org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration$$Lambda$810/1161998217] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:08:20.771] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'defaultsBindHandlerAdvisor' of type [org.springframework.cloud.commons.config.DefaultsBindHandlerAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:08:20.784] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'mzt.log.record-com.mzt.logapi.starter.configuration.LogRecordProperties' of type [com.mzt.logapi.starter.configuration.LogRecordProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:08:20.826] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'logRecordPerformanceMonitor' of type [com.mzt.logapi.service.impl.DefaultLogRecordPerformanceMonitor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:08:21.401] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'com.iailab.framework.datapermission.config.IailabDataPermissionAutoConfiguration' of type [com.iailab.framework.datapermission.config.IailabDataPermissionAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:08:21.418] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'dataPermissionAnnotationAdvisor' of type [com.iailab.framework.datapermission.core.aop.DataPermissionAnnotationAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:08:21.561] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'com.iailab.framework.tenant.config.IailabTenantAutoConfiguration' of type [com.iailab.framework.tenant.config.IailabTenantAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:08:21.678] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'dsProcessor' of type [com.iailab.framework.tenant.core.db.dynamic.TenantDsProcessor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:08:23.240] | [main][INFO ] | [c.a.d.p.DruidDataSource.init,line : 1002] {dataSource-1,master} inited
+[2024-11-06 09:08:23.242] | [main][INFO ] | [c.b.d.d.DynamicRoutingDataSource.addDataSource,line : 158] dynamic-datasource - add a datasource named [master] success
+[2024-11-06 09:08:23.244] | [main][INFO ] | [c.b.d.d.DynamicRoutingDataSource.afterPropertiesSet,line : 241] dynamic-datasource initial loaded [1] datasource,primary datasource named [master]
+[2024-11-06 09:08:28.721] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'infra-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:08:28.928] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:08:31.564] | [main][INFO ] | [o.q.i.StdSchedulerFactory.instantiate,line : 1220] Using default implementation for ThreadExecutor
+[2024-11-06 09:08:31.595] | [main][INFO ] | [o.q.c.SchedulerSignalerImpl.<init>,line : 61] Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
+[2024-11-06 09:08:31.596] | [main][INFO ] | [o.q.c.QuartzScheduler.<init>,line : 229] Quartz Scheduler v.2.3.2 created.
+[2024-11-06 09:08:31.621] | [main][INFO ] | [o.s.s.q.LocalDataSourceJobStore.initialize,line : 672] Using db table-based data access locking (synchronization).
+[2024-11-06 09:08:31.626] | [main][INFO ] | [o.s.s.q.LocalDataSourceJobStore.initialize,line : 145] JobStoreCMT initialized.
+[2024-11-06 09:08:31.627] | [main][INFO ] | [o.q.c.QuartzScheduler.initialize,line : 294] Scheduler meta-data: Quartz Scheduler (v2.3.2) 'IailabDataScheduler' with instanceId 'Thinkpad-E141730855311568'
+  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
+  NOT STARTED.
+  Currently in standby mode.
+  Number of jobs executed: 0
+  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 20 threads.
+  Using job-store 'org.springframework.scheduling.quartz.LocalDataSourceJobStore' - which supports persistence. and is clustered.
+
+[2024-11-06 09:08:31.627] | [main][INFO ] | [o.q.i.StdSchedulerFactory.instantiate,line : 1374] Quartz scheduler 'IailabDataScheduler' initialized from an externally provided properties instance.
+[2024-11-06 09:08:31.628] | [main][INFO ] | [o.q.i.StdSchedulerFactory.instantiate,line : 1378] Quartz scheduler version: 2.3.2
+[2024-11-06 09:08:31.631] | [main][INFO ] | [o.q.c.QuartzScheduler.setJobFactory,line : 2293] JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@3c51dec5
+[2024-11-06 09:08:33.431] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'infra-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:08:33.588] | [main][INFO ] | [o.r.Reflections.scan,line : 219] Reflections took 86 ms to scan 1 urls, producing 38 keys and 321 values
+[2024-11-06 09:08:34.350] | [main][DEBUG] | [o.s.b.a.AutoConfigurationPackages.get,line : 196] @EnableAutoConfiguration was declared on a class in the package 'com.iailab'. Automatic @Repository and @Entity scanning is enabled.
+[2024-11-06 09:08:34.876] | [main][DEBUG] | [o.s.w.s.m.m.a.RequestMappingHandlerAdapter.initControllerAdviceCache,line : 625] ControllerAdvice beans: 1 @ModelAttribute, 0 @InitBinder, 1 RequestBodyAdvice, 3 ResponseBodyAdvice
+[2024-11-06 09:08:35.264] | [main][DEBUG] | [o.s.w.s.m.m.a.RequestMappingHandlerMapping.handlerMethodsInitialized,line : 367] 149 mappings in 'requestMappingHandlerMapping'
+[2024-11-06 09:08:35.523] | [main][DEBUG] | [o.s.w.s.h.SimpleUrlHandlerMapping.logMappings,line : 188] Patterns [/webjars/**, /**, /swagger-ui*/*swagger-initializer.js, /swagger-ui*/**] in 'resourceHandlerMapping'
+[2024-11-06 09:08:35.577] | [main][DEBUG] | [o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver.initExceptionHandlerAdviceCache,line : 307] ControllerAdvice beans: 2 @ExceptionHandler, 3 ResponseBodyAdvice
+[2024-11-06 09:08:35.726] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:08:35.760] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:08:37.017] | [main][INFO ] | [org.redisson.Version.logVersion,line : 41] Redisson 3.18.0
+[2024-11-06 09:08:38.096] | [redisson-netty-2-7][INFO ] | [o.r.c.p.MasterPubSubConnectionPool.lambda$createConnection$0,line : 162] 1 connections initialized for 172.16.8.100/172.16.8.100:6379
+[2024-11-06 09:08:38.188] | [redisson-netty-2-19][INFO ] | [o.r.c.p.MasterConnectionPool.lambda$createConnection$0,line : 162] 24 connections initialized for 172.16.8.100/172.16.8.100:6379
+[2024-11-06 09:08:38.394] | [main][INFO ] | [o.s.b.a.e.w.EndpointLinksResolver.<init>,line : 58] Exposing 1 endpoint(s) beneath base path '/actuator'
+[2024-11-06 09:08:38.482] | [main][INFO ] | [o.s.s.w.DefaultSecurityFilterChain.<init>,line : 55] Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@650fb50f, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@748321c5, org.springframework.security.web.context.SecurityContextPersistenceFilter@4b7feb38, org.springframework.security.web.header.HeaderWriterFilter@5f967ad3, org.springframework.web.filter.CorsFilter@e0eecbe, org.springframework.security.web.authentication.logout.LogoutFilter@46ae1e6b, com.iailab.framework.security.core.filter.TokenAuthenticationFilter@1fa51751, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@44264fb6, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@90efbba, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@71a7e67, org.springframework.security.web.session.SessionManagementFilter@31829b82, org.springframework.security.web.access.ExceptionTranslationFilter@4bbb00a4, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@721d3a03]
+[2024-11-06 09:08:42.301] | [main][INFO ] | [c.f.c.s.SpringContextUtil.setApplicationContext,line : 38] ------SpringContextUtil setApplicationContext-------
+[2024-11-06 09:08:42.585] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'infra-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:08:42.694] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:08:42.710] | [main][INFO ] | [c.i.f.d.c.DictFrameworkUtils.init,line : 73] [init][初始化 DictFrameworkUtils 成功]
+[2024-11-06 09:08:42.733] | [main][INFO ] | [c.i.f.j.c.IailabJacksonAutoConfiguration.jsonUtils,line : 48] [init][初始化 JsonUtils 成功]
+[2024-11-06 09:08:42.811] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:08:43.238] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:08:45.990] | [main][INFO ] | [o.s.s.q.SchedulerFactoryBean.startScheduler,line : 734] Will start Quartz Scheduler [IailabDataScheduler] in 30 seconds
+[2024-11-06 09:08:46.009] | [main][INFO ] | [c.i.m.d.c.RabbitMQTest.logStarted,line : 61] Started RabbitMQTest in 40.921 seconds (JVM running for 43.575)
+[2024-11-06 09:08:46.015] | [main][DEBUG] | [o.s.b.a.ApplicationAvailabilityBean.onApplicationEvent,line : 77] Application availability state LivenessState changed to CORRECT
+[2024-11-06 09:08:46.200] | [main][DEBUG] | [c.i.m.d.j.d.S.selectList.debug,line : 135] ==>  Preparing: SELECT id, bean_name, params, cron_expression, status, remark, updater, update_date, tenant_id, creator, create_date FROM schedule_job
+[2024-11-06 09:08:46.226] | [main][DEBUG] | [c.i.m.d.j.d.S.selectList.debug,line : 135] ==> Parameters: 
+[2024-11-06 09:08:46.249] | [main][DEBUG] | [c.i.m.d.j.d.S.selectList.debug,line : 135] <==      Total: 1
+[2024-11-06 09:08:46.367] | [main][INFO ] | [c.a.c.n.r.NacosContextRefresher.registerNacosListener,line : 141] [Nacos Config] Listening config: dataId=data-server-dev.yaml, group=DEFAULT_GROUP
+[2024-11-06 09:08:46.503] | [main][DEBUG] | [o.s.b.a.ApplicationAvailabilityBean.onApplicationEvent,line : 77] Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
+[2024-11-06 09:08:47.242] | [main][INFO ] | [o.s.a.r.c.CachingConnectionFactory.connectAddresses,line : 639] Attempting to connect to: [127.0.0.1:15672]
+[2024-11-06 09:08:47.343] | [pool-13-thread-1][INFO ] | [c.i.f.b.c.BannerApplicationRunner.lambda$run$0,line : 22] 
+----------------------------------------------------------
+	项目启动成功!
+	----------------------------------------------------------
+[2024-11-06 09:08:52.294] | [Thread-7][WARN ] | [c.a.n.c.n.NotifyCenter.shutdown,line : 136] [NotifyCenter] Start destroying Publisher
+[2024-11-06 09:08:52.295] | [Thread-2][WARN ] | [c.a.n.c.h.HttpClientBeanHolder.shutdown,line : 102] [HttpClientBeanHolder] Start destroying common HttpClient
+[2024-11-06 09:08:52.295] | [Thread-7][WARN ] | [c.a.n.c.n.NotifyCenter.shutdown,line : 153] [NotifyCenter] Destruction of the end
+[2024-11-06 09:08:52.296] | [SpringApplicationShutdownHook][DEBUG] | [o.s.w.c.s.GenericWebApplicationContext.doClose,line : 1060] Closing org.springframework.web.context.support.GenericWebApplicationContext@586495f1, started on Wed Nov 06 09:08:13 CST 2024
+[2024-11-06 09:08:52.296] | [Thread-2][WARN ] | [c.a.n.c.h.HttpClientBeanHolder.shutdown,line : 111] [HttpClientBeanHolder] Destruction of the end
+[2024-11-06 09:08:52.355] | [SpringApplicationShutdownHook][INFO ] | [o.s.s.q.SchedulerFactoryBean.destroy,line : 847] Shutting down Quartz Scheduler
+[2024-11-06 09:08:52.355] | [SpringApplicationShutdownHook][INFO ] | [o.q.c.QuartzScheduler.shutdown,line : 666] Scheduler IailabDataScheduler_$_Thinkpad-E141730855311568 shutting down.
+[2024-11-06 09:08:52.355] | [SpringApplicationShutdownHook][INFO ] | [o.q.c.QuartzScheduler.standby,line : 585] Scheduler IailabDataScheduler_$_Thinkpad-E141730855311568 paused.
+[2024-11-06 09:08:52.355] | [SpringApplicationShutdownHook][INFO ] | [o.q.c.QuartzScheduler.shutdown,line : 740] Scheduler IailabDataScheduler_$_Thinkpad-E141730855311568 shutdown complete.
+[2024-11-06 09:08:52.359] | [SpringApplicationShutdownHook][INFO ] | [c.b.d.d.DynamicRoutingDataSource.destroy,line : 215] dynamic-datasource start closing ....
+[2024-11-06 09:08:52.363] | [SpringApplicationShutdownHook][INFO ] | [c.a.d.p.DruidDataSource.close,line : 2204] {dataSource-1} closing ...
+[2024-11-06 09:08:52.370] | [SpringApplicationShutdownHook][INFO ] | [c.a.d.p.DruidDataSource.close,line : 2277] {dataSource-1} closed
+[2024-11-06 09:08:52.371] | [SpringApplicationShutdownHook][INFO ] | [c.b.d.d.d.DefaultDataSourceDestroyer.destroy,line : 98] dynamic-datasource close the datasource named [master] success,
+[2024-11-06 09:08:52.371] | [SpringApplicationShutdownHook][INFO ] | [c.b.d.d.DynamicRoutingDataSource.destroy,line : 219] dynamic-datasource all closed success,bye
+[2024-11-06 09:11:44.044] | [main][INFO ] | [c.i.m.d.c.RabbitMQTest.logStarting,line : 55] Starting RabbitMQTest using Java 1.8.0_401 on Thinkpad-E14 with PID 19404 (started by houzh in D:\Work\Yingdashi\Project\iailab-plat\iailab-plat\iailab-module-data\iailab-module-data-biz)
+[2024-11-06 09:11:44.047] | [main][DEBUG] | [c.i.m.d.c.RabbitMQTest.logStarting,line : 56] Running with Spring Boot v2.7.18, Spring v5.3.31
+[2024-11-06 09:11:44.047] | [main][INFO ] | [c.i.m.d.c.RabbitMQTest.logStartupProfileInfo,line : 638] The following 1 profile is active: "dev"
+[2024-11-06 09:11:44.048] | [main][DEBUG] | [o.s.b.SpringApplication.load,line : 665] Loading source class com.iailab.DataWebApplication
+[2024-11-06 09:11:44.095] | [main][WARN ] | [c.a.c.n.c.NacosConfigDataLoader.logTo,line : 258] [Nacos Config] config[dataId=data-server-dev.yaml, group=DEFAULT_GROUP] is empty
+[2024-11-06 09:11:44.099] | [main][DEBUG] | [o.s.w.c.s.GenericWebApplicationContext.prepareRefresh,line : 637] Refreshing org.springframework.web.context.support.GenericWebApplicationContext@5b29ab61
+[2024-11-06 09:11:48.038] | [main][INFO ] | [o.s.c.c.s.GenericScope.setSerializationId,line : 283] BeanFactory id=3cae36f0-6484-3ade-8207-23df73e70f40
+[2024-11-06 09:11:48.793] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'com.mzt.logapi.starter.configuration.LogRecordProxyAutoConfiguration' of type [com.mzt.logapi.starter.configuration.LogRecordProxyAutoConfiguration$$EnhancerBySpringCGLIB$$a1f2c9f4] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:11:48.814] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'org.springframework.cloud.commons.config.CommonsConfigAutoConfiguration' of type [org.springframework.cloud.commons.config.CommonsConfigAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:11:48.819] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration' of type [org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:11:48.822] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'loadBalancerClientsDefaultsMappingsProvider' of type [org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration$$Lambda$810/1079430578] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:11:48.830] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'defaultsBindHandlerAdvisor' of type [org.springframework.cloud.commons.config.DefaultsBindHandlerAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:11:48.838] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'mzt.log.record-com.mzt.logapi.starter.configuration.LogRecordProperties' of type [com.mzt.logapi.starter.configuration.LogRecordProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:11:48.862] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'logRecordPerformanceMonitor' of type [com.mzt.logapi.service.impl.DefaultLogRecordPerformanceMonitor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:11:49.304] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'com.iailab.framework.datapermission.config.IailabDataPermissionAutoConfiguration' of type [com.iailab.framework.datapermission.config.IailabDataPermissionAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:11:49.318] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'dataPermissionAnnotationAdvisor' of type [com.iailab.framework.datapermission.core.aop.DataPermissionAnnotationAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:11:49.421] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'com.iailab.framework.tenant.config.IailabTenantAutoConfiguration' of type [com.iailab.framework.tenant.config.IailabTenantAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:11:49.484] | [main][INFO ] | [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization,line : 376] Bean 'dsProcessor' of type [com.iailab.framework.tenant.core.db.dynamic.TenantDsProcessor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+[2024-11-06 09:11:50.714] | [main][INFO ] | [c.a.d.p.DruidDataSource.init,line : 1002] {dataSource-1,master} inited
+[2024-11-06 09:11:50.715] | [main][INFO ] | [c.b.d.d.DynamicRoutingDataSource.addDataSource,line : 158] dynamic-datasource - add a datasource named [master] success
+[2024-11-06 09:11:50.718] | [main][INFO ] | [c.b.d.d.DynamicRoutingDataSource.afterPropertiesSet,line : 241] dynamic-datasource initial loaded [1] datasource,primary datasource named [master]
+[2024-11-06 09:11:55.346] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'infra-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:11:55.551] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:11:57.524] | [main][INFO ] | [o.q.i.StdSchedulerFactory.instantiate,line : 1220] Using default implementation for ThreadExecutor
+[2024-11-06 09:11:57.556] | [main][INFO ] | [o.q.c.SchedulerSignalerImpl.<init>,line : 61] Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
+[2024-11-06 09:11:57.556] | [main][INFO ] | [o.q.c.QuartzScheduler.<init>,line : 229] Quartz Scheduler v.2.3.2 created.
+[2024-11-06 09:11:57.580] | [main][INFO ] | [o.s.s.q.LocalDataSourceJobStore.initialize,line : 672] Using db table-based data access locking (synchronization).
+[2024-11-06 09:11:57.584] | [main][INFO ] | [o.s.s.q.LocalDataSourceJobStore.initialize,line : 145] JobStoreCMT initialized.
+[2024-11-06 09:11:57.586] | [main][INFO ] | [o.q.c.QuartzScheduler.initialize,line : 294] Scheduler meta-data: Quartz Scheduler (v2.3.2) 'IailabDataScheduler' with instanceId 'Thinkpad-E141730855517527'
+  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
+  NOT STARTED.
+  Currently in standby mode.
+  Number of jobs executed: 0
+  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 20 threads.
+  Using job-store 'org.springframework.scheduling.quartz.LocalDataSourceJobStore' - which supports persistence. and is clustered.
+
+[2024-11-06 09:11:57.586] | [main][INFO ] | [o.q.i.StdSchedulerFactory.instantiate,line : 1374] Quartz scheduler 'IailabDataScheduler' initialized from an externally provided properties instance.
+[2024-11-06 09:11:57.587] | [main][INFO ] | [o.q.i.StdSchedulerFactory.instantiate,line : 1378] Quartz scheduler version: 2.3.2
+[2024-11-06 09:11:57.589] | [main][INFO ] | [o.q.c.QuartzScheduler.setJobFactory,line : 2293] JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@6b989889
+[2024-11-06 09:11:59.615] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'infra-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:11:59.767] | [main][INFO ] | [o.r.Reflections.scan,line : 219] Reflections took 86 ms to scan 1 urls, producing 38 keys and 321 values
+[2024-11-06 09:12:00.450] | [main][DEBUG] | [o.s.b.a.AutoConfigurationPackages.get,line : 196] @EnableAutoConfiguration was declared on a class in the package 'com.iailab'. Automatic @Repository and @Entity scanning is enabled.
+[2024-11-06 09:12:00.848] | [main][DEBUG] | [o.s.w.s.m.m.a.RequestMappingHandlerAdapter.initControllerAdviceCache,line : 625] ControllerAdvice beans: 1 @ModelAttribute, 0 @InitBinder, 1 RequestBodyAdvice, 3 ResponseBodyAdvice
+[2024-11-06 09:12:01.253] | [main][DEBUG] | [o.s.w.s.m.m.a.RequestMappingHandlerMapping.handlerMethodsInitialized,line : 367] 149 mappings in 'requestMappingHandlerMapping'
+[2024-11-06 09:12:01.527] | [main][DEBUG] | [o.s.w.s.h.SimpleUrlHandlerMapping.logMappings,line : 188] Patterns [/webjars/**, /**, /swagger-ui*/*swagger-initializer.js, /swagger-ui*/**] in 'resourceHandlerMapping'
+[2024-11-06 09:12:01.604] | [main][DEBUG] | [o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver.initExceptionHandlerAdviceCache,line : 307] ControllerAdvice beans: 2 @ExceptionHandler, 3 ResponseBodyAdvice
+[2024-11-06 09:12:01.785] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:12:01.812] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:12:03.041] | [main][INFO ] | [org.redisson.Version.logVersion,line : 41] Redisson 3.18.0
+[2024-11-06 09:12:04.080] | [redisson-netty-2-7][INFO ] | [o.r.c.p.MasterPubSubConnectionPool.lambda$createConnection$0,line : 162] 1 connections initialized for 172.16.8.100/172.16.8.100:6379
+[2024-11-06 09:12:04.196] | [redisson-netty-2-19][INFO ] | [o.r.c.p.MasterConnectionPool.lambda$createConnection$0,line : 162] 24 connections initialized for 172.16.8.100/172.16.8.100:6379
+[2024-11-06 09:12:04.452] | [main][INFO ] | [o.s.b.a.e.w.EndpointLinksResolver.<init>,line : 58] Exposing 1 endpoint(s) beneath base path '/actuator'
+[2024-11-06 09:12:04.571] | [main][INFO ] | [o.s.s.w.DefaultSecurityFilterChain.<init>,line : 55] Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@503f5382, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@5710fca8, org.springframework.security.web.context.SecurityContextPersistenceFilter@50df710a, org.springframework.security.web.header.HeaderWriterFilter@23038621, org.springframework.web.filter.CorsFilter@7e2e1179, org.springframework.security.web.authentication.logout.LogoutFilter@6b4b6cd6, com.iailab.framework.security.core.filter.TokenAuthenticationFilter@19e7aa5b, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@3e64cf62, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@7768ffae, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@336e59a6, org.springframework.security.web.session.SessionManagementFilter@71b253b, org.springframework.security.web.access.ExceptionTranslationFilter@1cd25514, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@4d682552]
+[2024-11-06 09:12:07.460] | [main][INFO ] | [c.f.c.s.SpringContextUtil.setApplicationContext,line : 38] ------SpringContextUtil setApplicationContext-------
+[2024-11-06 09:12:07.753] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'infra-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:12:07.809] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:12:07.824] | [main][INFO ] | [c.i.f.d.c.DictFrameworkUtils.init,line : 73] [init][初始化 DictFrameworkUtils 成功]
+[2024-11-06 09:12:07.859] | [main][INFO ] | [c.i.f.j.c.IailabJacksonAutoConfiguration.jsonUtils,line : 48] [init][初始化 JsonUtils 成功]
+[2024-11-06 09:12:07.917] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:12:08.316] | [main][INFO ] | [o.s.c.o.FeignClientFactoryBean.getTarget,line : 418] For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+[2024-11-06 09:12:12.183] | [main][INFO ] | [o.s.s.q.SchedulerFactoryBean.startScheduler,line : 734] Will start Quartz Scheduler [IailabDataScheduler] in 30 seconds
+[2024-11-06 09:12:12.200] | [main][INFO ] | [c.i.m.d.c.RabbitMQTest.logStarted,line : 61] Started RabbitMQTest in 34.676 seconds (JVM running for 36.159)
+[2024-11-06 09:12:12.208] | [main][DEBUG] | [o.s.b.a.ApplicationAvailabilityBean.onApplicationEvent,line : 77] Application availability state LivenessState changed to CORRECT
+[2024-11-06 09:12:12.403] | [main][DEBUG] | [c.i.m.d.j.d.S.selectList.debug,line : 135] ==>  Preparing: SELECT id, bean_name, params, cron_expression, status, remark, updater, update_date, tenant_id, creator, create_date FROM schedule_job
+[2024-11-06 09:12:12.433] | [main][DEBUG] | [c.i.m.d.j.d.S.selectList.debug,line : 135] ==> Parameters: 
+[2024-11-06 09:12:12.473] | [main][DEBUG] | [c.i.m.d.j.d.S.selectList.debug,line : 135] <==      Total: 1
+[2024-11-06 09:12:12.611] | [main][INFO ] | [c.a.c.n.r.NacosContextRefresher.registerNacosListener,line : 141] [Nacos Config] Listening config: dataId=data-server-dev.yaml, group=DEFAULT_GROUP
+[2024-11-06 09:12:12.703] | [main][DEBUG] | [o.s.b.a.ApplicationAvailabilityBean.onApplicationEvent,line : 77] Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
+[2024-11-06 09:12:13.427] | [main][INFO ] | [o.s.a.r.c.CachingConnectionFactory.connectAddresses,line : 639] Attempting to connect to: [127.0.0.1:5672]
+[2024-11-06 09:12:13.584] | [pool-13-thread-1][INFO ] | [c.i.f.b.c.BannerApplicationRunner.lambda$run$0,line : 22] 
+----------------------------------------------------------
+	项目启动成功!
+	----------------------------------------------------------
+[2024-11-06 09:12:13.589] | [main][INFO ] | [o.s.a.r.c.CachingConnectionFactory.createBareConnection,line : 590] Created new connection: rabbitConnectionFactory#3e37c38f:0/SimpleConnection@3023a901 [delegate=amqp://guest@127.0.0.1:5672/, localPort= 60360]
+[2024-11-06 09:12:13.767] | [Thread-2][WARN ] | [c.a.n.c.h.HttpClientBeanHolder.shutdown,line : 102] [HttpClientBeanHolder] Start destroying common HttpClient
+[2024-11-06 09:12:13.767] | [Thread-7][WARN ] | [c.a.n.c.n.NotifyCenter.shutdown,line : 136] [NotifyCenter] Start destroying Publisher
+[2024-11-06 09:12:13.768] | [Thread-7][WARN ] | [c.a.n.c.n.NotifyCenter.shutdown,line : 153] [NotifyCenter] Destruction of the end
+[2024-11-06 09:12:13.768] | [SpringApplicationShutdownHook][DEBUG] | [o.s.w.c.s.GenericWebApplicationContext.doClose,line : 1060] Closing org.springframework.web.context.support.GenericWebApplicationContext@5b29ab61, started on Wed Nov 06 09:11:44 CST 2024
+[2024-11-06 09:12:13.769] | [Thread-2][WARN ] | [c.a.n.c.h.HttpClientBeanHolder.shutdown,line : 111] [HttpClientBeanHolder] Destruction of the end
+[2024-11-06 09:12:13.850] | [SpringApplicationShutdownHook][INFO ] | [o.s.s.q.SchedulerFactoryBean.destroy,line : 847] Shutting down Quartz Scheduler
+[2024-11-06 09:12:13.851] | [SpringApplicationShutdownHook][INFO ] | [o.q.c.QuartzScheduler.shutdown,line : 666] Scheduler IailabDataScheduler_$_Thinkpad-E141730855517527 shutting down.
+[2024-11-06 09:12:13.851] | [SpringApplicationShutdownHook][INFO ] | [o.q.c.QuartzScheduler.standby,line : 585] Scheduler IailabDataScheduler_$_Thinkpad-E141730855517527 paused.
+[2024-11-06 09:12:13.852] | [SpringApplicationShutdownHook][INFO ] | [o.q.c.QuartzScheduler.shutdown,line : 740] Scheduler IailabDataScheduler_$_Thinkpad-E141730855517527 shutdown complete.
+[2024-11-06 09:12:13.858] | [SpringApplicationShutdownHook][INFO ] | [c.b.d.d.DynamicRoutingDataSource.destroy,line : 215] dynamic-datasource start closing ....
+[2024-11-06 09:12:13.864] | [SpringApplicationShutdownHook][INFO ] | [c.a.d.p.DruidDataSource.close,line : 2204] {dataSource-1} closing ...
+[2024-11-06 09:12:13.876] | [SpringApplicationShutdownHook][INFO ] | [c.a.d.p.DruidDataSource.close,line : 2277] {dataSource-1} closed
+[2024-11-06 09:12:13.876] | [SpringApplicationShutdownHook][INFO ] | [c.b.d.d.d.DefaultDataSourceDestroyer.destroy,line : 98] dynamic-datasource close the datasource named [master] success,
+[2024-11-06 09:12:13.876] | [SpringApplicationShutdownHook][INFO ] | [c.b.d.d.DynamicRoutingDataSource.destroy,line : 219] dynamic-datasource all closed success,bye
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/resources/application-prod.yaml b/iailab-module-data/iailab-module-data-biz/src/main/resources/application-prod.yaml
new file mode 100644
index 0000000..da39151
--- /dev/null
+++ b/iailab-module-data/iailab-module-data-biz/src/main/resources/application-prod.yaml
@@ -0,0 +1,127 @@
+spring:
+  autoconfigure:
+    exclude:
+      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
+      - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration
+  datasource:
+    druid:
+      web-stat-filter:
+        enabled: true
+      stat-view-servlet:
+        enabled: true
+        allow:
+        url-pattern: /druid/*
+        login-username:
+        login-password:
+      filter:
+        stat:
+          enabled: true
+          log-slow-sql: true
+          slow-sql-millis: 100
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
+    dynamic:
+      druid:
+        initial-size: 1
+        min-idle: 1
+        max-active: 20
+        max-wait: 600000
+        time-between-eviction-runs-millis: 60000
+        min-evictable-idle-time-millis: 300000
+        max-evictable-idle-time-millis: 900000
+        validation-query: select version()
+        test-while-idle: true
+        test-on-borrow: false
+        test-on-return: false
+      primary: master
+      datasource:
+        master:
+          url: jdbc:mysql://localhost:3306/iailab_expert_master?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+          username: root
+          password: 123456
+
+  redis:
+    host: 127.0.0.1
+    port: 6379
+    database: 0
+    password: 123456
+
+  rabbitmq:
+    host: 172.16.1.221
+    port: 5672
+    username: admin
+    password: admin123
+
+  boot:
+    admin:
+      client:
+        instance:
+          service-host-type: IP
+
+rocketmq:
+  name-server: 172.16.8.100:9876 # RocketMQ Namesrv
+
+xxl:
+  job:
+    enabled: true
+    admin:
+      addresses: http://172.16.8.100:9090/xxl-job-admin
+
+lock4j:
+  acquire-timeout: 3000
+  expire: 30000
+
+management:
+  endpoints:
+    web:
+      base-path: /actuator
+      exposure:
+        include: '*'
+
+logging:
+  level:
+    com.iailab.module.system.dal.mysql: debug
+    com.iailab.module.system.dal.mysql.sensitiveword.SensitiveWordMapper: INFO
+    com.iailab.module.system.dal.mysql.sms.SmsChannelMapper: INFO
+
+iailab:
+  env:
+    tag: ${HOSTNAME}
+  captcha:
+    enable: false
+  security:
+    mock-enable: true
+  xss:
+    enable: false
+    exclude-urls:
+      - ${spring.boot.admin.context-path}/**
+      - ${management.endpoints.web.base-path}/**
+  access-log:
+    enable: false
+  demo: false
+
+influx-db:
+  org: iailab
+  token: _338h4Kbu2KQaes5QwAyOz9pTUueXoSF9XmPi8N9oTS1SrhTZVj4J9JfSraUyWA0PfWMZOlf9QWax-USkJQR_A==
+  url: http://172.16.1.221:8086
+  username: iailab
+  password: iailab2019
+
+iems:
+  upload-dir: D:/DLUT/upload/
+
+video:
+  capture-dir: D:/iailab
+  dahua:
+    path:
+      capture-path: /Dahua/Capture/
+      model-path: /Dahua/Model/
+  hikvision:
+    path:
+      capture-path: /Hikvision/Capture/
+      model-path: /Hikvision/Model/
+    pic-size: 2 # size of picture 0=CIF, 1=QCIF, 2=D1 3=UXGA(1600x1200), 4=SVGA(800x600), 5=HD720p(1280x720),6=VGA
+    pic-quality: 0 # quality of picture 0-best 1-better 2-normal
+
diff --git a/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/logger/ApiAccessLogApi.java b/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/logger/ApiAccessLogApi.java
index 599cc76..f7e510a 100644
--- a/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/logger/ApiAccessLogApi.java
+++ b/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/logger/ApiAccessLogApi.java
@@ -6,6 +6,7 @@
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
 import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 
@@ -21,4 +22,13 @@
     @Operation(summary = "创建 API 访问日志")
     CommonResult<Boolean> createApiAccessLog(@Valid @RequestBody ApiAccessLogCreateReqDTO createDTO);
 
+    /**
+     * 【异步】创建 API 访问日志
+     *
+     * @param createDTO 访问日志 DTO
+     */
+    @Async
+    default void createApiAccessLogAsync(ApiAccessLogCreateReqDTO createDTO) {
+        createApiAccessLog(createDTO).checkError();
+    }
 }
diff --git a/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/logger/ApiErrorLogApi.java b/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/logger/ApiErrorLogApi.java
index c4db73d..0b961fc 100644
--- a/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/logger/ApiErrorLogApi.java
+++ b/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/logger/ApiErrorLogApi.java
@@ -6,6 +6,7 @@
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
 import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 
@@ -21,4 +22,14 @@
     @Operation(summary = "创建 API 异常日志")
     CommonResult<Boolean> createApiErrorLog(@Valid @RequestBody ApiErrorLogCreateReqDTO createDTO);
 
+    /**
+     * 【异步】创建 API 异常日志
+     *
+     * @param createDTO 异常日志 DTO
+     */
+    @Async
+    default void createApiErrorLogAsync(ApiErrorLogCreateReqDTO createDTO) {
+        createApiErrorLog(createDTO).checkError();
+    }
+
 }
diff --git a/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/monitor/MonitorApi.java b/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/monitor/MonitorApi.java
new file mode 100644
index 0000000..f39fd52
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/monitor/MonitorApi.java
@@ -0,0 +1,42 @@
+package com.iailab.module.infra.api.monitor;
+
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.module.infra.api.monitor.dto.MonitorDiskDTO;
+import com.iailab.module.infra.api.monitor.dto.MonitorMemDTO;
+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.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import javax.validation.Valid;
+import java.util.List;
+
+@FeignClient(name = ApiConstants.NAME)
+@Tag(name = "RPC 服务 - 磁盘监控")
+public interface MonitorApi {
+
+    String PREFIX = ApiConstants.PREFIX + "/monitor";
+
+    /**
+     * 保存内存信息日志
+     *
+     * @param reportMemReqDTO 内存日志对象
+     * @return 文件路径
+     */
+    @PostMapping(PREFIX + "/reportMemInfo")
+    @Operation(summary = "保存内存日志对象")
+    CommonResult<Long> reportMemInfo(@Valid @RequestBody MonitorMemDTO reportMemReqDTO);
+
+    /**
+     * 保存磁盘信息日志
+     *
+     * @param reportMemReqDTOS 磁盘日志对象
+     * @return 文件路径
+     */
+    @PostMapping(PREFIX + "/reportDiskInfo")
+    @Operation(summary = "保存磁盘日志对象")
+    CommonResult reportDiskInfo(@Valid @RequestBody List<MonitorDiskDTO> reportMemReqDTOS);
+
+}
diff --git a/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/monitor/dto/MonitorDiskDTO.java b/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/monitor/dto/MonitorDiskDTO.java
new file mode 100644
index 0000000..6f42a20
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/monitor/dto/MonitorDiskDTO.java
@@ -0,0 +1,55 @@
+package com.iailab.module.infra.api.monitor.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+import java.math.BigDecimal;
+
+/**
+ * 磁盘监控日志 DTO
+ *
+ * @author 超级管理员
+ */
+@Schema(description = "RPC 服务 - 服务监控-磁盘监控 Request DTO")
+@Data
+public class MonitorDiskDTO {
+
+    /**
+     * 主机名称
+     */
+    private String hostName;
+    /**
+     * 服务器ip
+     */
+    private String hostIp;
+    /**
+     * 盘符
+     */
+    private String disk;
+    /**
+     * 磁盘名
+     */
+    private String diskName;
+    /**
+     * 总空间
+     */
+    private BigDecimal spaceTotal;
+    /**
+     * 已用空间
+     */
+    private BigDecimal spaceUsed;
+    /**
+     * 可用空间
+     */
+    private BigDecimal spaceUsable;
+    /**
+     * 空间使用比例
+     */
+    private BigDecimal spaceRatio;
+
+    /**
+     * 租户Id
+     */
+    private Integer tenantId;
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/monitor/dto/MonitorMemDTO.java b/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/monitor/dto/MonitorMemDTO.java
new file mode 100644
index 0000000..cfb1ca0
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/api/monitor/dto/MonitorMemDTO.java
@@ -0,0 +1,79 @@
+package com.iailab.module.infra.api.monitor.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+import java.math.BigDecimal;
+
+/**
+ * 内存监控日志 DO
+ *
+ * @author 超级管理员
+ */
+@Schema(description = "RPC 服务 - 服务监控-内存监控 Request DTO")
+@Data
+public class MonitorMemDTO {
+
+    /**
+     * 主机名称
+     */
+    private String hostName;
+    /**
+     * 服务器ip
+     */
+    private String hostIp;
+    /**
+     * 服务名
+     */
+    private String serverName;
+    /**
+     * 总物理内存
+     */
+    private BigDecimal physicalTotal;
+    /**
+     * 已用物理内存
+     */
+    private BigDecimal physicalUsed;
+    /**
+     * 剩余物理内存
+     */
+    private BigDecimal physicalFree;
+    /**
+     * 物理内存使用率
+     */
+    private BigDecimal physicalUsage;
+    /**
+     * jvm运行总内存
+     */
+    private BigDecimal runtimeTotal;
+    /**
+     * jvm最大内存
+     */
+    private BigDecimal runtimeMax;
+    /**
+     * jvm已用内存
+     */
+    private BigDecimal runtimeUsed;
+    /**
+     * jvm空闲内存
+     */
+    private BigDecimal runtimeFree;
+    /**
+     * jvm内存使用率
+     */
+    private BigDecimal runtimeUsage;
+    /**
+     * 系统cpu利用率
+     */
+    private BigDecimal systemCpuLoad;
+    /**
+     * 进程cpu利用率
+     */
+    private BigDecimal processCpuLoad;
+
+    /**
+     * 租户Id
+     */
+    private Integer tenantId;
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/util/ServerInfoCollector.java b/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/util/ServerInfoCollector.java
new file mode 100644
index 0000000..b0a4638
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-api/src/main/java/com/iailab/module/infra/util/ServerInfoCollector.java
@@ -0,0 +1,198 @@
+package com.iailab.module.infra.util;
+
+import cn.hutool.core.util.NumberUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.iailab.module.infra.api.monitor.dto.MonitorDiskDTO;
+import com.iailab.module.infra.api.monitor.dto.MonitorMemDTO;
+import org.apache.commons.lang3.ObjectUtils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.management.ManagementFactory;
+import java.lang.management.OperatingSystemMXBean;
+import java.math.BigDecimal;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystems;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ServerInfoCollector {
+
+    private static BigDecimal UNIT = new BigDecimal(1024 * 1024);
+
+    private static BigDecimal PERCENT_UNIT = new BigDecimal(100);
+
+    private static String hostName;
+
+    private static String hostIp;
+
+    public static void initServerInfo() throws UnknownHostException {
+        // 获取本地主机对象
+        InetAddress localHost = InetAddress.getLocalHost();
+        // 获取主机名
+        hostName = localHost.getHostName();
+//        // 获取IP地址
+        hostIp = localHost.getHostAddress();
+//        try {
+//            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+//            while (interfaces.hasMoreElements()) {
+//                NetworkInterface networkInterface = interfaces.nextElement();
+//                // 过滤掉loopback接口
+//                if (networkInterface.isLoopback() || !networkInterface.isUp()) {
+//                    continue;
+//                }
+//                Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
+//                while (addresses.hasMoreElements()) {
+//                    InetAddress addr = addresses.nextElement();
+//                    // 过滤掉IPv6地址和回环地址
+//                    if (!addr.isLoopbackAddress() && addr.getHostAddress().contains(".")) { // 确保是IPv4地址
+//                        System.out.println("局域网IP地址: " + addr.getHostAddress());
+//                        hostIp = addr.getHostAddress();
+//                    }
+//                }
+//            }
+//        } catch (SocketException e) {
+//            e.printStackTrace();
+//        }
+    }
+
+    /**
+     * 内存信息处理
+     */
+    public static MonitorMemDTO collectMonitorMem(String serverName) throws UnknownHostException {
+        initServerInfo();
+        OperatingSystemMXBean operatingSystemBean = ManagementFactory.getOperatingSystemMXBean();
+        JSONObject operatingSystemJson = JSON.parseObject(JSON.toJSONString(operatingSystemBean));
+        BigDecimal totalPhysicalMemory = operatingSystemJson.getBigDecimal("totalPhysicalMemorySize");
+        BigDecimal freePhysicalMemory = operatingSystemJson.getBigDecimal("freePhysicalMemorySize");
+        BigDecimal usedPhysicalMemory = totalPhysicalMemory.subtract(freePhysicalMemory);
+        BigDecimal systemCpuLoad = operatingSystemJson.getBigDecimal("systemCpuLoad");
+        BigDecimal processCpuLoad = operatingSystemJson.getBigDecimal("processCpuLoad");
+        Runtime runtime = Runtime.getRuntime();
+        MonitorMemDTO monitorMemDTO = new MonitorMemDTO();
+        monitorMemDTO.setHostName(hostName)
+                .setHostIp(hostIp)
+                .setServerName(serverName)
+                .setPhysicalTotal(totalPhysicalMemory.divide(UNIT).setScale(2, BigDecimal.ROUND_HALF_UP))
+                .setPhysicalUsed(usedPhysicalMemory.divide(UNIT).setScale(2, BigDecimal.ROUND_HALF_UP))
+                .setPhysicalFree(freePhysicalMemory.divide(UNIT).setScale(2, BigDecimal.ROUND_HALF_UP))
+                .setPhysicalUsage(NumberUtil.div(usedPhysicalMemory, totalPhysicalMemory).setScale(2, BigDecimal.ROUND_HALF_UP))
+                .setRuntimeTotal(new BigDecimal(runtime.totalMemory()).divide(UNIT).setScale(2, BigDecimal.ROUND_HALF_UP))
+                .setRuntimeFree(new BigDecimal(runtime.freeMemory()).divide(UNIT).setScale(2, BigDecimal.ROUND_HALF_UP))
+                .setRuntimeMax(new BigDecimal(runtime.maxMemory()).divide(UNIT).setScale(2, BigDecimal.ROUND_HALF_UP))
+                .setRuntimeUsed(new BigDecimal(runtime.maxMemory() - runtime.totalMemory() + runtime.freeMemory()).divide(UNIT).setScale(2, BigDecimal.ROUND_HALF_UP))
+                .setRuntimeUsage(new BigDecimal(NumberUtil.div(runtime.totalMemory()/1f - runtime.freeMemory(), runtime.totalMemory())).setScale(2, BigDecimal.ROUND_HALF_UP))
+                .setSystemCpuLoad(systemCpuLoad.multiply(PERCENT_UNIT))
+                .setProcessCpuLoad(processCpuLoad.multiply(PERCENT_UNIT));
+        return monitorMemDTO;
+    }
+
+    /**
+     * 磁盘信息处理
+     */
+    public static List<MonitorDiskDTO> collectMonitorDisk() throws IOException {
+        List<MonitorDiskDTO> monitorDiskSaveReqVOS = new ArrayList<>();
+        String os = System.getProperty("os.name").toLowerCase();
+        System.out.println(os);
+        if (os.contains("win")) {
+            getWindowsDiskUsage(monitorDiskSaveReqVOS);
+        } else if (os.contains("nix") || os.contains("nux") || os.contains("mac")) {
+            try {
+                getLinuxDiskUsage(monitorDiskSaveReqVOS);
+            } catch (IOException | InterruptedException e) {
+                e.printStackTrace();
+            }
+        } else {
+            System.out.println("不支持的服务器系统!");
+        }
+        return monitorDiskSaveReqVOS;
+    }
+
+    private static void getWindowsDiskUsage(List<MonitorDiskDTO> monitorDiskSaveReqVOS) throws IOException {
+        initServerInfo();
+        // 获取默认文件系统的所有存储设备
+        Iterable<FileStore> fileStores = FileSystems.getDefault().getFileStores();
+        for (FileStore store : fileStores) {
+            MonitorDiskDTO monitorDiskSaveReqVO = new MonitorDiskDTO();
+            // 获取磁盘的总空间、已用空间和可用空间
+            BigDecimal totalSpace = new BigDecimal(store.getTotalSpace()).divide(UNIT).setScale(2, BigDecimal.ROUND_HALF_UP); // 总空间
+            BigDecimal usableSpace = new BigDecimal(store.getUsableSpace()).divide(UNIT).setScale(2, BigDecimal.ROUND_HALF_UP); // 可用空间
+            BigDecimal usedSpace = totalSpace.subtract(usableSpace).setScale(2, BigDecimal.ROUND_HALF_UP); // 计算已用空间
+            monitorDiskSaveReqVO.setHostName(hostName)
+                    .setHostIp(hostIp)
+                    .setDisk(store.toString())
+                    .setDiskName(ObjectUtils.isNotEmpty(store.name()) ? store.name(): "/")
+                    .setSpaceTotal(totalSpace)
+                    .setSpaceUsable(usableSpace)
+                    .setSpaceUsed(usedSpace)
+                    .setSpaceRatio(usedSpace.divide(totalSpace, BigDecimal.ROUND_HALF_UP).setScale(4, BigDecimal.ROUND_HALF_UP));
+            monitorDiskSaveReqVOS.add(monitorDiskSaveReqVO);
+        }
+    }
+
+    /**
+     * 获取 Linux 系统的磁盘使用情况
+     */
+    private static void getLinuxDiskUsage(List<MonitorDiskDTO> monitorDiskSaveReqVOS) throws IOException, InterruptedException {
+        Process process = Runtime.getRuntime().exec("df -h");
+        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+        String line;
+        boolean isHeader = true;
+        while ((line = reader.readLine()) != null) {
+            if (isHeader) {
+                isHeader = false;
+                continue;
+            }
+            String[] parts = line.trim().split("\\s+");
+            System.out.println(parts);
+            if (parts.length >= 6) {
+                MonitorDiskDTO monitorDiskSaveReqVO = new MonitorDiskDTO();
+                String filesystem = parts[0];
+                String size = parts[1];
+                String used = parts[2];
+                String available = parts[3];
+                String mountedOn = parts[5];
+                BigDecimal totalSpace = formatSize(size);
+                BigDecimal usableSpace = formatSize(available);
+                BigDecimal usedSpace = formatSize(used);
+                monitorDiskSaveReqVO.setHostName(hostName)
+                        .setHostIp(hostIp)
+                        .setDisk(filesystem + "(" + mountedOn + ")")
+                        .setDiskName(mountedOn)
+                        .setSpaceTotal(totalSpace)
+                        .setSpaceUsable(usableSpace)
+                        .setSpaceUsed(usedSpace)
+                        .setSpaceRatio(usedSpace.divide(totalSpace, BigDecimal.ROUND_HALF_UP).setScale(4, BigDecimal.ROUND_HALF_UP));
+                monitorDiskSaveReqVOS.add(monitorDiskSaveReqVO);
+            }
+        }
+        int exitCode = process.waitFor();
+        if (exitCode != 0) {
+            System.err.println("Command execution failed with exit code: " + exitCode);
+        }
+        reader.close();
+    }
+
+    /**
+     * 将字节大小转换为可读的格式
+     * @param size 字节数
+     * @return 人类可读的大小(MB)
+     */
+    private static BigDecimal formatSize(String size) {
+        if(size.contains("T")){
+            return new BigDecimal(Float.parseFloat(size.replace("T",""))).multiply(UNIT).setScale(2, BigDecimal.ROUND_HALF_UP);
+        } else if (size.contains("G")){
+            return new BigDecimal(Float.parseFloat(size.replace("G","")) * 1024).setScale(2, BigDecimal.ROUND_HALF_UP);
+        } else if (size.contains("M")){
+            return new BigDecimal(Float.parseFloat(size.replace("M",""))).setScale(2, BigDecimal.ROUND_HALF_UP);
+        } else if (size.contains("K")){
+            return new BigDecimal(Float.parseFloat(size.replace("K","")) / 1024).setScale(2, BigDecimal.ROUND_HALF_UP);
+        } else {
+            return new BigDecimal(0);
+        }
+    }
+}
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/api/monitor/MonitorApiImpl.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/api/monitor/MonitorApiImpl.java
new file mode 100644
index 0000000..ec8fccc
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/api/monitor/MonitorApiImpl.java
@@ -0,0 +1,40 @@
+package com.iailab.module.infra.api.monitor;
+
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.infra.api.monitor.dto.MonitorDiskDTO;
+import com.iailab.module.infra.api.monitor.dto.MonitorMemDTO;
+import com.iailab.module.infra.controller.admin.monitordisk.vo.MonitorDiskSaveReqVO;
+import com.iailab.module.infra.controller.admin.monitormem.vo.MonitorMemSaveReqVO;
+import com.iailab.module.infra.service.monitordisk.MonitorDiskService;
+import com.iailab.module.infra.service.monitormem.MonitorMemService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+
+@RestController // 提供 RESTful API 接口,给 Feign 调用
+@Validated
+public class MonitorApiImpl implements MonitorApi {
+
+    @Resource
+    private MonitorMemService monitorMemService;
+
+    @Resource
+    private MonitorDiskService monitorDiskService;
+
+    @Override
+    public CommonResult<Long> reportMemInfo(MonitorMemDTO reportMemReqDTO) {
+        MonitorMemSaveReqVO bean = BeanUtils.toBean(reportMemReqDTO, MonitorMemSaveReqVO.class);
+        return CommonResult.success(monitorMemService.createMonitorMem(bean));
+    }
+
+    @Override
+    public CommonResult reportDiskInfo(List<MonitorDiskDTO> reportDiskReqDTOS) {
+        List<MonitorDiskSaveReqVO> bean = BeanUtils.toBean(reportDiskReqDTOS, MonitorDiskSaveReqVO.class);
+        monitorDiskService.createMonitorDiskBatch(bean);
+        return CommonResult.success();
+    }
+}
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/MonitorDiskController.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/MonitorDiskController.java
new file mode 100644
index 0000000..31d08e7
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/MonitorDiskController.java
@@ -0,0 +1,126 @@
+package com.iailab.module.infra.controller.admin.monitordisk;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import com.iailab.framework.common.pojo.PageParam;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import static com.iailab.framework.common.pojo.CommonResult.success;
+
+import com.iailab.framework.excel.core.util.ExcelUtils;
+
+import com.iailab.framework.apilog.core.annotation.ApiAccessLog;
+import static com.iailab.framework.apilog.core.enums.OperateTypeEnum.*;
+
+import com.iailab.module.infra.controller.admin.monitordisk.vo.*;
+import com.iailab.module.infra.dal.dataobject.monitordisk.MonitorDiskDO;
+import com.iailab.module.infra.service.monitordisk.MonitorDiskService;
+
+@Tag(name = "管理后台 - 磁盘监控日志")
+@RestController
+@RequestMapping("/infra/monitor-disk")
+@Validated
+public class MonitorDiskController {
+
+    @Resource
+    private MonitorDiskService monitorDiskService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建磁盘监控日志")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-disk:create')")
+    public CommonResult<Long> createMonitorDisk(@Valid @RequestBody MonitorDiskSaveReqVO createReqVO) {
+        return success(monitorDiskService.createMonitorDisk(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新磁盘监控日志")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-disk:update')")
+    public CommonResult<Boolean> updateMonitorDisk(@Valid @RequestBody MonitorDiskSaveReqVO updateReqVO) {
+        monitorDiskService.updateMonitorDisk(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除磁盘监控日志")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('infra:monitor-disk:delete')")
+    public CommonResult<Boolean> deleteMonitorDisk(@RequestParam("id") Long id) {
+        monitorDiskService.deleteMonitorDisk(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得磁盘监控日志")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-disk:query')")
+    public CommonResult<MonitorDiskRespVO> getMonitorDisk(@RequestParam("id") Long id) {
+        MonitorDiskDO monitorDisk = monitorDiskService.getMonitorDisk(id);
+        return success(BeanUtils.toBean(monitorDisk, MonitorDiskRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得磁盘监控日志分页")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-disk:query')")
+    public CommonResult<PageResult<MonitorDiskRespVO>> getMonitorDiskPage(@Valid MonitorDiskPageReqVO pageReqVO) {
+        PageResult<MonitorDiskDO> pageResult = monitorDiskService.getMonitorDiskPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, MonitorDiskRespVO.class));
+    }
+
+    @GetMapping("/getMonitorDiskList")
+    @Operation(summary = "获得磁盘监控日志列表")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-disk:query')")
+    public CommonResult<List<Map<String, List<Map<String, Object>>>>> getMonitorMemList(@Valid MonitorDiskReqVO reqVO) {
+        List<Map<String, List<Map<String, Object>>>> result = monitorDiskService.getMonitorDiskList(reqVO);
+        return success(result);
+    }
+
+    @GetMapping("/getAllHost")
+    @Operation(summary = "获得内存监控日志统计")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-disk:query')")
+    public CommonResult<List<String>> getAllHost() {
+        List<String> hosts = monitorDiskService.getAllHost();
+        return success(hosts);
+    }
+
+    @GetMapping("/getAllIp")
+    @Operation(summary = "获得内存监控日志统计")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-disk:query')")
+    public CommonResult<List<String>> getAllIp() {
+        List<String> ips = monitorDiskService.getAllIp();
+        return success(ips);
+    }
+
+    @GetMapping("/getMonitorDiskInfo")
+    @Operation(summary = "获得磁盘监控日志列表")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-disk:query')")
+    public CommonResult<List<Map<String, Object>>> getMonitorDiskInfo(@Valid MonitorDiskReqVO reqVO) {
+        List<Map<String, Object>> result = monitorDiskService.getMonitorDiskInfo(reqVO);
+        return success(result);
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出磁盘监控日志 Excel")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-disk:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportMonitorDiskExcel(@Valid MonitorDiskPageReqVO pageReqVO,
+              HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<MonitorDiskDO> list = monitorDiskService.getMonitorDiskPage(pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "磁盘监控日志.xls", "数据", MonitorDiskRespVO.class,
+                        BeanUtils.toBean(list, MonitorDiskRespVO.class));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskPageReqVO.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskPageReqVO.java
new file mode 100644
index 0000000..d23e29f
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskPageReqVO.java
@@ -0,0 +1,47 @@
+package com.iailab.module.infra.controller.admin.monitordisk.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import com.iailab.framework.common.pojo.PageParam;
+import java.math.BigDecimal;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 磁盘监控日志分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class MonitorDiskPageReqVO extends PageParam {
+
+    @Schema(description = "主机名称", example = "iailab")
+    private String hostName;
+
+    @Schema(description = "服务器ip")
+    private String hostIp;
+
+    @Schema(description = "盘符")
+    private String disk;
+
+    @Schema(description = "磁盘名", example = "王五")
+    private String diskName;
+
+    @Schema(description = "总空间")
+    private BigDecimal spaceTotal;
+
+    @Schema(description = "已用空间")
+    private BigDecimal spaceUsed;
+
+    @Schema(description = "可用空间")
+    private BigDecimal spaceUsable;
+
+    @Schema(description = "空间使用比例")
+    private BigDecimal spaceRatio;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskReqVO.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskReqVO.java
new file mode 100644
index 0000000..0532b2b
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskReqVO.java
@@ -0,0 +1,49 @@
+package com.iailab.module.infra.controller.admin.monitordisk.vo;
+
+import com.iailab.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 磁盘监控日志 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class MonitorDiskReqVO extends PageParam {
+
+    @Schema(description = "主机名称", example = "iailab")
+    private String hostName;
+
+    @Schema(description = "服务器ip")
+    private String hostIp;
+
+    @Schema(description = "盘符")
+    private String disk;
+
+    @Schema(description = "磁盘名", example = "王五")
+    private String diskName;
+
+    @Schema(description = "总空间")
+    private BigDecimal spaceTotal;
+
+    @Schema(description = "已用空间")
+    private BigDecimal spaceUsed;
+
+    @Schema(description = "可用空间")
+    private BigDecimal spaceUsable;
+
+    @Schema(description = "空间使用比例")
+    private BigDecimal spaceRatio;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskRespVO.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskRespVO.java
new file mode 100644
index 0000000..94b8efe
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskRespVO.java
@@ -0,0 +1,57 @@
+package com.iailab.module.infra.controller.admin.monitordisk.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import java.math.BigDecimal;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+
+@Schema(description = "管理后台 - 磁盘监控日志 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class MonitorDiskRespVO {
+
+    @Schema(description = "访问ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "528")
+    @ExcelProperty("访问ID")
+    private Long id;
+
+    @Schema(description = "主机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "iailab")
+    @ExcelProperty("主机名称")
+    private String hostName;
+
+    @Schema(description = "服务器ip", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("服务器ip")
+    private String hostIp;
+
+    @Schema(description = "盘符")
+    @ExcelProperty("盘符")
+    private String disk;
+
+    @Schema(description = "磁盘名", example = "王五")
+    @ExcelProperty("磁盘名")
+    private String diskName;
+
+    @Schema(description = "总空间")
+    @ExcelProperty("总空间")
+    private BigDecimal spaceTotal;
+
+    @Schema(description = "已用空间")
+    @ExcelProperty("已用空间")
+    private BigDecimal spaceUsed;
+
+    @Schema(description = "可用空间")
+    @ExcelProperty("可用空间")
+    private BigDecimal spaceUsable;
+
+    @Schema(description = "空间使用比例")
+    @ExcelProperty("空间使用比例")
+    private BigDecimal spaceRatio;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskSaveReqVO.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskSaveReqVO.java
new file mode 100644
index 0000000..303ef59
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitordisk/vo/MonitorDiskSaveReqVO.java
@@ -0,0 +1,41 @@
+package com.iailab.module.infra.controller.admin.monitordisk.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - 磁盘监控日志新增/修改 Request VO")
+@Data
+public class MonitorDiskSaveReqVO {
+
+    @Schema(description = "访问ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "528")
+    private Long id;
+
+    @Schema(description = "主机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "iailab")
+    @NotEmpty(message = "主机名称不能为空")
+    private String hostName;
+
+    @Schema(description = "服务器ip", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotEmpty(message = "服务器ip不能为空")
+    private String hostIp;
+
+    @Schema(description = "盘符")
+    private String disk;
+
+    @Schema(description = "磁盘名", example = "王五")
+    private String diskName;
+
+    @Schema(description = "总空间")
+    private BigDecimal spaceTotal;
+
+    @Schema(description = "已用空间")
+    private BigDecimal spaceUsed;
+
+    @Schema(description = "可用空间")
+    private BigDecimal spaceUsable;
+
+    @Schema(description = "空间使用比例")
+    private BigDecimal spaceRatio;
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/MonitorMemController.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/MonitorMemController.java
new file mode 100644
index 0000000..6681062
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/MonitorMemController.java
@@ -0,0 +1,126 @@
+package com.iailab.module.infra.controller.admin.monitormem;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import com.iailab.framework.common.pojo.PageParam;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import static com.iailab.framework.common.pojo.CommonResult.success;
+
+import com.iailab.framework.excel.core.util.ExcelUtils;
+
+import com.iailab.framework.apilog.core.annotation.ApiAccessLog;
+import static com.iailab.framework.apilog.core.enums.OperateTypeEnum.*;
+
+import com.iailab.module.infra.controller.admin.monitormem.vo.*;
+import com.iailab.module.infra.dal.dataobject.monitormem.MonitorMemDO;
+import com.iailab.module.infra.service.monitormem.MonitorMemService;
+
+@Tag(name = "管理后台 - 内存监控日志")
+@RestController
+@RequestMapping("/infra/monitor-mem")
+@Validated
+public class MonitorMemController {
+
+    @Resource
+    private MonitorMemService monitorMemService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建内存监控日志")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-mem:create')")
+    public CommonResult<Long> createMonitorMem(@Valid @RequestBody MonitorMemSaveReqVO createReqVO) {
+        return success(monitorMemService.createMonitorMem(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新内存监控日志")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-mem:update')")
+    public CommonResult<Boolean> updateMonitorMem(@Valid @RequestBody MonitorMemSaveReqVO updateReqVO) {
+        monitorMemService.updateMonitorMem(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除内存监控日志")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('infra:monitor-mem:delete')")
+    public CommonResult<Boolean> deleteMonitorMem(@RequestParam("id") Long id) {
+        monitorMemService.deleteMonitorMem(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得内存监控日志")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-mem:query')")
+    public CommonResult<MonitorMemRespVO> getMonitorMem(@RequestParam("id") Long id) {
+        MonitorMemDO monitorMem = monitorMemService.getMonitorMem(id);
+        return success(BeanUtils.toBean(monitorMem, MonitorMemRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得内存监控日志分页")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-mem:query')")
+    public CommonResult<PageResult<MonitorMemRespVO>> getMonitorMemPage(@Valid MonitorMemPageReqVO pageReqVO) {
+        PageResult<MonitorMemDO> pageResult = monitorMemService.getMonitorMemPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, MonitorMemRespVO.class));
+    }
+
+    @GetMapping("/getMonitorMemList")
+    @Operation(summary = "获得内存监控日志统计")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-mem:query')")
+    public CommonResult<List<MonitorMemDO>> getMonitorMemList(@Valid MonitorMemReqVO reqVO) {
+        List<MonitorMemDO> result = monitorMemService.getMonitorMemList(reqVO);
+        return success(result);
+    }
+
+    @GetMapping("/getAllHost")
+    @Operation(summary = "获得内存监控日志统计")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-mem:query')")
+    public CommonResult<List<String>> getAllHost() {
+        List<String> hosts = monitorMemService.getAllHost();
+        return success(hosts);
+    }
+
+    @GetMapping("/getAllServer")
+    @Operation(summary = "获得内存监控日志统计")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-mem:query')")
+    public CommonResult<List<String>> getAllServer() {
+        List<String> servers = monitorMemService.getAllServer();
+        return success(servers);
+    }
+
+    @GetMapping("/getAllIp")
+    @Operation(summary = "获得内存监控日志统计")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-mem:query')")
+    public CommonResult<List<String>> getAllIp() {
+        List<String> ips = monitorMemService.getAllIp();
+        return success(ips);
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出内存监控日志 Excel")
+    @PreAuthorize("@ss.hasPermission('infra:monitor-mem:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportMonitorMemExcel(@Valid MonitorMemPageReqVO pageReqVO,
+              HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<MonitorMemDO> list = monitorMemService.getMonitorMemPage(pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "内存监控日志.xls", "数据", MonitorMemRespVO.class,
+                        BeanUtils.toBean(list, MonitorMemRespVO.class));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemPageReqVO.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemPageReqVO.java
new file mode 100644
index 0000000..5e341d0
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemPageReqVO.java
@@ -0,0 +1,59 @@
+package com.iailab.module.infra.controller.admin.monitormem.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import com.iailab.framework.common.pojo.PageParam;
+import java.math.BigDecimal;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 内存监控日志分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class MonitorMemPageReqVO extends PageParam {
+
+    @Schema(description = "主机名称", example = "张三")
+    private String hostName;
+
+    @Schema(description = "服务器ip")
+    private String hostIp;
+
+    @Schema(description = "服务名", example = "王五")
+    private String serverName;
+
+    @Schema(description = "总物理内存")
+    private BigDecimal physicalTotal;
+
+    @Schema(description = "已用物理内存")
+    private BigDecimal physicalUsed;
+
+    @Schema(description = "剩余物理内存")
+    private BigDecimal physicalFree;
+
+    @Schema(description = "物理内存使用率")
+    private BigDecimal physicalUsage;
+
+    @Schema(description = "jvm运行总内存")
+    private BigDecimal runtimeTotal;
+
+    @Schema(description = "jvm最大内存")
+    private BigDecimal runtimeMax;
+
+    @Schema(description = "jvm已用内存")
+    private BigDecimal runtimeUsed;
+
+    @Schema(description = "jvm空闲内存")
+    private BigDecimal runtimeFree;
+
+    @Schema(description = "jvm内存使用率")
+    private BigDecimal runtimeUsage;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemReqVO.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemReqVO.java
new file mode 100644
index 0000000..de75baa
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemReqVO.java
@@ -0,0 +1,58 @@
+package com.iailab.module.infra.controller.admin.monitormem.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 查询内存监控日志 Request VO")
+@Data
+@ToString(callSuper = true)
+public class MonitorMemReqVO {
+
+    @Schema(description = "主机名称", example = "张三")
+    private String hostName;
+
+    @Schema(description = "服务器ip")
+    private String hostIp;
+
+    @Schema(description = "服务名", example = "王五")
+    private String serverName;
+
+    @Schema(description = "总物理内存")
+    private BigDecimal physicalTotal;
+
+    @Schema(description = "已用物理内存")
+    private BigDecimal physicalUsed;
+
+    @Schema(description = "剩余物理内存")
+    private BigDecimal physicalFree;
+
+    @Schema(description = "物理内存使用率")
+    private BigDecimal physicalUsage;
+
+    @Schema(description = "jvm运行总内存")
+    private BigDecimal runtimeTotal;
+
+    @Schema(description = "jvm最大内存")
+    private BigDecimal runtimeMax;
+
+    @Schema(description = "jvm已用内存")
+    private BigDecimal runtimeUsed;
+
+    @Schema(description = "jvm空闲内存")
+    private BigDecimal runtimeFree;
+
+    @Schema(description = "jvm内存使用率")
+    private BigDecimal runtimeUsage;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemRespVO.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemRespVO.java
new file mode 100644
index 0000000..66fe20c
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemRespVO.java
@@ -0,0 +1,81 @@
+package com.iailab.module.infra.controller.admin.monitormem.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import java.math.BigDecimal;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+
+@Schema(description = "管理后台 - 内存监控日志 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class MonitorMemRespVO {
+
+    @Schema(description = "访问ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "8051")
+    @ExcelProperty("访问ID")
+    private Long id;
+
+    @Schema(description = "主机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
+    @ExcelProperty("主机名称")
+    private String hostName;
+
+    @Schema(description = "服务器ip", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("服务器ip")
+    private String hostIp;
+
+    @Schema(description = "服务名", example = "王五")
+    @ExcelProperty("服务名")
+    private String serverName;
+
+    @Schema(description = "总物理内存")
+    @ExcelProperty("总物理内存")
+    private BigDecimal physicalTotal;
+
+    @Schema(description = "已用物理内存")
+    @ExcelProperty("已用物理内存")
+    private BigDecimal physicalUsed;
+
+    @Schema(description = "剩余物理内存")
+    @ExcelProperty("剩余物理内存")
+    private BigDecimal physicalFree;
+
+    @Schema(description = "物理内存使用率")
+    @ExcelProperty("物理内存使用率")
+    private BigDecimal physicalUsage;
+
+    @Schema(description = "jvm运行总内存")
+    @ExcelProperty("jvm运行总内存")
+    private BigDecimal runtimeTotal;
+
+    @Schema(description = "jvm最大内存")
+    @ExcelProperty("jvm最大内存")
+    private BigDecimal runtimeMax;
+
+    @Schema(description = "jvm已用内存")
+    @ExcelProperty("jvm已用内存")
+    private BigDecimal runtimeUsed;
+
+    @Schema(description = "jvm空闲内存")
+    @ExcelProperty("jvm空闲内存")
+    private BigDecimal runtimeFree;
+
+    @Schema(description = "jvm内存使用率")
+    @ExcelProperty("jvm内存使用率")
+    private BigDecimal runtimeUsage;
+
+    @Schema(description = "系统cpu利用率")
+    @ExcelProperty("系统cpu利用率")
+    private BigDecimal systemCpuLoad;
+
+    @Schema(description = "进程cpu利用率")
+    @ExcelProperty("进程cpu利用率")
+    private BigDecimal processCpuLoad;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemSaveReqVO.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemSaveReqVO.java
new file mode 100644
index 0000000..b08f583
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/controller/admin/monitormem/vo/MonitorMemSaveReqVO.java
@@ -0,0 +1,60 @@
+package com.iailab.module.infra.controller.admin.monitormem.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - 内存监控日志新增/修改 Request VO")
+@Data
+public class MonitorMemSaveReqVO {
+
+    @Schema(description = "访问ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "8051")
+    private Long id;
+
+    @Schema(description = "主机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
+    @NotEmpty(message = "主机名称不能为空")
+    private String hostName;
+
+    @Schema(description = "服务器ip", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotEmpty(message = "服务器ip不能为空")
+    private String hostIp;
+
+    @Schema(description = "服务名", example = "王五")
+    private String serverName;
+
+    @Schema(description = "总物理内存")
+    private BigDecimal physicalTotal;
+
+    @Schema(description = "已用物理内存")
+    private BigDecimal physicalUsed;
+
+    @Schema(description = "剩余物理内存")
+    private BigDecimal physicalFree;
+
+    @Schema(description = "物理内存使用率")
+    private BigDecimal physicalUsage;
+
+    @Schema(description = "jvm运行总内存")
+    private BigDecimal runtimeTotal;
+
+    @Schema(description = "jvm最大内存")
+    private BigDecimal runtimeMax;
+
+    @Schema(description = "jvm已用内存")
+    private BigDecimal runtimeUsed;
+
+    @Schema(description = "jvm空闲内存")
+    private BigDecimal runtimeFree;
+
+    @Schema(description = "jvm内存使用率")
+    private BigDecimal runtimeUsage;
+
+    @Schema(description = "系统cpu利用率")
+    private BigDecimal systemCpuLoad;
+
+    @Schema(description = "进程cpu利用率")
+    private BigDecimal processCpuLoad;
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/dataobject/monitordisk/MonitorDiskDO.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/dataobject/monitordisk/MonitorDiskDO.java
new file mode 100644
index 0000000..0321f55
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/dataobject/monitordisk/MonitorDiskDO.java
@@ -0,0 +1,72 @@
+package com.iailab.module.infra.dal.dataobject.monitordisk;
+
+import lombok.*;
+import java.util.*;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 磁盘监控日志 DO
+ *
+ * @author 超级管理员
+ */
+@TableName("system_monitor_disk")
+@KeySequence("system_monitor_disk_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class MonitorDiskDO extends BaseDO {
+
+    /**
+     * 访问ID
+     */
+    @TableId
+    private Long id;
+    /**
+     * 主机名称
+     */
+    private String hostName;
+    /**
+     * 服务器ip
+     */
+    private String hostIp;
+    /**
+     * 盘符
+     */
+    private String disk;
+    /**
+     * 磁盘名
+     */
+    private String diskName;
+    /**
+     * 总空间
+     */
+    private BigDecimal spaceTotal;
+    /**
+     * 已用空间
+     */
+    private BigDecimal spaceUsed;
+    /**
+     * 可用空间
+     */
+    private BigDecimal spaceUsable;
+    /**
+     * 空间使用比例
+     */
+    private BigDecimal spaceRatio;
+
+    /**
+     * 租户Id
+     */
+    private Integer tenantId;
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/dataobject/monitormem/MonitorMemDO.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/dataobject/monitormem/MonitorMemDO.java
new file mode 100644
index 0000000..e2369d6
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/dataobject/monitormem/MonitorMemDO.java
@@ -0,0 +1,103 @@
+package com.iailab.module.infra.dal.dataobject.monitormem;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 内存监控日志 DO
+ *
+ * @author 超级管理员
+ */
+@TableName("system_monitor_mem")
+@KeySequence("system_monitor_mem_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class MonitorMemDO extends BaseDO {
+
+    /**
+     * 访问ID
+     */
+    @TableId
+    private Long id;
+    /**
+     * 主机名称
+     */
+    private String hostName;
+    /**
+     * 服务器ip
+     */
+    private String hostIp;
+    /**
+     * 服务名
+     */
+    private String serverName;
+    /**
+     * 总物理内存
+     */
+    private BigDecimal physicalTotal;
+    /**
+     * 已用物理内存
+     */
+    private BigDecimal physicalUsed;
+    /**
+     * 剩余物理内存
+     */
+    private BigDecimal physicalFree;
+    /**
+     * 物理内存使用率
+     */
+    private BigDecimal physicalUsage;
+    /**
+     * jvm运行总内存
+     */
+    private BigDecimal runtimeTotal;
+    /**
+     * jvm最大内存
+     */
+    private BigDecimal runtimeMax;
+    /**
+     * jvm已用内存
+     */
+    private BigDecimal runtimeUsed;
+    /**
+     * jvm空闲内存
+     */
+    private BigDecimal runtimeFree;
+    /**
+     * jvm内存使用率
+     */
+    private BigDecimal runtimeUsage;
+    /**
+     * 系统cpu利用率
+     */
+    private BigDecimal systemCpuLoad;
+    /**
+     * 进程cpu利用率
+     */
+    private BigDecimal processCpuLoad;
+
+    /**
+     * 租户Id
+     */
+    private Integer tenantId;
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/mysql/monitordisk/MonitorDiskMapper.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/mysql/monitordisk/MonitorDiskMapper.java
new file mode 100644
index 0000000..5cb6ef6
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/mysql/monitordisk/MonitorDiskMapper.java
@@ -0,0 +1,46 @@
+package com.iailab.module.infra.dal.mysql.monitordisk;
+
+import java.util.*;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.module.infra.dal.dataobject.monitordisk.MonitorDiskDO;
+import org.apache.ibatis.annotations.Mapper;
+import com.iailab.module.infra.controller.admin.monitordisk.vo.*;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 磁盘监控日志 Mapper
+ *
+ * @author 超级管理员
+ */
+@Mapper
+public interface MonitorDiskMapper extends BaseMapperX<MonitorDiskDO> {
+
+    default PageResult<MonitorDiskDO> selectPage(MonitorDiskPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<MonitorDiskDO>()
+                .likeIfPresent(MonitorDiskDO::getHostName, reqVO.getHostName())
+                .eqIfPresent(MonitorDiskDO::getHostIp, reqVO.getHostIp())
+                .eqIfPresent(MonitorDiskDO::getDisk, reqVO.getDisk())
+                .likeIfPresent(MonitorDiskDO::getDiskName, reqVO.getDiskName())
+                .eqIfPresent(MonitorDiskDO::getSpaceTotal, reqVO.getSpaceTotal())
+                .eqIfPresent(MonitorDiskDO::getSpaceUsed, reqVO.getSpaceUsed())
+                .eqIfPresent(MonitorDiskDO::getSpaceUsable, reqVO.getSpaceUsable())
+                .eqIfPresent(MonitorDiskDO::getSpaceRatio, reqVO.getSpaceRatio())
+                .betweenIfPresent(MonitorDiskDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(MonitorDiskDO::getId));
+    }
+
+    default List<MonitorDiskDO> getMonitorDiskList(MonitorDiskReqVO reqVO) {
+        return selectList(new LambdaQueryWrapperX<MonitorDiskDO>()
+                .likeIfPresent(MonitorDiskDO::getHostName, reqVO.getHostName())
+                .eqIfPresent(MonitorDiskDO::getHostIp, reqVO.getHostIp())
+                .eqIfPresent(MonitorDiskDO::getDisk, reqVO.getDisk())
+                .likeIfPresent(MonitorDiskDO::getDiskName, reqVO.getDiskName())
+                .betweenIfPresent(MonitorDiskDO::getCreateTime, reqVO.getCreateTime())
+                .orderByAsc(MonitorDiskDO::getCreateTime));
+    }
+
+    List<MonitorDiskDO> getMonitorDiskInfo(@Param("params") MonitorDiskReqVO reqVO);
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/mysql/monitormem/MonitorMemMapper.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/mysql/monitormem/MonitorMemMapper.java
new file mode 100644
index 0000000..905ddef
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/dal/mysql/monitormem/MonitorMemMapper.java
@@ -0,0 +1,47 @@
+package com.iailab.module.infra.dal.mysql.monitormem;
+
+import java.util.*;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.module.infra.dal.dataobject.monitormem.MonitorMemDO;
+import org.apache.ibatis.annotations.Mapper;
+import com.iailab.module.infra.controller.admin.monitormem.vo.*;
+
+/**
+ * 内存监控日志 Mapper
+ *
+ * @author 超级管理员
+ */
+@Mapper
+public interface MonitorMemMapper extends BaseMapperX<MonitorMemDO> {
+
+    default PageResult<MonitorMemDO> selectPage(MonitorMemPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<MonitorMemDO>()
+                .likeIfPresent(MonitorMemDO::getHostName, reqVO.getHostName())
+                .eqIfPresent(MonitorMemDO::getHostIp, reqVO.getHostIp())
+                .likeIfPresent(MonitorMemDO::getServerName, reqVO.getServerName())
+                .eqIfPresent(MonitorMemDO::getPhysicalTotal, reqVO.getPhysicalTotal())
+                .eqIfPresent(MonitorMemDO::getPhysicalUsed, reqVO.getPhysicalUsed())
+                .eqIfPresent(MonitorMemDO::getPhysicalFree, reqVO.getPhysicalFree())
+                .eqIfPresent(MonitorMemDO::getPhysicalUsage, reqVO.getPhysicalUsage())
+                .eqIfPresent(MonitorMemDO::getRuntimeTotal, reqVO.getRuntimeTotal())
+                .eqIfPresent(MonitorMemDO::getRuntimeMax, reqVO.getRuntimeMax())
+                .eqIfPresent(MonitorMemDO::getRuntimeUsed, reqVO.getRuntimeUsed())
+                .eqIfPresent(MonitorMemDO::getRuntimeFree, reqVO.getRuntimeFree())
+                .eqIfPresent(MonitorMemDO::getRuntimeUsage, reqVO.getRuntimeUsage())
+                .betweenIfPresent(MonitorMemDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(MonitorMemDO::getId));
+    }
+
+    default List<MonitorMemDO> getMonitorMemList(MonitorMemReqVO reqVO) {
+        return selectList(new LambdaQueryWrapperX<MonitorMemDO>()
+                .likeIfPresent(MonitorMemDO::getHostName, reqVO.getHostName())
+                .eqIfPresent(MonitorMemDO::getHostIp, reqVO.getHostIp())
+                .likeIfPresent(MonitorMemDO::getServerName, reqVO.getServerName())
+                .betweenIfPresent(MonitorMemDO::getCreateTime, reqVO.getCreateTime())
+                .orderByAsc(MonitorMemDO::getCreateTime));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/job/monitor/MonitorDiskJob.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/job/monitor/MonitorDiskJob.java
new file mode 100644
index 0000000..b679851
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/job/monitor/MonitorDiskJob.java
@@ -0,0 +1,47 @@
+//package com.iailab.module.infra.job.monitor;
+//
+//import com.iailab.framework.tenant.core.aop.TenantIgnore;
+//import com.iailab.module.infra.api.monitor.MonitorApi;
+//import com.iailab.module.infra.api.monitor.dto.MonitorDiskDTO;
+//import com.iailab.module.infra.util.ServerInfoCollector;
+//import com.xxl.job.core.handler.annotation.XxlJob;
+//import org.slf4j.Logger;
+//import org.slf4j.LoggerFactory;
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.stereotype.Component;
+//import org.springframework.transaction.annotation.Transactional;
+//
+//import java.io.IOException;
+//import java.util.Date;
+//import java.util.List;
+//import java.util.concurrent.atomic.AtomicInteger;
+//
+//@Component
+//public class MonitorDiskJob {
+//
+//    private Logger logger = LoggerFactory.getLogger(getClass());
+//
+//    private final AtomicInteger counts = new AtomicInteger();
+//
+//    private static final Object lock = new Object();
+//
+//    private final MonitorApi monitorApi;
+//
+//
+//    public MonitorDiskJob(MonitorApi monitorApi) {
+//        this.monitorApi = monitorApi;
+//    }
+//
+//    @XxlJob("monitorDiskJob")
+//    @TenantIgnore
+//    @Transactional
+//    public void execute() throws IOException {
+//        synchronized (lock) {
+//            logger.info("[execute][定时第 ({}) 次执行]", counts.incrementAndGet());
+//            System.out.println(new Date() + ": 我是基础设施-服务器磁盘监控日志存储定时任务");
+//            List<MonitorDiskDTO> monitorDiskDTOS = ServerInfoCollector.collectMonitorDisk();
+//            monitorApi.reportDiskInfo(monitorDiskDTOS);
+//        }
+//    }
+//
+//}
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/job/monitor/MonitorMemJob.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/job/monitor/MonitorMemJob.java
new file mode 100644
index 0000000..30b77f4
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/job/monitor/MonitorMemJob.java
@@ -0,0 +1,50 @@
+//package com.iailab.module.infra.job.monitor;
+//
+//import com.iailab.framework.tenant.core.aop.TenantIgnore;
+//import com.iailab.module.infra.api.monitor.MonitorApi;
+//import com.iailab.module.infra.api.monitor.dto.MonitorMemDTO;
+//import com.iailab.module.infra.util.ServerInfoCollector;
+//import com.xxl.job.core.handler.annotation.XxlJob;
+//import org.slf4j.Logger;
+//import org.slf4j.LoggerFactory;
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.stereotype.Component;
+//import org.springframework.transaction.annotation.Transactional;
+//
+//import java.io.IOException;
+//import java.util.Date;
+//import java.util.concurrent.atomic.AtomicInteger;
+//
+//@Component
+//public class MonitorMemJob {
+//
+//    private Logger logger = LoggerFactory.getLogger(getClass());
+//
+//    private final AtomicInteger counts = new AtomicInteger();
+//
+//    private static final Object lock = new Object();
+//
+//    private final MonitorApi monitorApi;
+//
+//    @Value("${spring.application.name}")
+//    public String serverName;
+//
+//    public MonitorMemJob(MonitorApi monitorApi) {
+//        this.monitorApi = monitorApi;
+//    }
+//
+//    @XxlJob("monitorMemJob")
+//    @TenantIgnore
+//    @Transactional
+//    public void execute() throws IOException {
+//        synchronized (lock) {
+//            logger.info("[execute][定时第 ({}) 次执行]", counts.incrementAndGet());
+//            System.out.println(new Date() + ": 我是基础设施-服务器内存监控日志存储定时任务");
+//            MonitorMemDTO monitorMemDTO = ServerInfoCollector.collectMonitorMem(serverName);
+//            monitorApi.reportMemInfo(monitorMemDTO);
+//        }
+//    }
+//
+//
+//
+//}
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitordisk/MonitorDiskService.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitordisk/MonitorDiskService.java
new file mode 100644
index 0000000..48d1e5f
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitordisk/MonitorDiskService.java
@@ -0,0 +1,94 @@
+package com.iailab.module.infra.service.monitordisk;
+
+import java.time.LocalDateTime;
+import java.util.*;
+import javax.validation.*;
+import com.iailab.module.infra.controller.admin.monitordisk.vo.*;
+import com.iailab.module.infra.dal.dataobject.monitordisk.MonitorDiskDO;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.pojo.PageParam;
+
+/**
+ * 磁盘监控日志 Service 接口
+ *
+ * @author 超级管理员
+ */
+public interface MonitorDiskService {
+
+    /**
+     * 创建磁盘监控日志
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createMonitorDisk(@Valid MonitorDiskSaveReqVO createReqVO);
+
+    /**
+     * 批量创建磁盘监控日志
+     *
+     * @param createReqVOS 创建信息
+     * @return 编号
+     */
+    void createMonitorDiskBatch(List<MonitorDiskSaveReqVO> createReqVOS);
+
+    /**
+     * 更新磁盘监控日志
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateMonitorDisk(@Valid MonitorDiskSaveReqVO updateReqVO);
+
+    /**
+     * 删除磁盘监控日志
+     *
+     * @param id 编号
+     */
+    void deleteMonitorDisk(Long id);
+
+    /**
+     * 获得磁盘监控日志
+     *
+     * @param id 编号
+     * @return 磁盘监控日志
+     */
+    MonitorDiskDO getMonitorDisk(Long id);
+
+    /**
+     * 获得磁盘监控日志分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 磁盘监控日志分页
+     */
+    PageResult<MonitorDiskDO> getMonitorDiskPage(MonitorDiskPageReqVO pageReqVO);
+
+    /**
+     * 获得磁盘监控日志列表
+     *
+     * @param reqVO 查询
+     * @return 磁盘监控日志列表
+     */
+    List<Map<String, List<Map<String, Object>>>> getMonitorDiskList(MonitorDiskReqVO reqVO);
+
+    /**
+     * 获得磁盘监控日志信息
+     *
+     * @param reqVO 查询
+     * @return 磁盘监控日志信息
+     */
+    List<Map<String, Object>> getMonitorDiskInfo(MonitorDiskReqVO reqVO);
+
+    /**
+     * 获取所有主机
+     *
+     * @return 获取所有主机
+     */
+    List<String> getAllHost();
+
+    /**
+     * 获取所有ip
+     *
+     * @return 获取所有ip
+     */
+    List<String> getAllIp();
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitordisk/MonitorDiskServiceImpl.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitordisk/MonitorDiskServiceImpl.java
new file mode 100644
index 0000000..a6e8a61
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitordisk/MonitorDiskServiceImpl.java
@@ -0,0 +1,180 @@
+package com.iailab.module.infra.service.monitordisk;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.iailab.framework.tenant.core.aop.TenantIgnore;
+import com.iailab.module.infra.dal.dataobject.monitormem.MonitorMemDO;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.stereotype.Service;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+
+import com.iailab.module.infra.controller.admin.monitordisk.vo.*;
+import com.iailab.module.infra.dal.dataobject.monitordisk.MonitorDiskDO;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+
+import com.iailab.module.infra.dal.mysql.monitordisk.MonitorDiskMapper;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.module.infra.enums.ErrorCodeConstants.*;
+
+/**
+ * 磁盘监控日志 Service 实现类
+ *
+ * @author 超级管理员
+ */
+@Service
+@Validated
+public class MonitorDiskServiceImpl implements MonitorDiskService {
+
+    @Resource
+    private MonitorDiskMapper monitorDiskMapper;
+
+    @Override
+    public Long createMonitorDisk(MonitorDiskSaveReqVO createReqVO) {
+        // 插入
+        MonitorDiskDO monitorDisk = BeanUtils.toBean(createReqVO, MonitorDiskDO.class);
+        monitorDiskMapper.insert(monitorDisk);
+        // 返回
+        return monitorDisk.getId();
+    }
+
+    @Override
+    public void createMonitorDiskBatch(List<MonitorDiskSaveReqVO> createReqVOS) {
+        // 插入
+        List<MonitorDiskDO> monitorDiskDOS = BeanUtils.toBean(createReqVOS, MonitorDiskDO.class);
+        monitorDiskDOS.stream().forEach(monitorDiskDO -> {
+            monitorDiskDO.setTenantId(1);
+            monitorDiskDO.setCreator("iailab");
+        });
+        monitorDiskMapper.insertBatch(monitorDiskDOS);
+    }
+
+    @Override
+    public void updateMonitorDisk(MonitorDiskSaveReqVO updateReqVO) {
+        // 校验存在
+        validateMonitorDiskExists(updateReqVO.getId());
+        // 更新
+        MonitorDiskDO updateObj = BeanUtils.toBean(updateReqVO, MonitorDiskDO.class);
+        monitorDiskMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteMonitorDisk(Long id) {
+        // 校验存在
+        validateMonitorDiskExists(id);
+        // 删除
+        monitorDiskMapper.deleteById(id);
+    }
+
+    private void validateMonitorDiskExists(Long id) {
+        if (monitorDiskMapper.selectById(id) == null) {
+            throw exception(MONITOR_DISK_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public MonitorDiskDO getMonitorDisk(Long id) {
+        return monitorDiskMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<MonitorDiskDO> getMonitorDiskPage(MonitorDiskPageReqVO pageReqVO) {
+        return monitorDiskMapper.selectPage(pageReqVO);
+    }
+
+    /**
+     * 折线图展示
+     * @param reqVO 查询
+     * @return
+     */
+    @Override
+    public List<Map<String, List<Map<String, Object>>>> getMonitorDiskList(MonitorDiskReqVO reqVO) {
+        List<MonitorDiskDO> monitorDiskList = monitorDiskMapper.getMonitorDiskList(reqVO);
+        if(ObjectUtils.isNotEmpty(monitorDiskList)) {
+            // 根据 hostName 字段进行分组
+            Map<String, List<MonitorDiskDO>> hostNameCollect = monitorDiskList.stream()
+                    .collect(Collectors.groupingBy(MonitorDiskDO::getHostName));
+            List<Map<String, List<Map<String, Object>>>> host = hostNameCollect.entrySet().stream().map(hostEntry -> {
+                List<MonitorDiskDO> hostValue = hostEntry.getValue();
+                String key = hostEntry.getKey();
+                Map<String, List<Map<String, Object>>> hostItem = new HashMap<>();
+                // 根据 createTime 字段进行分组
+                Map<LocalDateTime, List<MonitorDiskDO>> createTimeCollect = hostValue.stream()
+                        .collect(Collectors.groupingBy(MonitorDiskDO::getCreateTime));
+                List<Map<String, Object>> collect = createTimeCollect.entrySet().stream().map(entry -> {
+                    List<MonitorDiskDO> value = entry.getValue();
+                    Map<String, Object> item = new HashMap<>();
+                    value.stream().forEach(monitorDiskDO -> {
+                        item.put("createTime", monitorDiskDO.getCreateTime());
+                        item.put(monitorDiskDO.getDisk(), monitorDiskDO.getSpaceRatio().multiply(new BigDecimal(100)));
+                    });
+                    return item;
+                }).collect(Collectors.toList());
+                hostItem.put(key, collect);
+                return hostItem;
+            }).collect(Collectors.toList());
+            return host;
+        }
+        return null;
+    }
+
+    /**
+     * 饼图展示
+     * @param reqVO 查询
+     * @return
+     */
+    @Override
+    @TenantIgnore
+    public List<Map<String, Object>> getMonitorDiskInfo(MonitorDiskReqVO reqVO) {
+        List<MonitorDiskDO> monitorDiskInfo = monitorDiskMapper.getMonitorDiskInfo(reqVO);
+        if(ObjectUtils.isNotEmpty(monitorDiskInfo)) {
+            // 根据 hostName 字段进行分组
+            Map<String, List<MonitorDiskDO>> hostNameCollect = monitorDiskInfo.stream()
+                    .collect(Collectors.groupingBy(MonitorDiskDO::getHostName));
+            List<Map<String, Object>> collect = hostNameCollect.entrySet().stream().map(entry -> {
+                List<MonitorDiskDO> value = entry.getValue();
+                Map<String, Object> item = new HashMap<>();
+                List<Map<String, Object>> disks = new ArrayList<>();
+                value.stream().forEach(monitorDiskDO -> {
+                    Map<String, Object> diskMap = new HashMap<>();
+                    item.put("name", monitorDiskDO.getHostName());
+                    item.put("ip", monitorDiskDO.getHostIp());
+                    diskMap.put("disk", monitorDiskDO.getDisk());
+                    diskMap.put("total", monitorDiskDO.getSpaceTotal().divide(new BigDecimal(1024)).setScale(3, BigDecimal.ROUND_HALF_UP).toString());
+                    diskMap.put("used", monitorDiskDO.getSpaceUsed().divide(new BigDecimal(1024)).setScale(2, BigDecimal.ROUND_HALF_UP));
+                    disks.add(diskMap);
+                });
+                item.put("disks", disks);
+                return item;
+            }).collect(Collectors.toList());
+            return collect;
+        }
+        return null;
+    }
+
+    @Override
+    public List<String> getAllHost() {
+        QueryWrapper<MonitorDiskDO> queryWrapper = new QueryWrapper<>();
+        queryWrapper.select("DISTINCT host_name"); // 添加 DISTINCT 修饰
+        List<String> hosts = monitorDiskMapper.selectObjs(queryWrapper);
+        return hosts;
+    }
+
+    @Override
+    public List<String> getAllIp() {
+        QueryWrapper<MonitorDiskDO> queryWrapper = new QueryWrapper<>();
+        queryWrapper.select("DISTINCT host_ip"); // 添加 DISTINCT 修饰
+        List<String> ips = monitorDiskMapper.selectObjs(queryWrapper);
+        return ips;
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitormem/MonitorMemService.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitormem/MonitorMemService.java
new file mode 100644
index 0000000..afc302c
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitormem/MonitorMemService.java
@@ -0,0 +1,83 @@
+package com.iailab.module.infra.service.monitormem;
+
+import java.util.*;
+import javax.validation.*;
+import com.iailab.module.infra.controller.admin.monitormem.vo.*;
+import com.iailab.module.infra.dal.dataobject.monitormem.MonitorMemDO;
+import com.iailab.framework.common.pojo.PageResult;
+
+/**
+ * 内存监控日志 Service 接口
+ *
+ * @author 超级管理员
+ */
+public interface MonitorMemService {
+
+    /**
+     * 创建内存监控日志
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createMonitorMem(@Valid MonitorMemSaveReqVO createReqVO);
+
+    /**
+     * 更新内存监控日志
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateMonitorMem(@Valid MonitorMemSaveReqVO updateReqVO);
+
+    /**
+     * 删除内存监控日志
+     *
+     * @param id 编号
+     */
+    void deleteMonitorMem(Long id);
+
+    /**
+     * 获得内存监控日志
+     *
+     * @param id 编号
+     * @return 内存监控日志
+     */
+    MonitorMemDO getMonitorMem(Long id);
+
+    /**
+     * 获得内存监控日志分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 内存监控日志分页
+     */
+    PageResult<MonitorMemDO> getMonitorMemPage(MonitorMemPageReqVO pageReqVO);
+
+    /**
+     * 获得内存监控日志
+     *
+     * @param pageReqVO 查询
+     * @return 内存监控日志
+     */
+    List<MonitorMemDO> getMonitorMemList(MonitorMemReqVO pageReqVO);
+
+    /**
+     * 获取所有主机
+     *
+     * @return 获取所有主机
+     */
+    List<String> getAllHost();
+
+    /**
+     * 获取所有服务
+     *
+     * @return 获取所有服务
+     */
+    List<String> getAllServer();
+
+    /**
+     * 获取所有ip
+     *
+     * @return 获取所有ip
+     */
+    List<String> getAllIp();
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitormem/MonitorMemServiceImpl.java b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitormem/MonitorMemServiceImpl.java
new file mode 100644
index 0000000..1314d49
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/java/com/iailab/module/infra/service/monitormem/MonitorMemServiceImpl.java
@@ -0,0 +1,108 @@
+package com.iailab.module.infra.service.monitormem;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import org.springframework.stereotype.Service;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+
+import com.iailab.module.infra.controller.admin.monitormem.vo.*;
+import com.iailab.module.infra.dal.dataobject.monitormem.MonitorMemDO;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+
+import com.iailab.module.infra.dal.mysql.monitormem.MonitorMemMapper;
+
+import java.util.Collections;
+import java.util.List;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.module.infra.enums.ErrorCodeConstants.*;
+
+/**
+ * 内存监控日志 Service 实现类
+ *
+ * @author 超级管理员
+ */
+@Service
+@Validated
+public class MonitorMemServiceImpl implements MonitorMemService {
+
+    @Resource
+    private MonitorMemMapper monitorMemMapper;
+
+    @Override
+    public Long createMonitorMem(MonitorMemSaveReqVO createReqVO) {
+        // 插入
+        MonitorMemDO monitorMem = BeanUtils.toBean(createReqVO, MonitorMemDO.class);
+        monitorMem.setCreator("iailab");
+        monitorMem.setTenantId(1);
+        monitorMemMapper.insert(monitorMem);
+        // 返回
+        return monitorMem.getId();
+    }
+
+    @Override
+    public void updateMonitorMem(MonitorMemSaveReqVO updateReqVO) {
+        // 校验存在
+        validateMonitorMemExists(updateReqVO.getId());
+        // 更新
+        MonitorMemDO updateObj = BeanUtils.toBean(updateReqVO, MonitorMemDO.class);
+        updateObj.setUpdater("iailab");
+        monitorMemMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteMonitorMem(Long id) {
+        // 校验存在
+        validateMonitorMemExists(id);
+        // 删除
+        monitorMemMapper.deleteById(id);
+    }
+
+    private void validateMonitorMemExists(Long id) {
+        if (monitorMemMapper.selectById(id) == null) {
+            throw exception(MONITOR_MEM_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public MonitorMemDO getMonitorMem(Long id) {
+        return monitorMemMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<MonitorMemDO> getMonitorMemPage(MonitorMemPageReqVO pageReqVO) {
+        return monitorMemMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<MonitorMemDO> getMonitorMemList(MonitorMemReqVO reqVO) {
+        return monitorMemMapper.getMonitorMemList(reqVO);
+    }
+
+    @Override
+    public List<String> getAllHost() {
+        QueryWrapper<MonitorMemDO> queryWrapper = new QueryWrapper<>();
+        queryWrapper.select("DISTINCT host_name"); // 添加 DISTINCT 修饰
+        List<String> hosts = monitorMemMapper.selectObjs(queryWrapper);
+        return hosts;
+    }
+
+    @Override
+    public List<String> getAllServer() {
+        QueryWrapper<MonitorMemDO> queryWrapper = new QueryWrapper<>();
+        queryWrapper.select("DISTINCT server_name"); // 添加 DISTINCT 修饰
+        List<String> servers = monitorMemMapper.selectObjs(queryWrapper);
+        return servers;
+    }
+
+    @Override
+    public List<String> getAllIp() {
+        QueryWrapper<MonitorMemDO> queryWrapper = new QueryWrapper<>();
+        queryWrapper.select("DISTINCT host_ip"); // 添加 DISTINCT 修饰
+        List<String> ips = monitorMemMapper.selectObjs(queryWrapper);
+        return ips;
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-dev.yaml b/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-dev.yaml
index f00ba68..179b067 100644
--- a/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-dev.yaml
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-dev.yaml
@@ -64,7 +64,7 @@
 
   # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
   redis:
-    host: 172.16.8.100 # 地址
+    host: 127.0.0.1 # 地址
     port: 6379 # 端口
     database: 0 # 数据库索引
     password: 123456 # 密码,建议生产环境开启
@@ -78,7 +78,7 @@
 spring:
   # RabbitMQ 配置项,对应 RabbitProperties 配置类
   rabbitmq:
-    host: 172.16.8.200 # RabbitMQ 服务的地址
+    host: 172.16.1.221 # RabbitMQ 服务的地址
     port: 5672 # RabbitMQ 服务的端口
     username: admin # RabbitMQ 服务的账号
     password: admin123 # RabbitMQ 服务的密码
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-prod.yaml b/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-prod.yaml
new file mode 100644
index 0000000..b5085eb
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-prod.yaml
@@ -0,0 +1,150 @@
+--- #################### 数据库相关配置 ####################
+spring:
+
+  # 数据源配置项
+  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 的自动配置
+  datasource:
+    druid: # Druid 【监控】相关的全局配置
+      web-stat-filter:
+        enabled: true
+      stat-view-servlet:
+        enabled: true
+        allow: # 设置白名单,不填则允许所有访问
+        url-pattern: /druid/*
+        login-username: # 控制台管理用户名和密码
+        login-password:
+      filter:
+        stat:
+          enabled: true
+          log-slow-sql: true # 慢 SQL 记录
+          slow-sql-millis: 100
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
+    dynamic: # 多数据源配置
+      druid: # Druid 【连接池】相关的全局配置
+        initial-size: 1 # 初始连接数
+        min-idle: 1 # 最小连接池数量
+        max-active: 20 # 最大连接池数量
+        max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
+        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
+        min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
+        max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
+        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
+        test-while-idle: true
+        test-on-borrow: false
+        test-on-return: false
+      primary: master
+      datasource:
+        master:
+          url: jdbc:mysql://172.16.8.100:3306/iailab_plat_system?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
+          #          url: jdbc:mysql://127.0.0.1:3306/iailab-plat?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # MySQL Connector/J 5.X 连接的示例
+          #          url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例
+          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
+          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例
+          #          url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例
+          username: root
+          password: 123456
+        #          username: sa # SQL Server 连接的示例
+        #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例
+        #          username: SYSDBA # DM 连接的示例
+        #          password: SYSDBA # DM 连接的示例
+        slave: # 模拟从库,可根据自己需要修改
+          lazy: true # 开启懒加载,保证启动速度
+          url: jdbc:mysql://127.0.0.1:3306/iailab-plat?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+          username: root
+          password: 123456
+
+  # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
+  redis:
+    host: 172.16.8.100 # 地址
+    port: 6379 # 端口
+    database: 0 # 数据库索引
+    password: 123456 # 密码,建议生产环境开启
+
+--- #################### MQ 消息队列相关配置 ####################
+
+# rocketmq 配置项,对应 RocketMQProperties 配置类
+rocketmq:
+  name-server: 172.16.8.100:9876 # RocketMQ Namesrv
+
+spring:
+  # RabbitMQ 配置项,对应 RabbitProperties 配置类
+  rabbitmq:
+    host: 172.16.1.221 # RabbitMQ 服务的地址
+    port: 5672 # RabbitMQ 服务的端口
+    username: admin # RabbitMQ 服务的账号
+    password: admin123 # RabbitMQ 服务的密码
+  # Kafka 配置项,对应 KafkaProperties 配置类
+  kafka:
+    bootstrap-servers: 172.16.8.100:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔
+
+--- #################### 定时任务相关配置 ####################
+xxl:
+  job:
+    enabled: true # 是否开启调度中心,默认为 true 开启
+    admin:
+      addresses: http://172.16.216.133:9090/xxl-job-admin # 调度中心部署跟地址
+
+--- #################### 服务保障相关配置 ####################
+
+# Lock4j 配置项
+lock4j:
+  acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
+  expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
+
+--- #################### 监控相关配置 ####################
+
+# Actuator 监控端点的配置项
+management:
+  endpoints:
+    web:
+      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
+      exposure:
+        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
+
+# Spring Boot Admin 配置项
+spring:
+  boot:
+    admin:
+      # Spring Boot Admin Client 客户端的相关配置
+      client:
+        instance:
+          service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]
+      # Spring Boot Admin Server 服务端的相关配置
+      context-path: /admin # 配置 Spring
+
+# 日志文件配置
+logging:
+  level:
+    # 配置自己写的 MyBatis Mapper 打印日志
+    com.iailab.module.infra.dal.mysql: debug
+    com.iailab.module.infra.dal.mysql.logger.ApiErrorLogMapper: INFO # 配置 ApiErrorLogMapper 的日志级别为 info,避免和 GlobalExceptionHandler 重复打印
+    com.iailab.module.infra.dal.mysql.file.FileConfigMapper: INFO # 配置 FileConfigMapper 的日志级别为 info
+
+--- #################### 定时任务相关配置 ####################
+
+xxl:
+  job:
+    executor:
+      appname: ${spring.application.name} # 执行器 AppName
+      logpath: D:/DLUT/IailabPlat/webapp/infra/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径
+    accessToken: default_token # 执行器通讯TOKEN
+
+--- #################### 平台相关配置 ####################
+
+# 平台配置项,设置当前项目所有自定义的配置
+iailab:
+  env: # 多环境的配置项
+    tag: ${HOSTNAME}
+  security:
+    mock-enable: true
+  access-log: # 访问日志的配置项
+    enable: true
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 9ea8ff9..d2c7436 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
@@ -81,8 +81,8 @@
     map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
   global-config:
     db-config:
-      id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
-      #      id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库
+      #id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
+      id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库
       #      id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
       #      id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解
       logic-delete-value: 1 # 逻辑已删除值(默认为 1)
diff --git a/iailab-module-infra/iailab-module-infra-biz/src/main/resources/mapper/monitordisk/MonitorDiskMapper.xml b/iailab-module-infra/iailab-module-infra-biz/src/main/resources/mapper/monitordisk/MonitorDiskMapper.xml
new file mode 100644
index 0000000..a6cf1ad
--- /dev/null
+++ b/iailab-module-infra/iailab-module-infra-biz/src/main/resources/mapper/monitordisk/MonitorDiskMapper.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.iailab.module.infra.dal.mysql.monitordisk.MonitorDiskMapper">
+
+	<select id="getMonitorDiskInfo" resultType="com.iailab.module.infra.dal.dataobject.monitordisk.MonitorDiskDO">
+		WITH RankedData AS (
+			SELECT
+				host_name,
+				host_ip,
+				disk,
+				disk_name,
+				space_total,
+				space_used,
+				create_time,
+				ROW_NUMBER() OVER (PARTITION BY host_name, disk, disk_name ORDER BY create_time DESC) AS rn
+			FROM
+				system_monitor_disk
+		)
+		SELECT
+			host_name,
+			host_ip,
+			disk,
+			disk_name,
+			space_total,
+			space_used
+		FROM
+			RankedData
+		WHERE
+			rn = 1
+		<if test="params.hostName != null and params.hostName != ''">
+			and host_name = #{params.hostName}
+		</if>
+		<if test="params.hostIp != null and params.hostIp != ''">
+			and host_ip = #{params.hostIp}
+		</if>
+		<if test="params.createTime != null and params.createTime != ''">
+			and create_time between #{params.createTime[0]} and #{params.createTime[1]}
+		</if>
+	</select>
+
+</mapper>
\ No newline at end of file
diff --git a/iailab-module-model/iailab-module-model-biz/src/main/resources/application-prod.yaml b/iailab-module-model/iailab-module-model-biz/src/main/resources/application-prod.yaml
new file mode 100644
index 0000000..442b277
--- /dev/null
+++ b/iailab-module-model/iailab-module-model-biz/src/main/resources/application-prod.yaml
@@ -0,0 +1,106 @@
+--- #################### db configuration ####################
+spring:
+  autoconfigure:
+    exclude:
+      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
+      - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration
+  boot:
+    admin:
+      client:
+        instance:
+          service-host-type: IP
+  datasource:
+    druid:
+      web-stat-filter:
+        enabled: true
+      stat-view-servlet:
+        enabled: true
+        allow:
+        url-pattern: /druid/*
+        login-username:
+        login-password:
+      filter:
+        stat:
+          enabled: true
+          log-slow-sql: true
+          slow-sql-millis: 100
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
+    dynamic:
+      druid:
+        initial-size: 1
+        min-idle: 1
+        max-active: 20
+        max-wait: 600000
+        time-between-eviction-runs-millis: 60000
+        min-evictable-idle-time-millis: 300000
+        max-evictable-idle-time-millis: 900000
+        validation-query: SELECT 1 FROM DUAL
+        test-while-idle: true
+        test-on-borrow: false
+        test-on-return: false
+      primary: master
+      datasource:
+        master:
+          url: jdbc:mysql://172.16.8.100:3306/iailab_expert_master?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+          username: root
+          password: 123456
+  redis:
+    host: 172.16.8.100
+    port: 6379
+    database: 0
+    password: 123456
+
+xxl:
+  job:
+    enabled: true
+    admin:
+      addresses: http://172.16.8.100:9090/xxl-job-admin
+
+lock4j:
+  acquire-timeout: 3000
+  expire: 30000
+
+management:
+  endpoints:
+    web:
+      base-path: /actuator
+      exposure:
+        include: '*'
+
+logging:
+  level:
+    com.iailab.module.system.dal.mysql: debug
+    com.iailab.module.system.dal.mysql.sensitiveword.SensitiveWordMapper: INFO
+    com.iailab.module.system.dal.mysql.sms.SmsChannelMapper: INFO
+iailab:
+  env:
+    tag: ${HOSTNAME}
+  captcha:
+    enable: false
+  security:
+    mock-enable: true
+  xss:
+    enable: false
+    exclude-urls:
+      - ${spring.boot.admin.context-path}/**
+      - ${management.endpoints.web.base-path}/**
+  access-log:
+    enable: false
+  demo: false
+influx-db:
+  org: iailab
+  token: _338h4Kbu2KQaes5QwAyOz9pTUueXoSF9XmPi8N9oTS1SrhTZVj4J9JfSraUyWA0PfWMZOlf9QWax-USkJQR_A==
+  url: http://172.16.1.221:8086
+  username: iailab
+  password: iailab2019
+iems:
+  upload-dir: D:/DLUT/upload/
+mpk:
+  bak-file-path: D:\DLUT\mpkBakFile
+  bak-resources: D:\DLUT\mpkResources
+  model-file-path: D:\DLUT\MDK\Model\miail\
+`mablab:
+  bak-file-path: D:\DLUT\matlabBakFile`
\ No newline at end of file
diff --git a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/permission/DataScopeEnum.java b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/permission/DataScopeEnum.java
index 869265f..25c9747 100644
--- a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/permission/DataScopeEnum.java
+++ b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/permission/DataScopeEnum.java
@@ -1,6 +1,6 @@
 package com.iailab.module.system.enums.permission;
 
-import com.iailab.framework.common.core.IntArrayValuable;
+import com.iailab.framework.common.core.ArrayValuable;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
@@ -15,7 +15,7 @@
  */
 @Getter
 @AllArgsConstructor
-public enum DataScopeEnum implements IntArrayValuable {
+public enum DataScopeEnum implements ArrayValuable {
 
     ALL(1), // 全部数据权限
 
@@ -30,10 +30,10 @@
      */
     private final Integer scope;
 
-    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DataScopeEnum::getScope).toArray();
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(DataScopeEnum::getScope).toArray(Integer[]::new);
 
     @Override
-    public int[] array() {
+    public Integer[] array() {
         return ARRAYS;
     }
 
diff --git a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/sms/SmsSceneEnum.java b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/sms/SmsSceneEnum.java
index c2892e9..9eb52db 100644
--- a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/sms/SmsSceneEnum.java
+++ b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/sms/SmsSceneEnum.java
@@ -1,7 +1,7 @@
 package com.iailab.module.system.enums.sms;
 
 import cn.hutool.core.util.ArrayUtil;
-import com.iailab.framework.common.core.IntArrayValuable;
+import com.iailab.framework.common.core.ArrayValuable;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
@@ -14,7 +14,7 @@
  */
 @Getter
 @AllArgsConstructor
-public enum SmsSceneEnum implements IntArrayValuable {
+public enum SmsSceneEnum implements ArrayValuable {
 
     MEMBER_LOGIN(1, "user-sms-login", "会员用户 - 手机号登陆"),
     MEMBER_UPDATE_MOBILE(2, "user-update-mobile", "会员用户 - 修改手机"),
@@ -23,7 +23,7 @@
 
     ADMIN_MEMBER_LOGIN(21, "admin-sms-login", "后台用户 - 手机号登录");
 
-    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SmsSceneEnum::getScene).toArray();
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(SmsSceneEnum::getScene).toArray(Integer[]::new);
 
     /**
      * 验证场景的编号
@@ -39,7 +39,7 @@
     private final String description;
 
     @Override
-    public int[] array() {
+    public Integer[] array() {
         return ARRAYS;
     }
 
diff --git a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/social/SocialTypeEnum.java b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/social/SocialTypeEnum.java
index b309fb9..1905e6d 100644
--- a/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/social/SocialTypeEnum.java
+++ b/iailab-module-system/iailab-module-system-api/src/main/java/com/iailab/module/system/enums/social/SocialTypeEnum.java
@@ -1,7 +1,7 @@
 package com.iailab.module.system.enums.social;
 
 import cn.hutool.core.util.ArrayUtil;
-import com.iailab.framework.common.core.IntArrayValuable;
+import com.iailab.framework.common.core.ArrayValuable;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
@@ -14,7 +14,7 @@
  */
 @Getter
 @AllArgsConstructor
-public enum SocialTypeEnum implements IntArrayValuable {
+public enum SocialTypeEnum implements ArrayValuable {
 
     /**
      * Gitee
@@ -55,7 +55,7 @@
     WECHAT_MINI_APP(34, "WECHAT_MINI_APP"),
     ;
 
-    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SocialTypeEnum::getType).toArray();
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(SocialTypeEnum::getType).toArray(Integer[]::new);
 
     /**
      * 类型
@@ -67,7 +67,7 @@
     private final String source;
 
     @Override
-    public int[] array() {
+    public Integer[] array() {
         return ARRAYS;
     }
 
diff --git a/iailab-module-system/iailab-module-system-biz/proguard.cfg b/iailab-module-system/iailab-module-system-biz/proguard.cfg
new file mode 100644
index 0000000..da2c034
--- /dev/null
+++ b/iailab-module-system/iailab-module-system-biz/proguard.cfg
@@ -0,0 +1,132 @@
+#指定Java的版本
+-target 1.8
+#proguard会对代码进行优化压缩,他会删除从未使用的类或者类成员变量等
+-dontshrink
+#是否关闭字节码级别的优化,如果不开启则设置如下配置
+-dontoptimize
+#混淆时不生成大小写混合的类名,默认是可以大小写混合
+-dontusemixedcaseclassnames
+# 对于类成员的命名的混淆采取唯一策略
+-useuniqueclassmembernames
+#混淆时不生成大小写混合的类名,默认是可以大小写混合
+-dontusemixedcaseclassnames
+#混淆类名之后,对使用Class.forName('className')之类的地方进行相应替代
+-adaptclassstrings
+
+-verbose
+-printmapping proguard-mapping.txt  # 生成混淆映射表(用于调试)
+
+#对异常、注解信息予以保留
+-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
+# 此选项将保存接口中的所有原始名称(不混淆)-->
+-keepnames interface ** { *; }
+# 此选项将保存所有软件包中的所有原始接口文件(不进行混淆)
+#-keep interface * extends * { *; }
+#保留参数名,因为控制器,或者Mybatis等接口的参数如果混淆会导致无法接受参数,xml文件找不到参数
+-keepparameternames
+# 保留枚举成员及方法
+-keepclassmembers enum * { *; }
+# 不混淆所有类,保存原始定义的注释-
+-keepclassmembers class * {
+                        @org.springframework.context.annotation.Bean *;
+                        @org.springframework.beans.factory.annotation.Autowired *;
+                        @org.springframework.beans.factory.annotation.Value *;
+                        @org.springframework.stereotype.Service *;
+                        @org.springframework.stereotype.Component *;
+                        }
+
+#忽略warn消息
+-ignorewarnings
+#忽略note消息
+-dontnote
+#打印配置信息
+-printconfiguration
+-keep public class com.iailab.module.system.SystemServerApplication {
+        public static void main(java.lang.String[]);
+    }
+ # 保留Feign客户端接口
+ -keep @org.springframework.cloud.openfeign.FeignClient class * {
+     *;
+ }
+ # 业务包保留
+ -keep class com.iailab.module.system.api.** { *; }
+
+ # 日志与反射
+ -keepclassmembers class * {
+     org.slf4j.Logger *;
+ }
+ -keepattributes RuntimeVisibleAnnotations, Signature
+# 保留common模块的公共类
+-keep class com.iailab.framework.** { *; }
+
+# 保留mybatis-plus相关类
+-keep class com.baomidou.mybatisplus.** { *; }
+-keep @com.baomidou.mybatisplus.annotation.TableName class * { *; }
+
+# 保留数据权限注解
+-keep @com.iailab.framework.datapermission.core.** class *
+
+ # 保留Spring Cloud配置类
+ -keep class org.springframework.cloud.** { *; }
+
+ # 保留domain实体类
+ -keep class com.iailab.module.*.dal.dataobject.** { *; }
+
+ # 保留DTO和VO
+ -keep class com.iailab.module.*.controller.**.vo.** { *; }
+ -keep class com.iailab.module.*.service.**.dto.** { *; }
+
+ # 保留JSON注解
+ -keep @com.fasterxml.jackson.annotation.JsonInclude class *
+ -keepclassmembers class * {
+     @com.fasterxml.jackson.annotation.JsonIgnore *;
+     @com.fasterxml.jackson.annotation.JsonProperty *;
+ }
+ -keep @io.swagger.v3.oas.annotations.Operation class *
+ # 保留路由断言工厂类
+ -keep class org.springframework.cloud.gateway.handler.predicate.** { *; }
+
+ # 保留若依自定义过滤器
+ -keep class com.iailab.gateway.filters.** { *; }
+
+ # 保留所有Spring组件类的原始名称
+ -keepnames @org.springframework.stereotype.Component class *
+ -keepnames @org.springframework.stereotype.Service class *
+ -keepnames @org.springframework.stereotype.Repository class *
+
+ # 保留@Bean方法的名称
+ -keepclassmembernames class * {
+     @org.springframework.context.annotation.Bean *;
+ }
+ 
+ # 保留所有 Spring 注解及组件
+ -keep @org.springframework.stereotype.Component class * { *; }
+ -keep @org.springframework.context.annotation.Configuration class * { *; }
+ 
+ # 保留配置类及字段
+ -keep @org.springframework.boot.context.properties.ConfigurationProperties class * { *; }
+ -keepclassmembers @org.springframework.boot.context.properties.ConfigurationProperties class * { *; }
+ 
+ # 保留关键业务模块
+ -keep class com.iailab.module.system.framework.sms.** { *; }
+ -keepclassmembers class com.iailab.module.system.framework.sms.** { *; }
+
+ -keep class com.iailab.module.system.convert.**.** { *; }
+  -keepclassmembers class com.iailab.module.system.convert.**.** { *; }
+ 
+ # 保留 Setter 和资源文件
+ -keepclassmembers class * { void set*(***); }
+
+ # ========== SPI 相关保留 ==========
+ -keep interface com.xingyuv.captcha.service.** { *; }
+ -keep class * implements com.xingyuv.captcha.service.** { *; }
+
+ # ========== Spring 自动配置类 ==========
+ -keep @org.springframework.boot.autoconfigure.AutoConfiguration class * { *; }
+ -keep class com.xingyuv.captcha.config.** { *; }
+
+ # ========== 明确保留报错类 ==========
+ -keep class com.iailab.module.system.framework.captcha.core.RedisCaptchaServiceImpl {
+     public <init>();
+     public *;
+ }
\ No newline at end of file
diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/permission/MenuMapper.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/permission/MenuMapper.java
index 2f8e230..103d57c 100644
--- a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/permission/MenuMapper.java
+++ b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/dal/mysql/permission/MenuMapper.java
@@ -22,6 +22,7 @@
 
     default List<MenuDO> selectList(MenuListReqVO reqVO) {
         return selectList(new LambdaQueryWrapperX<MenuDO>()
+                .eq(MenuDO::getStatus, reqVO.getStatus())
                 .likeIfPresent(MenuDO::getName, reqVO.getName()));
     }
 
diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/job/monitor/MonitorDiskJob.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/job/monitor/MonitorDiskJob.java
new file mode 100644
index 0000000..7542143
--- /dev/null
+++ b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/job/monitor/MonitorDiskJob.java
@@ -0,0 +1,49 @@
+//package com.iailab.module.system.job.monitor;
+//
+//import com.iailab.framework.tenant.core.aop.TenantIgnore;
+//import com.iailab.module.infra.api.monitor.MonitorApi;
+//import com.iailab.module.infra.api.monitor.dto.MonitorDiskDTO;
+//import com.iailab.module.infra.util.ServerInfoCollector;
+//import com.xxl.job.core.handler.annotation.XxlJob;
+//import org.slf4j.Logger;
+//import org.slf4j.LoggerFactory;
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.stereotype.Component;
+//import org.springframework.transaction.annotation.Transactional;
+//
+//import java.io.IOException;
+//import java.util.Date;
+//import java.util.List;
+//import java.util.concurrent.atomic.AtomicInteger;
+//
+//@Component
+//public class MonitorDiskJob {
+//
+//    private Logger logger = LoggerFactory.getLogger(getClass());
+//
+//    private final AtomicInteger counts = new AtomicInteger();
+//
+//    private static final Object lock = new Object();
+//
+//    private final MonitorApi monitorApi;
+//
+//    @Value("${spring.application.name}")
+//    public String serverName;
+//
+//    public MonitorDiskJob(MonitorApi monitorApi) {
+//        this.monitorApi = monitorApi;
+//    }
+//
+//    @XxlJob("monitorDiskJob")
+//    @TenantIgnore
+//    @Transactional
+//    public void execute() throws IOException {
+//        synchronized (lock) {
+//            logger.info("[execute][定时第 ({}) 次执行]", counts.incrementAndGet());
+//            System.out.println(new Date() + ": 我是系统服务system-server-服务器磁盘监控日志存储定时任务");
+//            List<MonitorDiskDTO> monitorDiskDTOS = ServerInfoCollector.collectMonitorDisk();
+//            monitorApi.reportDiskInfo(monitorDiskDTOS);
+//        }
+//    }
+//
+//}
diff --git a/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/job/monitor/MonitorMemJob.java b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/job/monitor/MonitorMemJob.java
new file mode 100644
index 0000000..91aeb9f
--- /dev/null
+++ b/iailab-module-system/iailab-module-system-biz/src/main/java/com/iailab/module/system/job/monitor/MonitorMemJob.java
@@ -0,0 +1,48 @@
+//package com.iailab.module.system.job.monitor;
+//
+//import com.iailab.framework.tenant.core.aop.TenantIgnore;
+//import com.iailab.module.infra.api.monitor.MonitorApi;
+//import com.iailab.module.infra.api.monitor.dto.MonitorMemDTO;
+//import com.iailab.module.infra.util.ServerInfoCollector;
+//import com.xxl.job.core.handler.annotation.XxlJob;
+//import org.slf4j.Logger;
+//import org.slf4j.LoggerFactory;
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.stereotype.Component;
+//import org.springframework.transaction.annotation.Transactional;
+//
+//import java.io.IOException;
+//import java.util.Date;
+//import java.util.concurrent.atomic.AtomicInteger;
+//
+//@Component
+//public class MonitorMemJob {
+//
+//    private Logger logger = LoggerFactory.getLogger(getClass());
+//
+//    private final AtomicInteger counts = new AtomicInteger();
+//
+//    private static final Object lock = new Object();
+//
+//    private final MonitorApi monitorApi;
+//
+//    @Value("${spring.application.name}")
+//    public String serverName;
+//
+//    public MonitorMemJob(MonitorApi monitorApi) {
+//        this.monitorApi = monitorApi;
+//    }
+//
+//    @XxlJob("monitorMemJob")
+//    @TenantIgnore
+//    @Transactional
+//    public void execute() throws IOException {
+//        synchronized (lock) {
+//            logger.info("[execute][定时第 ({}) 次执行]", counts.incrementAndGet());
+//            System.out.println(new Date() + ": 我是系统服务system-server-服务器内存监控日志存储定时任务");
+//            MonitorMemDTO monitorMemDTO = ServerInfoCollector.collectMonitorMem(serverName);
+//            monitorApi.reportMemInfo(monitorMemDTO);
+//        }
+//    }
+//
+//}
diff --git a/iailab-module-system/iailab-module-system-biz/src/main/resources/application-prod.yaml b/iailab-module-system/iailab-module-system-biz/src/main/resources/application-prod.yaml
new file mode 100644
index 0000000..ad88744
--- /dev/null
+++ b/iailab-module-system/iailab-module-system-biz/src/main/resources/application-prod.yaml
@@ -0,0 +1,116 @@
+--- #################### 数据库相关配置 ####################
+spring:
+  # 数据源配置项
+  autoconfigure:
+    exclude:
+      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源
+  datasource:
+    druid: # Druid 【监控】相关的全局配置
+      web-stat-filter:
+        enabled: true
+      stat-view-servlet:
+        enabled: true
+        allow: # 设置白名单,不填则允许所有访问
+        url-pattern: /druid/*
+        login-username: # 控制台管理用户名和密码
+        login-password:
+      filter:
+        stat:
+          enabled: true
+          log-slow-sql: true # 慢 SQL 记录
+          slow-sql-millis: 100
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
+    dynamic: # 多数据源配置
+      druid: # Druid 【连接池】相关的全局配置
+        initial-size: 5 # 初始连接数
+        min-idle: 10 # 最小连接池数量
+        max-active: 20 # 最大连接池数量
+        max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
+        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
+        min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
+        max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
+        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
+        test-while-idle: true
+        test-on-borrow: false
+        test-on-return: false
+      primary: master
+      datasource:
+        master:
+          url: jdbc:mysql://172.16.8.100:3306/iailab_plat_system?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
+          username: root
+          password: 123456
+        slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改
+          lazy: true # 开启懒加载,保证启动速度
+          url: jdbc:mysql://127.0.0.1:3306/iailab_plat_system?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
+          username: root
+          password: 123456
+
+  # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
+  redis:
+    host: 127.0.0.1 # 地址
+    port: 6379 # 端口
+    database: 1 # 数据库索引
+    password: 123456 # 密码,建议生产环境开启
+
+--- #################### MQ 消息队列相关配置 ####################
+
+# rocketmq 配置项,对应 RocketMQProperties 配置类
+rocketmq:
+  name-server: 127.0.0.1:9876 # RocketMQ Namesrv
+
+spring:
+  # RabbitMQ 配置项,对应 RabbitProperties 配置类
+  rabbitmq:
+    host: 172.16.1.221 # RabbitMQ 服务的地址
+    port: 5672 # RabbitMQ 服务的端口
+    username: admin # RabbitMQ 服务的账号
+    password: admin123 # RabbitMQ 服务的密码
+  # Kafka 配置项,对应 KafkaProperties 配置类
+  kafka:
+    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔
+
+--- #################### 定时任务相关配置 ####################
+xxl:
+  job:
+    enabled: true # 是否开启调度中心,默认为 true 开启
+    admin:
+      addresses: http://172.16.216.133:9090/xxl-job-admin # 调度中心部署跟地址
+
+--- #################### 服务保障相关配置 ####################
+
+# Lock4j 配置项
+lock4j:
+  acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
+  expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
+
+--- #################### 监控相关配置 ####################
+
+# Actuator 监控端点的配置项
+management:
+  endpoints:
+    web:
+      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
+      exposure:
+        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
+
+--- #################### 平台相关配置 ####################
+
+# 平台配置项,设置当前项目所有自定义的配置
+iailab:
+  env: # 多环境的配置项
+    tag: ${HOSTNAME}
+  security:
+    mock-enable: true
+  access-log: # 访问日志的配置项
+    enable: false
+  xss:
+    enable: false
+    exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系
+      - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
+      - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
+  demo: false # 开启演示模式
+  captcha:
+    enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试
diff --git a/iailab-system/log/system-server.log b/iailab-system/log/system-server.log
new file mode 100644
index 0000000..400714f
--- /dev/null
+++ b/iailab-system/log/system-server.log
@@ -0,0 +1,188 @@
+2025-06-05 15:13:24.253 |  INFO 51220 | main [TID: N/A] c.i.m.system.SystemServerApplication     | Starting SystemServerApplication using Java 17.0.11 on Thinkpad-E14 with PID 51220 (D:\Work\Yingdashi\Project\iailab-plat\iailab-plat\iailab-module-system\iailab-module-system-biz\target\classes started by houzh in D:\Work\Yingdashi\Project\iailab-plat\iailab-plat)
+2025-06-05 15:13:24.258 |  INFO 51220 | main [TID: N/A] c.i.m.system.SystemServerApplication     | The following 1 profile is active: "test"
+2025-06-05 15:13:26.733 |  INFO 51220 | main [TID: N/A] c.a.c.n.c.NacosConfigDataLoader          | [Nacos Config] Load config[dataId=system-server-test.yaml, group=DEFAULT_GROUP] success
+2025-06-05 15:13:42.105 |  INFO 51220 | main [TID: N/A] o.s.cloud.context.scope.GenericScope     | BeanFactory id=c0d8b71f-8806-3bf3-ab6e-9e72d53a8b49
+2025-06-05 15:13:43.819 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'com.mzt.logapi.starter.configuration.LogRecordProxyAutoConfiguration' of type [com.mzt.logapi.starter.configuration.LogRecordProxyAutoConfiguration$$EnhancerBySpringCGLIB$$e92f3e7e] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:43.910 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'org.springframework.cloud.commons.config.CommonsConfigAutoConfiguration' of type [org.springframework.cloud.commons.config.CommonsConfigAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:43.932 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration' of type [org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:43.944 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'loadBalancerClientsDefaultsMappingsProvider' of type [org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration$$Lambda$658/0x000002ad116ff088] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:43.983 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'defaultsBindHandlerAdvisor' of type [org.springframework.cloud.commons.config.DefaultsBindHandlerAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:44.025 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'mzt.log.record-com.mzt.logapi.starter.configuration.LogRecordProperties' of type [com.mzt.logapi.starter.configuration.LogRecordProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:44.092 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'logRecordPerformanceMonitor' of type [com.mzt.logapi.service.impl.DefaultLogRecordPerformanceMonitor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:45.717 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'com.iailab.framework.datapermission.config.IailabDataPermissionAutoConfiguration' of type [com.iailab.framework.datapermission.config.IailabDataPermissionAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:45.774 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'dataPermissionAnnotationAdvisor' of type [com.iailab.framework.datapermission.core.aop.DataPermissionAnnotationAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:45.996 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'com.iailab.framework.tenant.config.IailabTenantAutoConfiguration' of type [com.iailab.framework.tenant.config.IailabTenantAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:46.156 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'dsProcessor' of type [com.iailab.framework.tenant.core.db.dynamic.TenantDsProcessor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:46.382 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'org.apache.rocketmq.spring.autoconfigure.ListenerContainerConfiguration' of type [org.apache.rocketmq.spring.autoconfigure.ListenerContainerConfiguration$$EnhancerBySpringCGLIB$$408f94c] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:46.397 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'org.apache.rocketmq.spring.autoconfigure.MessageConverterConfiguration' of type [org.apache.rocketmq.spring.autoconfigure.MessageConverterConfiguration$$EnhancerBySpringCGLIB$$accf02b0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:46.686 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'createRocketMQMessageConverter' of type [org.apache.rocketmq.spring.support.RocketMQMessageConverter] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:46.725 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'rocketmq-org.apache.rocketmq.spring.autoconfigure.RocketMQProperties' of type [org.apache.rocketmq.spring.autoconfigure.RocketMQProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:46.756 |  INFO 51220 | main [TID: N/A] trationDelegate$BeanPostProcessorChecker | Bean 'rocketMQMessageListenerContainerRegistrar' of type [org.apache.rocketmq.spring.support.RocketMQMessageListenerContainerRegistrar] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
+2025-06-05 15:13:49.329 |  INFO 51220 | main [TID: N/A] o.s.b.w.embedded.tomcat.TomcatWebServer  | Tomcat initialized with port(s): 48081 (http)
+2025-06-05 15:13:49.334 |  INFO 51220 | main [TID: N/A] o.a.catalina.core.AprLifecycleListener   | Loaded Apache Tomcat Native library [1.3.0] using APR version [1.7.4].
+2025-06-05 15:13:49.337 |  INFO 51220 | main [TID: N/A] o.a.catalina.core.AprLifecycleListener   | APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true], UDS [true].
+2025-06-05 15:13:49.338 |  INFO 51220 | main [TID: N/A] o.a.catalina.core.AprLifecycleListener   | APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
+2025-06-05 15:13:49.425 |  INFO 51220 | main [TID: N/A] o.a.catalina.core.AprLifecycleListener   | OpenSSL successfully initialized [OpenSSL 3.0.13 30 Jan 2024]
+2025-06-05 15:13:49.523 |  INFO 51220 | main [TID: N/A] o.apache.catalina.core.StandardService   | Starting service [Tomcat]
+2025-06-05 15:13:49.523 |  INFO 51220 | main [TID: N/A] org.apache.catalina.core.StandardEngine  | Starting Servlet engine: [Apache Tomcat/9.0.83]
+2025-06-05 15:13:50.780 |  INFO 51220 | main [TID: N/A] o.a.c.c.C.[Tomcat].[localhost].[/]       | Initializing Spring embedded WebApplicationContext
+2025-06-05 15:13:50.781 |  INFO 51220 | main [TID: N/A] w.s.c.ServletWebServerApplicationContext | Root WebApplicationContext: initialization completed in 24017 ms
+2025-06-05 15:13:52.077 |  INFO 51220 | main [TID: N/A] o.s.c.openfeign.FeignClientFactoryBean   | For 'infra-server' URL not provided. Will try picking an instance via load-balancing.
+2025-06-05 15:13:52.546 |  INFO 51220 | main [TID: N/A] o.s.c.openfeign.FeignClientFactoryBean   | For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+2025-06-05 15:13:55.197 |  WARN 51220 | Thread-6 [TID: N/A] c.a.nacos.common.notify.NotifyCenter     | [NotifyCenter] Start destroying Publisher
+2025-06-05 15:13:55.200 |  WARN 51220 | Thread-6 [TID: N/A] c.a.nacos.common.notify.NotifyCenter     | [NotifyCenter] Destruction of the end
+2025-06-05 15:13:55.196 |  WARN 51220 | Thread-1 [TID: N/A] c.a.n.common.http.HttpClientBeanHolder   | [HttpClientBeanHolder] Start destroying common HttpClient
+2025-06-05 15:13:55.212 |  WARN 51220 | Thread-1 [TID: N/A] c.a.n.common.http.HttpClientBeanHolder   | [HttpClientBeanHolder] Destruction of the end
+2025-06-05 15:13:56.095 |  INFO 51220 | main [TID: N/A] com.alibaba.druid.pool.DruidDataSource   | {dataSource-1,master} inited
+2025-06-05 15:13:56.123 |  INFO 51220 | main [TID: N/A] c.b.d.d.DynamicRoutingDataSource         | dynamic-datasource - add a datasource named [slave] success
+2025-06-05 15:13:56.133 |  INFO 51220 | main [TID: N/A] c.b.d.d.DynamicRoutingDataSource         | dynamic-datasource - add a datasource named [master] success
+2025-06-05 15:13:56.134 |  INFO 51220 | main [TID: N/A] c.b.d.d.DynamicRoutingDataSource         | dynamic-datasource initial loaded [2] datasource,primary datasource named [master]
+2025-06-05 15:13:57.222 |  INFO 51220 | main [TID: N/A] o.s.c.openfeign.FeignClientFactoryBean   | For 'system-server' URL not provided. Will try picking an instance via load-balancing.
+2025-06-05 15:14:02.825 |  INFO 51220 | main [TID: N/A] o.s.c.openfeign.FeignClientFactoryBean   | For 'infra-server' URL not provided. Will try picking an instance via load-balancing.
+2025-06-05 15:14:02.836 |  INFO 51220 | main [TID: N/A] o.s.c.openfeign.FeignClientFactoryBean   | For 'infra-server' URL not provided. Will try picking an instance via load-balancing.
+2025-06-05 15:14:05.089 |  INFO 51220 | main [TID: N/A] org.redisson.Version                     | Redisson 3.18.0
+2025-06-05 15:14:06.237 |  INFO 51220 | redisson-netty-2-9 [TID: N/A] o.r.c.pool.MasterPubSubConnectionPool    | 1 connections initialized for 172.16.8.100/172.16.8.100:6379
+2025-06-05 15:14:06.391 |  INFO 51220 | redisson-netty-2-19 [TID: N/A] o.r.c.pool.MasterConnectionPool          | 24 connections initialized for 172.16.8.100/172.16.8.100:6379
+2025-06-05 15:14:06.967 |  INFO 51220 | main [TID: N/A] o.s.c.openfeign.FeignClientFactoryBean   | For 'infra-server' URL not provided. Will try picking an instance via load-balancing.
+2025-06-05 15:14:07.268 |  INFO 51220 | main [TID: N/A] c.x.c.s.i.BlockPuzzleCaptchaServiceImpl  | --->>>xingyuv captcha-plus 初始化验证码底图<<<---blockPuzzle
+2025-06-05 15:14:07.595 | ERROR 51220 | main [TID: N/A] c.x.c.s.i.BlockPuzzleCaptchaServiceImpl  | load font error:{}
+
+java.io.IOException: Problem reading font data.
+	at java.desktop/java.awt.Font.createFont0(Font.java:1208) ~[na:na]
+	at java.desktop/java.awt.Font.createFont(Font.java:1076) ~[na:na]
+	at com.xingyuv.captcha.service.impl.AbstractCaptchaService.loadWaterMarkFont(AbstractCaptchaService.java:210) ~[captcha-plus-1.0.8.jar:na]
+	at com.xingyuv.captcha.service.impl.AbstractCaptchaService.init(AbstractCaptchaService.java:100) ~[captcha-plus-1.0.8.jar:na]
+	at com.xingyuv.captcha.service.impl.BlockPuzzleCaptchaServiceImpl.init(BlockPuzzleCaptchaServiceImpl.java:34) ~[captcha-plus-1.0.8.jar:na]
+	at com.xingyuv.captcha.service.impl.CaptchaServiceFactory.getInstance(CaptchaServiceFactory.java:36) ~[captcha-plus-1.0.8.jar:na]
+	at com.xingyuv.captcha.config.AjCaptchaServiceAutoConfiguration.captchaService(AjCaptchaServiceAutoConfiguration.java:63) ~[spring-boot-starter-captcha-plus-1.0.8.jar:1.0.8]
+	at com.xingyuv.captcha.config.AjCaptchaServiceAutoConfiguration$$EnhancerBySpringCGLIB$$efe47e61.CGLIB$captchaService$0(<generated>) ~[spring-boot-starter-captcha-plus-1.0.8.jar:1.0.8]
+	at com.xingyuv.captcha.config.AjCaptchaServiceAutoConfiguration$$EnhancerBySpringCGLIB$$efe47e61$$FastClassBySpringCGLIB$$2b9e829c.invoke(<generated>) ~[spring-boot-starter-captcha-plus-1.0.8.jar:1.0.8]
+	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.3.39.jar:5.3.39]
+	at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-5.3.39.jar:5.3.39]
+	at com.xingyuv.captcha.config.AjCaptchaServiceAutoConfiguration$$EnhancerBySpringCGLIB$$efe47e61.captchaService(<generated>) ~[spring-boot-starter-captcha-plus-1.0.8.jar:1.0.8]
+	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
+	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
+	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
+	at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
+	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:641) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:626) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:336) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:214) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeanByName(AbstractAutowireCapableBeanFactory.java:479) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:554) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:524) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:677) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:228) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:329) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:336) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:209) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1391) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:548) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:524) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:677) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:228) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:329) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:336) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:209) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:591) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:409) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1289) ~[spring-boot-2.7.18.jar:2.7.18]
+	at com.iailab.module.system.SystemServerApplication.main(SystemServerApplication.java:17) ~[classes/:na]
+
+2025-06-05 15:14:07.604 |  WARN 51220 | main [TID: N/A] ConfigServletWebServerApplicationContext | Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authController': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'adminAuthServiceImpl': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'captchaService' defined in class path resource [com/xingyuv/captcha/config/AjCaptchaServiceAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.xingyuv.captcha.service.CaptchaService]: Factory method 'captchaService' threw exception; nested exception is java.lang.IllegalStateException: Shutdown in progress
+2025-06-05 15:14:07.657 |  INFO 51220 | main [TID: N/A] c.b.d.d.DynamicRoutingDataSource         | dynamic-datasource start closing ....
+2025-06-05 15:14:07.663 |  INFO 51220 | main [TID: N/A] com.alibaba.druid.pool.DruidDataSource   | {dataSource-0} closing ...
+2025-06-05 15:14:07.663 |  INFO 51220 | main [TID: N/A] c.b.d.d.d.DefaultDataSourceDestroyer     | dynamic-datasource close the datasource named [slave] success,
+2025-06-05 15:14:07.663 |  INFO 51220 | main [TID: N/A] com.alibaba.druid.pool.DruidDataSource   | {dataSource-1} closing ...
+2025-06-05 15:14:07.674 |  INFO 51220 | main [TID: N/A] com.alibaba.druid.pool.DruidDataSource   | {dataSource-1} closed
+2025-06-05 15:14:07.675 |  INFO 51220 | main [TID: N/A] c.b.d.d.d.DefaultDataSourceDestroyer     | dynamic-datasource close the datasource named [master] success,
+2025-06-05 15:14:07.675 |  INFO 51220 | main [TID: N/A] c.b.d.d.DynamicRoutingDataSource         | dynamic-datasource all closed success,bye
+2025-06-05 15:14:07.677 |  WARN 51220 | main [TID: N/A] s.c.a.AnnotationConfigApplicationContext | Exception thrown from ApplicationListener handling ContextClosedEvent
+
+org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'rabbitConnectionFactory': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
+	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:220) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:214) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.context.event.AbstractApplicationEventMulticaster.retrieveApplicationListeners(AbstractApplicationEventMulticaster.java:264) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.event.AbstractApplicationEventMulticaster.getApplicationListeners(AbstractApplicationEventMulticaster.java:221) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:140) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:430) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:436) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:387) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1072) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:1035) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.cloud.context.named.NamedContextFactory.destroy(NamedContextFactory.java:99) ~[spring-cloud-context-3.1.8.jar:3.1.8]
+	at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:213) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:587) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:559) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1163) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:520) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1156) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1123) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:604) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:409) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1289) ~[spring-boot-2.7.18.jar:2.7.18]
+	at com.iailab.module.system.SystemServerApplication.main(SystemServerApplication.java:17) ~[classes/:na]
+
+2025-06-05 15:14:07.679 |  WARN 51220 | main [TID: N/A] s.c.a.AnnotationConfigApplicationContext | Exception thrown from ApplicationListener handling ContextClosedEvent
+
+org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'rabbitConnectionFactory': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
+	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:220) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:214) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.context.event.AbstractApplicationEventMulticaster.retrieveApplicationListeners(AbstractApplicationEventMulticaster.java:264) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.event.AbstractApplicationEventMulticaster.getApplicationListeners(AbstractApplicationEventMulticaster.java:221) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:140) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:430) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:436) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:387) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1072) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:1035) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.cloud.context.named.NamedContextFactory.destroy(NamedContextFactory.java:99) ~[spring-cloud-context-3.1.8.jar:3.1.8]
+	at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:213) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:587) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:559) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1163) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:520) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1156) ~[spring-beans-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1123) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:604) ~[spring-context-5.3.39.jar:5.3.39]
+	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:409) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) ~[spring-boot-2.7.18.jar:2.7.18]
+	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1289) ~[spring-boot-2.7.18.jar:2.7.18]
+	at com.iailab.module.system.SystemServerApplication.main(SystemServerApplication.java:17) ~[classes/:na]
+
diff --git a/pom.xml b/pom.xml
index 87aa10e..e28eda3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
 
     <name>${project.artifactId}</name>
     <description>平台项目基础脚手架</description>
-    <url>http://172.16.8.100:8888/summary/iailab-plat.git</url>
+    <url>http://172.16.8.100:8888/summary/iailab-plat-v2.0.git</url>
 
     <properties>
         <revision>1.0.0</revision>
diff --git "a/\345\267\245\344\270\232\344\272\222\350\201\224\347\275\221\345\271\263\345\217\260\351\211\264\346\235\203\345\212\237\350\203\275.md" "b/\345\267\245\344\270\232\344\272\222\350\201\224\347\275\221\345\271\263\345\217\260\351\211\264\346\235\203\345\212\237\350\203\275.md"
new file mode 100644
index 0000000..ee54152
--- /dev/null
+++ "b/\345\267\245\344\270\232\344\272\222\350\201\224\347\275\221\345\271\263\345\217\260\351\211\264\346\235\203\345\212\237\350\203\275.md"
@@ -0,0 +1,269 @@
+## 工业互联网平台鉴权功能 ##
+1. 登录功能
+   登录接口: http://0.0.0.0/admin-api/system/auth/login
+
+① 参数说明
+
+      {
+      "tenantName": "tenant1", //租户名称(必传),需事先在平台配置
+      "username": "admin",     //用户名(必传),平台事先维护好用户
+      "password": "123",       //密码(必传)
+      "captchaVerification": "PfcH6mgr==" //验证码(非必传),若开启的话需要前后台同时开启
+      }
+
+其中租户名称在登录逻辑中用不到,用到的是租户对应的ID,接口请求的时候需要将Tenant-Id封装到请求头中,登录逻辑会从请求头中获取并使用,如下截图
+特别注意:平台其它接口也都需要封装此租户ID
+
+
+② 主要代码
+1、控制层接口
+
+      `@PostMapping("/login")
+      @PermitAll
+      @Operation(summary = "使用账号密码登录")
+      public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) {
+      return success(authService.login(reqVO));
+      }`
+
+2、参数对象 AuthLoginReqVO
+
+@Schema(description = "管理后台 - 账号密码登录 Request VO")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class AuthLoginReqVO {
+
+    @Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "iailabyuanma")
+    @NotEmpty(message = "登录账号不能为空")
+    @Length(min = 4, max = 16, message = "账号长度为 4-16 位")
+    @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
+    private String username;
+
+    @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao")
+    @NotEmpty(message = "密码不能为空")
+    @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+    private String password;
+
+    // ========== 图片验证码相关 ==========
+
+    @Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED,
+            example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==")
+    @NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class)
+    private String captchaVerification;
+}
+3、登录实现类
+
+    @Override
+    public AuthLoginRespVO login(AuthLoginReqVO reqVO) {
+        // 校验验证码
+        validateCaptcha(reqVO);
+
+        // 使用账号密码,进行登录
+        AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword());
+
+
+        // 创建 Token 令牌,记录登录日志
+        return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
+    }
+4、验证用户名密码
+
+    @Override
+    public AdminUserDO authenticate(String username, String password) {
+        final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME;
+        // 校验账号是否存在
+        AdminUserDO user = userService.getUserByUsername(username);
+        if (user == null) {
+            createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
+            throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
+        }
+        if (!userService.isPasswordMatch(password, user.getPassword())) {
+            createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
+            throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
+        }
+        // 校验是否禁用
+        if (CommonStatusEnum.isDisable(user.getStatus())) {
+            createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED);
+            throw exception(AUTH_LOGIN_USER_DISABLED);
+        }
+        return user;
+    }
+用户名密码校验不通过常量如下图
+
+
+
+5、创建令牌
+
+    private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
+        // 插入登陆日志
+        createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
+        // 创建访问令牌
+        OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(),
+                OAuth2ClientConstants.CLIENT_ID_DEFAULT, null);
+        // 构建返回结果
+        return AuthConvert.INSTANCE.convert(accessTokenDO);
+    }
+其中“OAuth2ClientConstants.CLIENT_ID_DEFAULT”是在平台配置好的默认客户端,主要包括授权类型、刷新token有效期、访问token有效期等
+
+
+6、创建访问令牌和刷新令牌
+
+    @Override
+    @Transactional
+    public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId, List<String> scopes) {
+        OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
+        // 创建刷新令牌
+        OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO, scopes);
+        // 创建访问令牌
+        return createOAuth2AccessToken(refreshTokenDO, clientDO);
+    }
+    private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
+        OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
+                .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType())
+                .setUserInfo(buildUserInfo(refreshTokenDO.getUserId(), refreshTokenDO.getUserType()))
+                .setClientId(clientDO.getClientId()).setScopes(refreshTokenDO.getScopes())
+                .setRefreshToken(refreshTokenDO.getRefreshToken())
+                .setExpiresTime(LocalDateTime.now().plusSeconds(clientDO.getAccessTokenValiditySeconds()));
+        accessTokenDO.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号
+        oauth2AccessTokenMapper.insert(accessTokenDO);
+        // 记录到 Redis 中
+        oauth2AccessTokenRedisDAO.set(accessTokenDO);
+        return accessTokenDO;
+    }
+    private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO, List<String> scopes) {
+        OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setRefreshToken(generateRefreshToken())
+                .setUserId(userId).setUserType(userType)
+                .setClientId(clientDO.getClientId()).setScopes(scopes)
+                .setExpiresTime(LocalDateTime.now().plusSeconds(clientDO.getRefreshTokenValiditySeconds()));
+        oauth2RefreshTokenMapper.insert(refreshToken);
+        return refreshToken;
+    }
+注意:访问令牌是请求后端接口的通行证,所有需要鉴权的后端api都需要携带访问令牌,访问令牌过期后会请求刷新令牌接口,重新获取访问令牌。 当刷新令牌过期后,就需要重新登录获取授权了!
+7、刷新令牌
+
+    @PostMapping("/refresh-token")
+    @PermitAll
+    @Operation(summary = "刷新令牌")
+    @Parameter(name = "refreshToken", description = "刷新令牌", required = true)
+    public CommonResult<AuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
+        return success(authService.refreshToken(refreshToken));
+    }
+    @Override
+    public AuthLoginRespVO refreshToken(String refreshToken) {
+        OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT);
+        return AuthConvert.INSTANCE.convert(accessTokenDO);
+    }
+    @Override
+    public OAuth2AccessTokenDO refreshAccessToken(String refreshToken, String clientId) {
+        // 查询访问令牌
+        OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectByRefreshToken(refreshToken);
+        if (refreshTokenDO == null) {
+            throw exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "无效的刷新令牌");
+        }
+
+        // 校验 Client 匹配
+        OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
+        if (ObjectUtil.notEqual(clientId, refreshTokenDO.getClientId())) {
+            throw exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "刷新令牌的客户端编号不正确");
+        }
+
+        // 移除相关的访问令牌
+        List<OAuth2AccessTokenDO> accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshToken);
+        if (CollUtil.isNotEmpty(accessTokenDOs)) {
+            oauth2AccessTokenMapper.deleteBatchIds(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getId));
+            oauth2AccessTokenRedisDAO.deleteList(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getAccessToken));
+        }
+
+        // 已过期的情况下,删除刷新令牌
+        if (DateUtils.isExpired(refreshTokenDO.getExpiresTime())) {
+            oauth2RefreshTokenMapper.deleteById(refreshTokenDO.getId());
+            throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "刷新令牌已过期");
+        }
+
+        // 创建访问令牌
+        return createOAuth2AccessToken(refreshTokenDO, clientDO);
+    }
+8、前端封装token和租户ID代码参考
+
+      // request拦截器
+      service.interceptors.request.use(
+      (config: InternalAxiosRequestConfig) => {
+      // 是否需要设置 token
+      let isToken = (config!.headers || {}).isToken === false
+      whiteList.some((v) => {
+      if (config.url && config.url.indexOf(v) > -1) {
+      return (isToken = false)
+      }
+      })
+      if (getAccessToken() && !isToken) {
+      config.headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token
+      }
+      // 设置租户
+      if (tenantEnable && tenantEnable === 'true') {
+      const tenantId = getTenantId()
+      if (tenantId) config.headers['tenant-id'] = tenantId
+      }
+      const method = config.method?.toUpperCase()
+      // 防止 GET 请求缓存
+      if (method === 'GET') {
+      config.headers['Cache-Control'] = 'no-cache'
+      config.headers['Pragma'] = 'no-cache'
+      }
+      // 自定义参数序列化函数
+      else if (method === 'POST') {
+      const contentType = config.headers['Content-Type'] || config.headers['content-type']
+      if (contentType === 'application/x-www-form-urlencoded') {
+      if (config.data && typeof config.data !== 'string') {
+      config.data = qs.stringify(config.data)
+      }
+      }
+      }
+      return config
+      },
+      (error: AxiosError) => {
+      // Do something with request error
+      console.log(error) // for debug
+      return Promise.reject(error)
+      }
+      )
+2. 退出登录功能
+   登出接口: http://0.0.0.0/admin-api/system/auth/logout
+
+
+
+核心代码如下
+
+    @PostMapping("/logout")
+    @PermitAll
+    @Operation(summary = "登出系统")
+    public CommonResult<Boolean> logout(HttpServletRequest request) {
+        String token = SecurityFrameworkUtils.obtainAuthorization(request,
+                securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
+        if (StrUtil.isNotBlank(token)) {
+            authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
+        }
+        return success(true);
+    }
+    @Override
+    public void logout(String token, Integer logType) {
+        // 删除访问令牌
+        OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token);
+        if (accessTokenDO == null) {
+            return;
+        }
+        // 删除成功,则记录登出日志
+        createLogoutLog(accessTokenDO.getUserId(), accessTokenDO.getUserType(), logType);
+    }
+    @Override
+    public OAuth2AccessTokenDO removeAccessToken(String accessToken) {
+        // 删除访问令牌
+        OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessToken);
+        if (accessTokenDO == null) {
+            return null;
+        }
+        oauth2AccessTokenMapper.deleteById(accessTokenDO.getId());
+        oauth2AccessTokenRedisDAO.delete(accessToken);
+        // 删除刷新令牌
+        oauth2RefreshTokenMapper.deleteByRefreshToken(accessTokenDO.getRefreshToken());
+        return accessTokenDO;
+    }

--
Gitblit v1.9.3