Commit a4e230828f34a2dc84b0210068f361b8f856dcdb

Authored by YunaiV
2 parents e7be2a03 772326e7

Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into develop

# Conflicts:
#	yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm
Showing 572 changed files with 21453 additions and 12657 deletions

Too many changes.

To preserve performance only 100 of 572 files are displayed.

.image/common/ai-feature.png

32.3 KB | W: | H:

40.5 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
README.md
@@ -308,26 +308,26 @@ @@ -308,26 +308,26 @@
308 308
309 | 框架 | 说明 | 版本 | 学习指南 | 309 | 框架 | 说明 | 版本 | 学习指南 |
310 |---------------------------------------------------------------------------------------------|------------------|----------------|----------------------------------------------------------------| 310 |---------------------------------------------------------------------------------------------|------------------|----------------|----------------------------------------------------------------|
311 -| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 3.4.5 | [文档](https://github.com/YunaiV/SpringBoot-Labs) | 311 +| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 3.5.5 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
312 | [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | | 312 | [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | |
313 -| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.23 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |  
314 -| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.7 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) | 313 +| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.27 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
  314 +| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.12 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |
315 | [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 4.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) | 315 | [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 4.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
316 | [Redis](https://redis.io/) | key-value 数据库 | 5.0 / 6.0 /7.0 | | 316 | [Redis](https://redis.io/) | key-value 数据库 | 5.0 / 6.0 /7.0 | |
317 -| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.32.0 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) |  
318 -| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 6.1.10 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |  
319 -| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 6.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |  
320 -| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 8.0.1 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) | 317 +| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.35.0 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) |
  318 +| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 6.2.9 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |
  319 +| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 6.5.2 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
  320 +| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 8.0.2 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) |
321 | [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 7.0.0 | [文档](https://doc.iocoder.cn/bpm/) | 321 | [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 7.0.0 | [文档](https://doc.iocoder.cn/bpm/) |
322 -| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |  
323 -| [Springdoc](https://springdoc.org/) | Swagger 文档 | 2.3.0 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |  
324 -| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 9.0.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |  
325 -| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 3.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |  
326 -| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.17.1 | | 322 +| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.5.0 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
  323 +| [Springdoc](https://springdoc.org/) | Swagger 文档 | 2.8.9 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |
  324 +| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 9.5.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
  325 +| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 3.5.2 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |
  326 +| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.30.14 | |
327 | [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.6.3 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) | 327 | [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.6.3 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) |
328 -| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.34 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) |  
329 -| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.10.1 | - |  
330 -| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 5.7.0 | - | 328 +| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.38 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) |
  329 +| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.12.2 | - |
  330 +| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 5.17.0 | - |
331 331
332 ## 🐷 演示图 332 ## 🐷 演示图
333 333
@@ -32,17 +32,17 @@ @@ -32,17 +32,17 @@
32 <url>https://github.com/YunaiV/ruoyi-vue-pro</url> 32 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
33 33
34 <properties> 34 <properties>
35 - <revision>2025.08-SNAPSHOT</revision> 35 + <revision>2025.09-SNAPSHOT</revision>
36 <!-- Maven 相关 --> 36 <!-- Maven 相关 -->
37 <java.version>17</java.version> 37 <java.version>17</java.version>
38 <maven.compiler.source>${java.version}</maven.compiler.source> 38 <maven.compiler.source>${java.version}</maven.compiler.source>
39 <maven.compiler.target>${java.version}</maven.compiler.target> 39 <maven.compiler.target>${java.version}</maven.compiler.target>
40 - <maven-surefire-plugin.version>3.2.2</maven-surefire-plugin.version> 40 + <maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version>
41 <maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version> 41 <maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
42 - <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version> 42 + <flatten-maven-plugin.version>1.7.2</flatten-maven-plugin.version>
43 <!-- maven-surefire-plugin 暂时无法通过 bom 的依赖读取(兼容老版本 IDEA 2024 及以前版本) --> 43 <!-- maven-surefire-plugin 暂时无法通过 bom 的依赖读取(兼容老版本 IDEA 2024 及以前版本) -->
44 <lombok.version>1.18.38</lombok.version> 44 <lombok.version>1.18.38</lombok.version>
45 - <spring.boot.version>3.4.5</spring.boot.version> 45 + <spring.boot.version>3.5.5</spring.boot.version>
46 <mapstruct.version>1.6.3</mapstruct.version> 46 <mapstruct.version>1.6.3</mapstruct.version>
47 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 47 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
48 </properties> 48 </properties>
sql/mysql/ruoyi-vue-pro.sql
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 Target Server Version : 80200 (8.2.0) 11 Target Server Version : 80200 (8.2.0)
12 File Encoding : 65001 12 File Encoding : 65001
13 13
14 - Date: 12/05/2025 09:09:45 14 + Date: 31/08/2025 11:12:28
15 */ 15 */
16 16
17 SET NAMES utf8mb4; 17 SET NAMES utf8mb4;
@@ -49,7 +49,7 @@ CREATE TABLE `infra_api_access_log` ( @@ -49,7 +49,7 @@ CREATE TABLE `infra_api_access_log` (
49 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', 49 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
50 PRIMARY KEY (`id`) USING BTREE, 50 PRIMARY KEY (`id`) USING BTREE,
51 INDEX `idx_create_time`(`create_time` ASC) USING BTREE 51 INDEX `idx_create_time`(`create_time` ASC) USING BTREE
52 -) ENGINE = InnoDB AUTO_INCREMENT = 35953 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表'; 52 +) ENGINE = InnoDB AUTO_INCREMENT = 36233 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表';
53 53
54 -- ---------------------------- 54 -- ----------------------------
55 -- Records of infra_api_access_log 55 -- Records of infra_api_access_log
@@ -91,7 +91,7 @@ CREATE TABLE `infra_api_error_log` ( @@ -91,7 +91,7 @@ CREATE TABLE `infra_api_error_log` (
91 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 91 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
92 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', 92 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
93 PRIMARY KEY (`id`) USING BTREE 93 PRIMARY KEY (`id`) USING BTREE
94 -) ENGINE = InnoDB AUTO_INCREMENT = 22175 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志'; 94 +) ENGINE = InnoDB AUTO_INCREMENT = 22656 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
95 95
96 -- ---------------------------- 96 -- ----------------------------
97 -- Records of infra_api_error_log 97 -- Records of infra_api_error_log
@@ -128,7 +128,7 @@ CREATE TABLE `infra_codegen_column` ( @@ -128,7 +128,7 @@ CREATE TABLE `infra_codegen_column` (
128 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 128 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
129 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 129 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
130 PRIMARY KEY (`id`) USING BTREE 130 PRIMARY KEY (`id`) USING BTREE
131 -) ENGINE = InnoDB AUTO_INCREMENT = 2538 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义'; 131 +) ENGINE = InnoDB AUTO_INCREMENT = 2603 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
132 132
133 -- ---------------------------- 133 -- ----------------------------
134 -- Records of infra_codegen_column 134 -- Records of infra_codegen_column
@@ -166,7 +166,7 @@ CREATE TABLE `infra_codegen_table` ( @@ -166,7 +166,7 @@ CREATE TABLE `infra_codegen_table` (
166 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 166 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
167 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 167 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
168 PRIMARY KEY (`id`) USING BTREE 168 PRIMARY KEY (`id`) USING BTREE
169 -) ENGINE = InnoDB AUTO_INCREMENT = 191 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义'; 169 +) ENGINE = InnoDB AUTO_INCREMENT = 196 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
170 170
171 -- ---------------------------- 171 -- ----------------------------
172 -- Records of infra_codegen_table 172 -- Records of infra_codegen_table
@@ -251,7 +251,7 @@ CREATE TABLE `infra_file` ( @@ -251,7 +251,7 @@ CREATE TABLE `infra_file` (
251 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 251 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
252 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 252 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
253 PRIMARY KEY (`id`) USING BTREE 253 PRIMARY KEY (`id`) USING BTREE
254 -) ENGINE = InnoDB AUTO_INCREMENT = 1898 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表'; 254 +) ENGINE = InnoDB AUTO_INCREMENT = 1983 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
255 255
256 -- ---------------------------- 256 -- ----------------------------
257 -- Records of infra_file 257 -- Records of infra_file
@@ -276,21 +276,22 @@ CREATE TABLE `infra_file_config` ( @@ -276,21 +276,22 @@ CREATE TABLE `infra_file_config` (
276 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 276 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
277 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 277 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
278 PRIMARY KEY (`id`) USING BTREE 278 PRIMARY KEY (`id`) USING BTREE
279 -) ENGINE = InnoDB AUTO_INCREMENT = 31 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件配置表'; 279 +) ENGINE = InnoDB AUTO_INCREMENT = 35 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件配置表';
280 280
281 -- ---------------------------- 281 -- ----------------------------
282 -- Records of infra_file_config 282 -- Records of infra_file_config
283 -- ---------------------------- 283 -- ----------------------------
284 BEGIN; 284 BEGIN;
285 -INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '数据库(示例)', 1, '我是数据库', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClientConfig\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2022-03-15 23:56:24', '1', '2025-05-02 18:30:28', b'0');  
286 -INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (22, '七牛存储器(示例)', 20, '请换成你自己的密钥!!!', b'1', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3.cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS\",\"accessSecret\":\"wd0tbVBYlp0S-ihA8Qg2hPLncoP83wyrIq24OZuY\",\"enablePathStyleAccess\":false}', '1', '2024-01-13 22:11:12', '1', '2025-05-02 18:30:28', b'0');  
287 -INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (24, '腾讯云存储(示例)', 20, '请换成你的密钥!!!', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"https://cos.ap-shanghai.myqcloud.com\",\"domain\":\"http://tengxun-oss.iocoder.cn\",\"bucket\":\"aoteman-1255880240\",\"accessKey\":\"AKIDAF6WSh1uiIjwqtrOsGSN3WryqTM6cTMt\",\"accessSecret\":\"X\"}', '1', '2024-11-09 16:03:22', '1', '2025-05-02 18:30:28', b'0');  
288 -INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (25, '阿里云存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"oss-cn-beijing.aliyuncs.com\",\"domain\":\"http://ali-oss.iocoder.cn\",\"bucket\":\"yunai-aoteman\",\"accessKey\":\"LTAI5tEQLgnDyjh3WpNcdMKA\",\"accessSecret\":\"X\",\"enablePathStyleAccess\":false}', '1', '2024-11-09 16:47:08', '1', '2025-05-02 18:30:28', b'0');  
289 -INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (26, '火山云存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"tos-s3-cn-beijing.volces.com\",\"domain\":null,\"bucket\":\"yunai\",\"accessKey\":\"AKLTZjc3Zjc4MzZmMjU3NDk0ZTgxYmIyMmFkNTIwMDI1ZGE\",\"accessSecret\":\"X==\",\"enablePathStyleAccess\":false}', '1', '2024-11-09 16:56:42', '1', '2025-05-02 18:30:28', b'0');  
290 -INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (27, '华为云存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"obs.cn-east-3.myhuaweicloud.com\",\"domain\":\"\",\"bucket\":\"yudao\",\"accessKey\":\"PVDONDEIOTW88LF8DC4U\",\"accessSecret\":\"X\",\"enablePathStyleAccess\":false}', '1', '2024-11-09 17:18:41', '1', '2025-05-02 18:30:28', b'0');  
291 -INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (28, 'MinIO 存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"http://127.0.0.1:9000\",\"domain\":\"http://127.0.0.1:9000/yudao\",\"bucket\":\"yudao\",\"accessKey\":\"admin\",\"accessSecret\":\"password\",\"enablePathStyleAccess\":false}', '1', '2024-11-09 17:43:10', '1', '2025-05-02 18:30:28', b'0');  
292 -INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (29, '本地存储(示例)', 10, '仅适合 mac 或 windows', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig\",\"basePath\":\"/Users/yunai/tmp/file\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2025-05-02 11:25:45', '1', '2025-05-02 18:30:28', b'0');  
293 -INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (30, 'SFTP 存储(示例)', 12, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClientConfig\",\"basePath\":\"/upload\",\"domain\":\"http://127.0.0.1:48080\",\"host\":\"127.0.0.1\",\"port\":2222,\"username\":\"foo\",\"password\":\"pass\"}', '1', '2025-05-02 16:34:10', '1', '2025-05-02 18:30:28', b'0'); 285 +INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '数据库(示例)', 1, '我是数据库', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClientConfig\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2022-03-15 23:56:24', '1', '2025-08-18 08:35:14', b'0');
  286 +INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (22, '七牛存储器(示例)', 20, '请换成你自己的密钥!!!', b'1', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3.cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS\",\"accessSecret\":\"wd0tbVBYlp0S-ihA8Qg2hPLncoP83wyrIq24OZuY\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-01-13 22:11:12', '1', '2025-08-18 08:35:14', b'0');
  287 +INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (24, '腾讯云存储(示例)', 20, '请换成你的密钥!!!', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"https://cos.ap-shanghai.myqcloud.com\",\"domain\":\"http://tengxun-oss.iocoder.cn\",\"bucket\":\"aoteman-1255880240\",\"accessKey\":\"AKIDAF6WSh1uiIjwqtrOsGSN3WryqTM6cTMt\",\"accessSecret\":\"X\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 16:03:22', '1', '2025-08-18 08:35:14', b'0');
  288 +INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (25, '阿里云存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"oss-cn-beijing.aliyuncs.com\",\"domain\":\"http://ali-oss.iocoder.cn\",\"bucket\":\"yunai-aoteman\",\"accessKey\":\"LTAI5tEQLgnDyjh3WpNcdMKA\",\"accessSecret\":\"X\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 16:47:08', '1', '2025-08-18 08:35:14', b'0');
  289 +INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (26, '火山云存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"tos-s3-cn-beijing.volces.com\",\"domain\":null,\"bucket\":\"yunai\",\"accessKey\":\"AKLTZjc3Zjc4MzZmMjU3NDk0ZTgxYmIyMmFkNTIwMDI1ZGE\",\"accessSecret\":\"X==\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 16:56:42', '1', '2025-08-18 08:35:14', b'0');
  290 +INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (27, '华为云存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"obs.cn-east-3.myhuaweicloud.com\",\"domain\":\"\",\"bucket\":\"yudao\",\"accessKey\":\"PVDONDEIOTW88LF8DC4U\",\"accessSecret\":\"X\",\"enablePathStyleAccess\":false}', '1', '2024-11-09 17:18:41', '1', '2025-08-18 08:35:14', b'0');
  291 +INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (28, 'MinIO 存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"http://127.0.0.1:9000\",\"domain\":\"http://127.0.0.1:9000/yudao\",\"bucket\":\"yudao\",\"accessKey\":\"admin\",\"accessSecret\":\"password\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 17:43:10', '1', '2025-08-18 08:35:14', b'0');
  292 +INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (29, '本地存储(示例)', 10, '仅适合 mac 或 windows', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig\",\"basePath\":\"/Users/yunai/tmp/file\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2025-05-02 11:25:45', '1', '2025-08-18 08:35:14', b'0');
  293 +INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (30, 'SFTP 存储(示例)', 12, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClientConfig\",\"basePath\":\"/upload\",\"domain\":\"http://127.0.0.1:48080\",\"host\":\"127.0.0.1\",\"port\":2222,\"username\":\"foo\",\"password\":\"pass\"}', '1', '2025-05-02 16:34:10', '1', '2025-08-18 08:35:14', b'0');
  294 +INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (34, '七牛云存储【私有】(示例)', 20, '请换成你自己的密钥!!!', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3.cn-south-1.qiniucs.com\",\"domain\":\"http://t151glocd.hn-bkt.clouddn.com\",\"bucket\":\"ruoyi-vue-pro-private\",\"accessKey\":\"3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS\",\"accessSecret\":\"wd0tbVBYlp0S-ihA8Qg2hPLncoP83wyrIq24OZuY\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":false}', '1', '2025-08-17 21:22:00', '1', '2025-08-18 08:35:55', b'0');
294 COMMIT; 295 COMMIT;
295 296
296 -- ---------------------------- 297 -- ----------------------------
@@ -336,7 +337,7 @@ CREATE TABLE `infra_job` ( @@ -336,7 +337,7 @@ CREATE TABLE `infra_job` (
336 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 337 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
337 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 338 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
338 PRIMARY KEY (`id`) USING BTREE 339 PRIMARY KEY (`id`) USING BTREE
339 -) ENGINE = InnoDB AUTO_INCREMENT = 36 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务表'; 340 +) ENGINE = InnoDB AUTO_INCREMENT = 38 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务表';
340 341
341 -- ---------------------------- 342 -- ----------------------------
342 -- Records of infra_job 343 -- Records of infra_job
@@ -355,6 +356,8 @@ INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param` @@ -355,6 +356,8 @@ INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`
355 INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (27, '任务日志清理 Job', 2, 'jobLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 11:01:33', '1', '2024-09-12 13:40:34', b'0'); 356 INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (27, '任务日志清理 Job', 2, 'jobLogCleanJob', '', '0 0 0 * * ?', 3, 0, 0, '1', '2023-10-03 11:01:33', '1', '2024-09-12 13:40:34', b'0');
356 INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (33, 'demoJob', 2, 'demoJob', '', '0 * * * * ?', 1, 1, 0, '1', '2024-10-27 19:38:46', '1', '2025-05-10 18:13:54', b'0'); 357 INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (33, 'demoJob', 2, 'demoJob', '', '0 * * * * ?', 1, 1, 0, '1', '2024-10-27 19:38:46', '1', '2025-05-10 18:13:54', b'0');
357 INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (35, '转账订单的同步 Job', 2, 'payTransferSyncJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-05-10 17:35:54', '1', '2025-05-10 18:13:52', b'0'); 358 INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (35, '转账订单的同步 Job', 2, 'payTransferSyncJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-05-10 17:35:54', '1', '2025-05-10 18:13:52', b'0');
  359 +INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (36, 'IoT 设备离线检查 Job', 2, 'iotDeviceOfflineCheckJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-07-03 23:48:44', '\"1\"', '2025-07-03 23:48:47', b'0');
  360 +INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (37, 'IoT OTA 升级推送 Job', 2, 'iotOtaUpgradeJob', '', '0 * * * * ?', 0, 0, 0, '1', '2025-07-03 23:49:07', '\"1\"', '2025-07-03 23:49:13', b'0');
358 COMMIT; 361 COMMIT;
359 362
360 -- ---------------------------- 363 -- ----------------------------
@@ -378,7 +381,7 @@ CREATE TABLE `infra_job_log` ( @@ -378,7 +381,7 @@ CREATE TABLE `infra_job_log` (
378 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 381 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
379 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 382 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
380 PRIMARY KEY (`id`) USING BTREE 383 PRIMARY KEY (`id`) USING BTREE
381 -) ENGINE = InnoDB AUTO_INCREMENT = 972 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务日志表'; 384 +) ENGINE = InnoDB AUTO_INCREMENT = 980 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务日志表';
382 385
383 -- ---------------------------- 386 -- ----------------------------
384 -- Records of infra_job_log 387 -- Records of infra_job_log
@@ -448,7 +451,7 @@ CREATE TABLE `system_dict_data` ( @@ -448,7 +451,7 @@ CREATE TABLE `system_dict_data` (
448 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 451 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
449 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 452 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
450 PRIMARY KEY (`id`) USING BTREE 453 PRIMARY KEY (`id`) USING BTREE
451 -) ENGINE = InnoDB AUTO_INCREMENT = 3003 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表'; 454 +) ENGINE = InnoDB AUTO_INCREMENT = 3031 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
452 455
453 -- ---------------------------- 456 -- ----------------------------
454 -- Records of system_dict_data 457 -- Records of system_dict_data
@@ -639,7 +642,7 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st @@ -639,7 +642,7 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
639 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1230, 13, '支付宝条码支付', 'alipay_bar', 'pay_channel_code', 0, 'primary', '', '支付宝条码支付', '1', '2023-02-18 23:32:24', '1', '2023-07-19 20:09:23', b'0'); 642 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1230, 13, '支付宝条码支付', 'alipay_bar', 'pay_channel_code', 0, 'primary', '', '支付宝条码支付', '1', '2023-02-18 23:32:24', '1', '2023-07-19 20:09:23', b'0');
640 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1231, 10, 'Vue2 Element UI 标准模版', '10', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:03:55', '1', '2023-04-13 00:03:55', b'0'); 643 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1231, 10, 'Vue2 Element UI 标准模版', '10', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:03:55', '1', '2023-04-13 00:03:55', b'0');
641 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1232, 20, 'Vue3 Element Plus 标准模版', '20', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:04:08', '1', '2023-04-13 00:04:08', b'0'); 644 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1232, 20, 'Vue3 Element Plus 标准模版', '20', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:04:08', '1', '2023-04-13 00:04:08', b'0');
642 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1234, 30, 'Vben2.0 Ant Design Schema 模版', '30', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:04:26', '1', '2025-04-23 21:27:34', b'0'); 645 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1234, 30, 'Vben2.0 Ant Design Schema 模版', '30', 'infra_codegen_front_type', 1, '', '', '', '1', '2023-04-13 00:04:26', '1', '2025-07-27 10:55:14', b'0');
643 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1244, 0, '按件', '1', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:40', '1', '2023-05-21 22:46:40', b'0'); 646 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1244, 0, '按件', '1', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:40', '1', '2023-05-21 22:46:40', b'0');
644 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1245, 1, '按重量', '2', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:58', '1', '2023-05-21 22:46:58', b'0'); 647 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1245, 1, '按重量', '2', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:58', '1', '2023-05-21 22:46:58', b'0');
645 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1246, 2, '按体积', '3', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:47:18', '1', '2023-05-21 22:47:18', b'0'); 648 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1246, 2, '按体积', '3', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:47:18', '1', '2023-05-21 22:47:18', b'0');
@@ -881,36 +884,21 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st @@ -881,36 +884,21 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
881 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1691, 6, '重排', '6', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:26', '1', '2025-03-03 12:28:26', b'0'); 884 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1691, 6, '重排', '6', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:26', '1', '2025-03-03 12:28:26', b'0');
882 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1692, 14, 'MiniMax', 'MiniMax', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:04:51', '1', '2025-03-11 20:04:51', b'0'); 885 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1692, 14, 'MiniMax', 'MiniMax', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:04:51', '1', '2025-03-11 20:04:51', b'0');
883 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1693, 15, '月之暗灭', 'Moonshot', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:05:08', '1', '2025-03-11 20:05:08', b'0'); 886 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1693, 15, '月之暗灭', 'Moonshot', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:05:08', '1', '2025-03-11 20:05:08', b'0');
884 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2000, 0, '标准数据格式(JSON)', '0', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:26', '1', '2025-03-17 09:28:16', b'0');  
885 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2001, 1, '透传/自定义', '1', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:37', '1', '2025-03-17 09:28:19', b'0');  
886 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2002, 0, '直连设备', '0', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:54:58', '1', '2025-03-17 09:28:22', b'0'); 887 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2002, 0, '直连设备', '0', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:54:58', '1', '2025-03-17 09:28:22', b'0');
887 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2003, 2, '网关设备', '2', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:08', '1', '2025-03-17 09:28:28', b'0'); 888 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2003, 2, '网关设备', '2', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:08', '1', '2025-03-17 09:28:28', b'0');
888 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2004, 1, '网关子设备', '1', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:20', '1', '2025-03-17 09:28:31', b'0'); 889 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2004, 1, '网关子设备', '1', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:20', '1', '2025-03-17 09:28:31', b'0');
889 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2005, 1, '已发布', '1', 'iot_product_status', 0, 'success', '', '', '1', '2024-08-10 12:10:33', '1', '2025-03-17 09:28:34', b'0'); 890 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2005, 1, '已发布', '1', 'iot_product_status', 0, 'success', '', '', '1', '2024-08-10 12:10:33', '1', '2025-03-17 09:28:34', b'0');
890 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2006, 0, '开发中', '0', 'iot_product_status', 0, 'default', '', '', '1', '2024-08-10 14:19:18', '1', '2025-03-17 09:28:39', b'0'); 891 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2006, 0, '开发中', '0', 'iot_product_status', 0, 'default', '', '', '1', '2024-08-10 14:19:18', '1', '2025-03-17 09:28:39', b'0');
891 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2007, 0, '弱校验', '0', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:05:48', '1', '2025-03-17 09:28:41', b'0');  
892 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2008, 1, '免校验', '1', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:06:03', '1', '2025-03-17 09:28:44', b'0');  
893 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2009, 0, 'Wi-Fi', '0', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:04:47', '1', '2025-03-17 09:28:47', b'0'); 892 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2009, 0, 'Wi-Fi', '0', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:04:47', '1', '2025-03-17 09:28:47', b'0');
894 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2010, 1, '蜂窝(2G / 3G / 4G / 5G)', '1', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:14', '1', '2025-03-17 09:28:49', b'0'); 893 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2010, 1, '移动网络', '1', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:14', '1', '2025-06-12 23:27:19', b'0');
895 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2011, 2, '以太网', '2', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:35', '1', '2025-03-17 09:28:51', b'0'); 894 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2011, 2, '以太网', '2', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:35', '1', '2025-03-17 09:28:51', b'0');
896 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2012, 3, '其他', '3', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:52', '1', '2025-03-17 09:28:54', b'0'); 895 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2012, 3, '其他', '3', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:52', '1', '2025-03-17 09:28:54', b'0');
897 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2013, 0, '自定义', '0', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:10', '1', '2025-03-17 09:28:56', b'0');  
898 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2014, 1, 'Modbus', '1', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:21', '1', '2025-03-17 09:28:58', b'0');  
899 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2015, 2, 'OPC UA', '2', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:31', '1', '2025-03-17 09:29:00', b'0');  
900 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2016, 3, 'ZigBee', '3', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:39', '1', '2025-03-17 09:29:04', b'0');  
901 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2017, 4, 'BLE', '4', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:48', '1', '2025-03-17 09:29:06', b'0');  
902 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2018, 0, '未激活', '0', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2025-03-17 09:29:09', b'0'); 896 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2018, 0, '未激活', '0', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2025-03-17 09:29:09', b'0');
903 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2019, 1, '在线', '1', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2025-03-17 09:29:12', b'0'); 897 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2019, 1, '在线', '1', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2025-03-17 09:29:12', b'0');
904 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2020, 2, '离线', '2', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2025-03-17 09:29:14', b'0'); 898 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2020, 2, '离线', '2', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2025-03-17 09:29:14', b'0');
905 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2021, 1, '属性', '1', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:01', '1', '2025-03-17 09:29:24', b'0'); 899 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2021, 1, '属性', '1', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:01', '1', '2025-03-17 09:29:24', b'0');
906 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2022, 2, '服务', '2', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:11', '1', '2025-03-17 09:29:27', b'0'); 900 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2022, 2, '服务', '2', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:11', '1', '2025-03-17 09:29:27', b'0');
907 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2023, 3, '事件', '3', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:20', '1', '2025-03-17 09:29:29', b'0'); 901 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2023, 3, '事件', '3', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:20', '1', '2025-03-17 09:29:29', b'0');
908 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2024, 1, 'JAR 部署', '0', 'iot_plugin_deploy_type', 0, '', '', '', '1', '2024-12-13 10:55:32', '1', '2025-03-17 09:29:32', b'0');  
909 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2025, 2, '独立部署', '1', 'iot_plugin_deploy_type', 0, '', '', '', '1', '2024-12-13 10:55:43', '1', '2025-03-17 09:29:34', b'0');  
910 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2026, 0, '停止', '0', 'iot_plugin_status', 0, 'danger', '', '', '1', '2024-12-13 11:07:37', '1', '2025-03-17 09:29:37', b'0');  
911 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2027, 1, '运行', '1', 'iot_plugin_status', 0, '', '', '', '1', '2024-12-13 11:07:45', '1', '2025-03-17 09:34:17', b'0');  
912 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2028, 0, '普通插件', '0', 'iot_plugin_type', 0, '', '', '', '1', '2024-12-13 11:08:32', '1', '2025-03-17 09:34:19', b'0');  
913 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2029, 1, '设备插件', '1', 'iot_plugin_type', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:22', b'0');  
914 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2030, 1, '升每分钟', 'L/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:24', b'0'); 902 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2030, 1, '升每分钟', 'L/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:24', b'0');
915 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2031, 2, '毫克每千克', 'mg/kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:27', b'0'); 903 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2031, 2, '毫克每千克', 'mg/kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:27', b'0');
916 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2032, 3, '浊度', 'NTU', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:31', b'0'); 904 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2032, 3, '浊度', 'NTU', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:31', b'0');
@@ -1043,20 +1031,59 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st @@ -1043,20 +1031,59 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
1043 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2160, 130, '分米', 'dm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); 1031 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2160, 130, '分米', 'dm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
1044 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2161, 131, '千米', 'km', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); 1032 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2161, 131, '千米', 'km', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
1045 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2162, 132, '米', 'm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); 1033 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2162, 132, '米', 'm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
1046 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2163, 1, '输入', '1', 'iot_data_bridge_direction_enum', 0, 'primary', '', '', '1', '2025-03-09 12:38:24', '1', '2025-03-17 09:40:46', b'0');  
1047 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2164, 2, '输出', '2', 'iot_data_bridge_direction_enum', 0, 'primary', '', '', '1', '2025-03-09 12:38:36', '1', '2025-03-17 09:40:46', b'0');  
1048 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2165, 1, 'HTTP', '1', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:39:54', '1', '2025-03-17 09:40:46', b'0');  
1049 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2166, 2, 'TCP', '2', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:06', '1', '2025-03-17 09:40:46', b'0');  
1050 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2167, 3, 'WEBSOCKET', '3', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:24', '1', '2025-03-17 09:40:46', b'0');  
1051 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2168, 10, 'MQTT', '10', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:37', '1', '2025-03-17 09:40:46', b'0');  
1052 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2169, 20, 'DATABASE', '20', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:05', '1', '2025-03-17 09:40:46', b'0');  
1053 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2170, 21, 'REDIS_STREAM', '21', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:18', '1', '2025-03-17 09:40:46', b'0');  
1054 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2171, 30, 'ROCKETMQ', '30', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:30', '1', '2025-03-17 09:40:46', b'0');  
1055 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2172, 31, 'RABBITMQ', '31', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:47', '1', '2025-03-17 09:40:46', b'0');  
1056 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2173, 32, 'KAFKA', '32', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:59', '1', '2025-03-17 09:40:46', b'0'); 1034 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2165, 1, 'HTTP', '1', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:39:54', '1', '2025-06-24 12:44:47', b'0');
  1035 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2166, 2, 'TCP', '2', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:40:06', '1', '2025-06-24 12:44:46', b'0');
  1036 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2167, 3, 'WebSocket', '3', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:40:24', '1', '2025-06-24 12:44:45', b'0');
  1037 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2168, 10, 'MQTT', '10', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:40:37', '1', '2025-06-24 12:44:44', b'0');
  1038 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2169, 20, 'Database', '20', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:05', '1', '2025-06-24 12:44:44', b'0');
  1039 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2170, 21, 'Redis Stream', '21', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:18', '1', '2025-06-24 12:44:43', b'0');
  1040 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2171, 30, 'RocketMQ', '30', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:30', '1', '2025-06-24 12:44:42', b'0');
  1041 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2172, 31, 'RabbitMQ', '31', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:47', '1', '2025-06-24 12:44:41', b'0');
  1042 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2173, 32, 'Kafka', '32', 'iot_data_sink_type_enum', 0, 'default', '', '', '1', '2025-03-09 12:41:59', '1', '2025-06-24 12:44:39', b'0');
  1043 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2174, 1, '设备上下线变更', '1', 'iot_rule_scene_trigger_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:00:01', '\"1\"', '2025-07-06 10:28:16', b'0');
  1044 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2175, 2, '物模型属性上报', '2', 'iot_rule_scene_trigger_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:00:09', '\"1\"', '2025-07-06 10:28:22', b'0');
  1045 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2176, 1, '设备状态', 'state', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:24:58', '1', '2025-03-20 15:24:58', b'0');
  1046 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2177, 2, '设备属性', 'property', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:25:09', '1', '2025-03-20 15:25:09', b'0');
  1047 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2178, 3, '设备事件', 'event', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:25:23', '1', '2025-03-20 15:25:23', b'0');
  1048 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2179, 4, '设备服务', 'service', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:25:39', '1', '2025-03-20 15:25:39', b'0');
  1049 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2180, 5, '设备配置', 'config', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:25:51', '1', '2025-03-20 15:25:57', b'0');
  1050 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2181, 6, '设备 OTA', 'ota', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:26:17', '1', '2025-03-20 15:26:17', b'0');
  1051 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2182, 7, '设备注册', 'register', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:26:35', '1', '2025-03-20 15:26:35', b'0');
  1052 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2183, 8, '设备拓扑', 'topology', 'iot_device_message_type_enum', 0, 'primary', '', '', '1', '2025-03-20 15:26:46', '1', '2025-03-20 15:26:46', b'0');
  1053 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2184, 1, '设备属性设置', '1', 'iot_rule_scene_action_type_enum', 0, 'primary', '', '', '1', '2025-03-28 15:27:12', '\"1\"', '2025-07-06 10:37:33', b'0');
  1054 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2185, 2, '设备服务调用', '2', 'iot_rule_scene_action_type_enum', 0, 'primary', '', '', '1', '2025-03-28 15:27:25', '\"1\"', '2025-07-06 10:37:41', b'0');
  1055 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2186, 100, '告警触发', '100', 'iot_rule_scene_action_type_enum', 0, 'primary', '', '', '1', '2025-03-28 15:27:35', '\"1\"', '2025-07-06 10:37:50', b'0');
1057 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3000, 16, '百川智能', 'BaiChuan', 'ai_platform', 0, '', '', '', '1', '2025-03-23 12:15:46', '1', '2025-03-23 12:15:46', b'0'); 1056 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3000, 16, '百川智能', 'BaiChuan', 'ai_platform', 0, '', '', '', '1', '2025-03-23 12:15:46', '1', '2025-03-23 12:15:46', b'0');
1058 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3001, 50, 'Vben5.0 Ant Design Schema 模版', '40', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-04-23 21:47:47', '1', '2025-05-02 12:01:15', b'0'); 1057 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3001, 50, 'Vben5.0 Ant Design Schema 模版', '40', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-04-23 21:47:47', '1', '2025-05-02 12:01:15', b'0');
1059 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3002, 6, '支付宝余额', '6', 'brokerage_withdraw_type', 0, '', '', 'API 打款', '1', '2025-05-10 08:24:49', '1', '2025-05-10 08:24:49', b'0'); 1058 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3002, 6, '支付宝余额', '6', 'brokerage_withdraw_type', 0, '', '', 'API 打款', '1', '2025-05-10 08:24:49', '1', '2025-05-10 08:24:49', b'0');
  1059 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3003, 1, 'Alink', 'Alink', 'iot_codec_type', 0, '', '', '阿里云 Alink', '1', '2025-06-12 22:56:06', '1', '2025-06-12 23:22:24', b'0');
  1060 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3004, 3, 'WARN', '3', 'iot_alert_level', 0, 'warning', '', '', '1', '2025-06-27 20:32:22', '1', '2025-06-27 20:34:31', b'0');
  1061 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3005, 1, 'INFO', '1', 'iot_alert_level', 0, 'primary', '', '', '1', '2025-06-27 20:33:28', '1', '2025-06-27 20:34:35', b'0');
  1062 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3006, 5, 'ERROR', '5', 'iot_alert_level', 0, 'danger', '', '', '1', '2025-06-27 20:33:50', '1', '2025-06-27 20:33:50', b'0');
  1063 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3007, 1, '短信', '1', 'iot_alert_receive_type', 0, '', '', '', '1', '2025-06-27 22:49:30', '1', '2025-06-27 22:49:30', b'0');
  1064 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3008, 2, '邮箱', '2', 'iot_alert_receive_type', 0, '', '', '', '1', '2025-06-27 22:49:39', '1', '2025-06-27 22:50:07', b'0');
  1065 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3009, 3, '站内信', '3', 'iot_alert_receive_type', 0, '', '', '', '1', '2025-06-27 22:50:20', '1', '2025-06-27 22:50:20', b'0');
  1066 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3010, 1, '全部设备', '1', 'iot_ota_task_device_scope', 0, '', '', '', '1', '2025-07-02 09:43:09', '1', '2025-07-02 09:43:09', b'0');
  1067 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3011, 2, '指定设备', '2', 'iot_ota_task_device_scope', 0, '', '', '', '1', '2025-07-02 09:43:15', '1', '2025-07-02 09:43:15', b'0');
  1068 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3012, 10, '进行中', '10', 'iot_ota_task_status', 0, 'primary', '', '', '1', '2025-07-02 09:44:01', '\"1\"', '2025-07-02 09:44:21', b'0');
  1069 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3013, 20, '已结束', '20', 'iot_ota_task_status', 0, 'success', '', '', '1', '2025-07-02 09:44:14', '\"1\"', '2025-07-02 23:56:12', b'0');
  1070 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3014, 30, '已取消', '30', 'iot_ota_task_status', 0, 'danger', '', '', '1', '2025-07-02 09:44:36', '1', '2025-07-02 09:44:36', b'0');
  1071 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3015, 0, '待推送', '0', 'iot_ota_task_record_status', 0, '', '', '', '1', '2025-07-02 09:45:16', '1', '2025-07-02 09:45:16', b'0');
  1072 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3016, 10, '已推送', '10', 'iot_ota_task_record_status', 0, '', '', '', '1', '2025-07-02 09:45:25', '1', '2025-07-02 09:45:25', b'0');
  1073 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3017, 20, '升级中', '20', 'iot_ota_task_record_status', 0, 'primary', '', '', '1', '2025-07-02 09:45:37', '1', '2025-07-02 09:45:37', b'0');
  1074 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3018, 30, '升级成功', '30', 'iot_ota_task_record_status', 0, 'success', '', '', '1', '2025-07-02 09:45:47', '1', '2025-07-02 09:45:47', b'0');
  1075 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3019, 40, '升级失败', '40', 'iot_ota_task_record_status', 0, 'danger', '', '', '1', '2025-07-02 09:46:02', '1', '2025-07-02 09:46:02', b'0');
  1076 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3020, 50, '升级取消', '50', 'iot_ota_task_record_status', 0, 'warning', '', '', '1', '2025-07-02 09:46:09', '\"1\"', '2025-07-02 09:46:27', b'0');
  1077 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3021, 1, 'IP 定位', '1', 'iot_location_type', 0, '', '', '', '1', '2025-07-05 09:56:46', '1', '2025-07-05 09:56:46', b'0');
  1078 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3022, 2, '设备上报', '2', 'iot_location_type', 0, '', '', '', '1', '2025-07-05 09:56:57', '1', '2025-07-05 09:56:57', b'0');
  1079 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3023, 3, '手动定位', '3', 'iot_location_type', 0, '', '', '', '1', '2025-07-05 09:57:05', '1', '2025-07-05 09:57:05', b'0');
  1080 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3024, 3, '设备事件上报', '3', 'iot_rule_scene_trigger_type_enum', 0, '', '', '', '1', '2025-07-06 10:28:29', '1', '2025-07-06 10:28:29', b'0');
  1081 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3025, 4, '设备服务调用', '4', 'iot_rule_scene_trigger_type_enum', 0, '', '', '', '1', '2025-07-06 10:28:35', '1', '2025-07-06 10:28:35', b'0');
  1082 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3026, 100, '定时触发', '100', 'iot_rule_scene_trigger_type_enum', 0, '', '', '', '1', '2025-07-06 10:28:48', '1', '2025-07-06 10:28:48', b'0');
  1083 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3027, 101, '告警恢复', '101', 'iot_rule_scene_action_type_enum', 0, '', '', '', '1', '2025-07-06 10:37:57', '1', '2025-07-06 10:37:57', b'0');
  1084 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3028, 2, 'Anthropic', 'Anthropic', 'ai_platform', 0, '', '', '', '1', '2025-08-21 22:54:24', '1', '2025-08-21 22:57:58', b'0');
  1085 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3029, 2, '谷歌 Gemini', 'Gemini', 'ai_platform', 0, '', '', '', '1', '2025-08-22 22:39:35', '1', '2025-08-22 22:44:49', b'0');
  1086 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3030, 1, '文件系统', 'filesystem', 'ai_mcp_client_name', 0, '', '', '', '1', '2025-08-28 13:58:43', '1', '2025-08-28 21:19:42', b'0');
1060 COMMIT; 1087 COMMIT;
1061 1088
1062 -- ---------------------------- 1089 -- ----------------------------
@@ -1076,7 +1103,7 @@ CREATE TABLE `system_dict_type` ( @@ -1076,7 +1103,7 @@ CREATE TABLE `system_dict_type` (
1076 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 1103 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
1077 `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间', 1104 `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
1078 PRIMARY KEY (`id`) USING BTREE 1105 PRIMARY KEY (`id`) USING BTREE
1079 -) ENGINE = InnoDB AUTO_INCREMENT = 2000 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表'; 1106 +) ENGINE = InnoDB AUTO_INCREMENT = 2008 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
1080 1107
1081 -- ---------------------------- 1108 -- ----------------------------
1082 -- Records of system_dict_type 1109 -- Records of system_dict_type
@@ -1175,20 +1202,24 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat @@ -1175,20 +1202,24 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat
1175 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (628, 'AI 写作类型', 'ai_write_type', 0, '', '1', '2024-07-10 21:25:29', '1', '2024-07-10 21:25:29', b'0', '1970-01-01 00:00:00'); 1202 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (628, 'AI 写作类型', 'ai_write_type', 0, '', '1', '2024-07-10 21:25:29', '1', '2024-07-10 21:25:29', b'0', '1970-01-01 00:00:00');
1176 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (629, 'BPM 流程模型类型', 'bpm_model_type', 0, '', '1', '2024-08-26 15:21:43', '1', '2024-08-26 15:21:43', b'0', '1970-01-01 00:00:00'); 1203 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (629, 'BPM 流程模型类型', 'bpm_model_type', 0, '', '1', '2024-08-26 15:21:43', '1', '2024-08-26 15:21:43', b'0', '1970-01-01 00:00:00');
1177 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (640, 'AI 模型类型', 'ai_model_type', 0, '', '1', '2025-03-03 12:24:07', '1', '2025-03-03 12:24:07', b'0', '1970-01-01 00:00:00'); 1204 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (640, 'AI 模型类型', 'ai_model_type', 0, '', '1', '2025-03-03 12:24:07', '1', '2025-03-03 12:24:07', b'0', '1970-01-01 00:00:00');
1178 -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1000, 'IoT 数据格式', 'iot_data_format', 0, '', '1', '2024-08-10 11:52:58', '1', '2025-03-17 09:25:06', b'0', '1970-01-01 00:00:00');  
1179 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1001, 'IoT 产品设备类型', 'iot_product_device_type', 0, '', '1', '2024-08-10 11:54:30', '1', '2025-03-17 09:25:08', b'0', '1970-01-01 00:00:00'); 1205 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1001, 'IoT 产品设备类型', 'iot_product_device_type', 0, '', '1', '2024-08-10 11:54:30', '1', '2025-03-17 09:25:08', b'0', '1970-01-01 00:00:00');
1180 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1002, 'IoT 产品状态', 'iot_product_status', 0, '', '1', '2024-08-10 12:06:09', '1', '2025-03-17 09:25:10', b'0', '1970-01-01 00:00:00'); 1206 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1002, 'IoT 产品状态', 'iot_product_status', 0, '', '1', '2024-08-10 12:06:09', '1', '2025-03-17 09:25:10', b'0', '1970-01-01 00:00:00');
1181 -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1003, 'IoT 数据校验级别', 'iot_validate_type', 0, '', '1', '2024-09-06 20:05:13', '1', '2025-03-17 09:25:12', b'0', '1970-01-01 00:00:00');  
1182 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1004, 'IoT 联网方式', 'iot_net_type', 0, '', '1', '2024-09-06 22:04:13', '1', '2025-03-17 09:25:14', b'0', '1970-01-01 00:00:00'); 1207 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1004, 'IoT 联网方式', 'iot_net_type', 0, '', '1', '2024-09-06 22:04:13', '1', '2025-03-17 09:25:14', b'0', '1970-01-01 00:00:00');
1183 -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1005, 'IoT 接入网关协议', 'iot_protocol_type', 0, '', '1', '2024-09-06 22:20:17', '1', '2025-03-17 09:25:16', b'0', '1970-01-01 00:00:00');  
1184 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1006, 'IoT 设备状态', 'iot_device_state', 0, '', '1', '2024-09-21 08:12:55', '1', '2025-03-17 09:25:19', b'0', '1970-01-01 00:00:00'); 1208 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1006, 'IoT 设备状态', 'iot_device_state', 0, '', '1', '2024-09-21 08:12:55', '1', '2025-03-17 09:25:19', b'0', '1970-01-01 00:00:00');
1185 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1007, 'IoT 物模型功能类型', 'iot_thing_model_type', 0, '', '1', '2024-09-29 20:02:36', '1', '2025-03-17 09:25:24', b'0', '1970-01-01 00:00:00'); 1209 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1007, 'IoT 物模型功能类型', 'iot_thing_model_type', 0, '', '1', '2024-09-29 20:02:36', '1', '2025-03-17 09:25:24', b'0', '1970-01-01 00:00:00');
1186 -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1008, 'IoT 插件部署方式', 'iot_plugin_deploy_type', 0, '', '1', '2024-12-13 10:55:13', '1', '2025-03-17 09:25:27', b'0', '1970-01-01 00:00:00');  
1187 -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1009, 'IoT 插件状态', 'iot_plugin_status', 0, '', '1', '2024-12-13 11:05:34', '1', '2025-03-17 09:25:30', b'0', '1970-01-01 00:00:00');  
1188 -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1010, 'IoT 插件类型', 'iot_plugin_type', 0, '', '1', '2024-12-13 11:08:19', '1', '2025-03-17 09:25:32', b'0', '1970-01-01 00:00:00');  
1189 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1011, 'IoT 物模型单位', 'iot_thing_model_unit', 0, '', '1', '2024-12-25 17:36:46', '1', '2025-03-17 09:25:35', b'0', '1970-01-01 00:00:00'); 1210 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1011, 'IoT 物模型单位', 'iot_thing_model_unit', 0, '', '1', '2024-12-25 17:36:46', '1', '2025-03-17 09:25:35', b'0', '1970-01-01 00:00:00');
1190 -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1012, 'IoT 数据桥接的方向枚举', 'iot_data_bridge_direction_enum', 0, '', '1', '2025-03-09 12:37:40', '1', '2025-03-17 09:25:39', b'0', '1970-01-01 00:00:00');  
1191 -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1013, 'IoT 数据桥梁的类型枚举', 'iot_data_bridge_type_enum', 0, '', '1', '2025-03-09 12:39:36', '1', '2025-04-06 17:09:46', b'0', '1970-01-01 00:00:00'); 1211 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1013, 'IoT 数据流转目的的类型枚举', 'iot_data_sink_type_enum', 0, '', '1', '2025-03-09 12:39:36', '1', '2025-06-24 12:45:24', b'0', '1970-01-01 00:00:00');
  1212 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1014, 'IoT 场景流转的触发类型枚举', 'iot_rule_scene_trigger_type_enum', 0, '', '1', '2025-03-20 14:59:44', '1', '2025-03-20 14:59:44', b'0', '1970-01-01 00:00:00');
  1213 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1015, 'IoT 设备消息类型枚举', 'iot_device_message_type_enum', 0, '', '1', '2025-03-20 15:01:15', '1', '2025-03-20 15:01:15', b'0', '1970-01-01 00:00:00');
  1214 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1016, 'IoT 规则场景的触发类型枚举', 'iot_rule_scene_action_type_enum', 0, '', '1', '2025-03-28 15:26:54', '1', '2025-03-28 15:29:13', b'0', '1970-01-01 00:00:00');
  1215 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2000, 'IoT 数据格式', 'iot_codec_type', 0, 'IoT 编解码器类型', '1', '2025-06-12 22:55:46', '1', '2025-06-12 22:55:46', b'0', '1970-01-01 00:00:00');
  1216 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2001, 'IoT 告警级别', 'iot_alert_level', 0, '', '1', '2025-06-27 20:30:57', '1', '2025-06-27 20:30:57', b'0', '1970-01-01 00:00:00');
  1217 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2002, 'IoT 告警', 'iot_alert_receive_type', 0, '', '1', '2025-06-27 22:49:19', '1', '2025-06-27 22:49:19', b'0', '1970-01-01 00:00:00');
  1218 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2003, 'IoT 固件设备范围', 'iot_ota_task_device_scope', 0, '', '1', '2025-07-02 09:42:49', '1', '2025-07-02 09:42:49', b'0', '1970-01-01 00:00:00');
  1219 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2004, 'IoT 固件升级任务状态', 'iot_ota_task_status', 0, '', '1', '2025-07-02 09:43:43', '1', '2025-07-02 09:43:43', b'0', '1970-01-01 00:00:00');
  1220 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2005, 'IoT 固件升级记录状态', 'iot_ota_task_record_status', 0, '', '1', '2025-07-02 09:45:02', '1', '2025-07-02 09:45:02', b'0', '1970-01-01 00:00:00');
  1221 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2006, 'IoT 定位类型', 'iot_location_type', 0, '', '1', '2025-07-05 09:56:25', '1', '2025-07-05 09:56:25', b'0', '1970-01-01 00:00:00');
  1222 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2007, 'AI MCP 客户端名字', 'ai_mcp_client_name', 0, '', '1', '2025-08-28 13:57:40', '1', '2025-08-28 13:57:40', b'0', '1970-01-01 00:00:00');
1192 COMMIT; 1223 COMMIT;
1193 1224
1194 -- ---------------------------- 1225 -- ----------------------------
@@ -1212,7 +1243,7 @@ CREATE TABLE `system_login_log` ( @@ -1212,7 +1243,7 @@ CREATE TABLE `system_login_log` (
1212 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 1243 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
1213 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', 1244 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
1214 PRIMARY KEY (`id`) USING BTREE 1245 PRIMARY KEY (`id`) USING BTREE
1215 -) ENGINE = InnoDB AUTO_INCREMENT = 3822 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录'; 1246 +) ENGINE = InnoDB AUTO_INCREMENT = 4021 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
1216 1247
1217 -- ---------------------------- 1248 -- ----------------------------
1218 -- Records of system_login_log 1249 -- Records of system_login_log
@@ -1246,7 +1277,7 @@ CREATE TABLE `system_mail_account` ( @@ -1246,7 +1277,7 @@ CREATE TABLE `system_mail_account` (
1246 -- ---------------------------- 1277 -- ----------------------------
1247 BEGIN; 1278 BEGIN;
1248 INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '7684413@qq.com', '7684413@qq.com', '1234576', '127.0.0.1', 8080, b'0', b'0', '1', '2023-01-25 17:39:52', '1', '2025-04-04 16:34:40', b'0'); 1279 INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '7684413@qq.com', '7684413@qq.com', '1234576', '127.0.0.1', 8080, b'0', b'0', '1', '2023-01-25 17:39:52', '1', '2025-04-04 16:34:40', b'0');
1249 -INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'ydym_test@163.com', 'ydym_test@163.com', 'WBZTEINMIFVRYSOE', 'smtp.163.com', 465, b'1', b'0', '1', '2023-01-26 01:26:03', '1', '2023-04-12 22:39:38', b'0'); 1280 +INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'ydym_test@163.com', 'ydym_test@163.com', 'WBZTEINMIFVRYSOE', 'smtp.163.com', 465, b'1', b'0', '1', '2023-01-26 01:26:03', '1', '2025-07-26 21:57:55', b'0');
1250 INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3, '76854114@qq.com', '3335', '11234', 'yunai1.cn', 466, b'0', b'0', '1', '2023-01-27 15:06:38', '1', '2023-01-27 07:08:36', b'1'); 1281 INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3, '76854114@qq.com', '3335', '11234', 'yunai1.cn', 466, b'0', b'0', '1', '2023-01-27 15:06:38', '1', '2023-01-27 07:08:36', b'1');
1251 INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '7685413x@qq.com', '2', '3', '4', 5, b'1', b'0', '1', '2023-04-12 23:05:06', '1', '2023-04-12 15:05:11', b'1'); 1282 INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '7685413x@qq.com', '2', '3', '4', 5, b'1', b'0', '1', '2023-04-12 23:05:06', '1', '2023-04-12 15:05:11', b'1');
1252 COMMIT; 1283 COMMIT;
@@ -1260,8 +1291,8 @@ CREATE TABLE `system_mail_log` ( @@ -1260,8 +1291,8 @@ CREATE TABLE `system_mail_log` (
1260 `user_id` bigint NULL DEFAULT NULL COMMENT '用户编号', 1291 `user_id` bigint NULL DEFAULT NULL COMMENT '用户编号',
1261 `user_type` tinyint NULL DEFAULT NULL COMMENT '用户类型', 1292 `user_type` tinyint NULL DEFAULT NULL COMMENT '用户类型',
1262 `to_mails` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '接收邮箱地址', 1293 `to_mails` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '接收邮箱地址',
1263 - `cc_mails` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '抄送邮箱地址',  
1264 - `bcc_mails` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密送邮箱地址', 1294 + `cc_mails` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '抄送邮箱地址',
  1295 + `bcc_mails` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '密送邮箱地址',
1265 `account_id` bigint NOT NULL COMMENT '邮箱账号编号', 1296 `account_id` bigint NOT NULL COMMENT '邮箱账号编号',
1266 `from_mail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '发送邮箱地址', 1297 `from_mail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '发送邮箱地址',
1267 `template_id` bigint NOT NULL COMMENT '模板编号', 1298 `template_id` bigint NOT NULL COMMENT '模板编号',
@@ -1280,7 +1311,7 @@ CREATE TABLE `system_mail_log` ( @@ -1280,7 +1311,7 @@ CREATE TABLE `system_mail_log` (
1280 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 1311 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
1281 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 1312 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
1282 PRIMARY KEY (`id`) USING BTREE 1313 PRIMARY KEY (`id`) USING BTREE
1283 -) ENGINE = InnoDB AUTO_INCREMENT = 360 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮件日志表'; 1314 +) ENGINE = InnoDB AUTO_INCREMENT = 367 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮件日志表';
1284 1315
1285 -- ---------------------------- 1316 -- ----------------------------
1286 -- Records of system_mail_log 1317 -- Records of system_mail_log
@@ -1316,8 +1347,8 @@ CREATE TABLE `system_mail_template` ( @@ -1316,8 +1347,8 @@ CREATE TABLE `system_mail_template` (
1316 -- ---------------------------- 1347 -- ----------------------------
1317 BEGIN; 1348 BEGIN;
1318 INSERT INTO `system_mail_template` (`id`, `name`, `code`, `account_id`, `nickname`, `title`, `content`, `params`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (13, '后台用户短信登录', 'admin-sms-login', 1, '奥特曼', '你猜我猜', '<p>您的验证码是{code},名字是{name}</p>', '[\"code\",\"name\"]', 0, '3', '1', '2021-10-11 08:10:00', '1', '2023-12-02 19:51:14', b'0'); 1349 INSERT INTO `system_mail_template` (`id`, `name`, `code`, `account_id`, `nickname`, `title`, `content`, `params`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (13, '后台用户短信登录', 'admin-sms-login', 1, '奥特曼', '你猜我猜', '<p>您的验证码是{code},名字是{name}</p>', '[\"code\",\"name\"]', 0, '3', '1', '2021-10-11 08:10:00', '1', '2023-12-02 19:51:14', b'0');
1319 -INSERT INTO `system_mail_template` (`id`, `name`, `code`, `account_id`, `nickname`, `title`, `content`, `params`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (14, '测试模版', 'test_01', 2, '芋艿', '一个标题', '<p>你是 {key01} 吗?</p><p><br></p><p>是的话,赶紧 {key02} 一下!</p>', '[\"key01\",\"key02\"]', 0, NULL, '1', '2023-01-26 01:27:40', '1', '2023-01-27 10:32:16', b'0');  
1320 -INSERT INTO `system_mail_template` (`id`, `name`, `code`, `account_id`, `nickname`, `title`, `content`, `params`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (15, '3', '2', 2, '7', '4', '<p>45</p>', '[]', 1, '80', '1', '2023-01-27 15:50:35', '1', '2023-01-27 16:34:49', b'0'); 1350 +INSERT INTO `system_mail_template` (`id`, `name`, `code`, `account_id`, `nickname`, `title`, `content`, `params`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (14, '测试模版', 'test_01', 2, '芋艿', '一个标题', '<p>你是 {key01} 吗?</p><p><br></p><p>是的话,赶紧 {key02} 一下!</p>', '[\"key01\",\"key02\"]', 0, NULL, '1', '2023-01-26 01:27:40', '1', '2025-07-26 21:48:45', b'0');
  1351 +INSERT INTO `system_mail_template` (`id`, `name`, `code`, `account_id`, `nickname`, `title`, `content`, `params`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (15, '3', '2', 2, '7', '4', '<p>45</p>', '[]', 1, '80', '1', '2023-01-27 15:50:35', '1', '2025-07-26 21:47:49', b'1');
1321 COMMIT; 1352 COMMIT;
1322 1353
1323 -- ---------------------------- 1354 -- ----------------------------
@@ -1345,7 +1376,7 @@ CREATE TABLE `system_menu` ( @@ -1345,7 +1376,7 @@ CREATE TABLE `system_menu` (
1345 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 1376 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
1346 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 1377 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
1347 PRIMARY KEY (`id`) USING BTREE 1378 PRIMARY KEY (`id`) USING BTREE
1348 -) ENGINE = InnoDB AUTO_INCREMENT = 5013 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表'; 1379 +) ENGINE = InnoDB AUTO_INCREMENT = 5042 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
1349 1380
1350 -- ---------------------------- 1381 -- ----------------------------
1351 -- Records of system_menu 1382 -- Records of system_menu
@@ -2203,55 +2234,47 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i @@ -2203,55 +2234,47 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
2203 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2924, '工具删除', 'ai:tool:delete', 3, 4, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0'); 2234 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2924, '工具删除', 'ai:tool:delete', 3, 4, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0');
2204 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4000, 'IoT 物联网', '', 1, 500, 0, '/iot', 'fa-solid:hdd', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:55:28', '1', '2024-12-07 15:58:34', b'0'); 2235 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4000, 'IoT 物联网', '', 1, 500, 0, '/iot', 'fa-solid:hdd', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:55:28', '1', '2024-12-07 15:58:34', b'0');
2205 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4001, '设备接入', '', 1, 2, 4000, 'device', 'ep:platform', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:57:56', '1', '2025-02-27 08:39:49', b'0'); 2236 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4001, '设备接入', '', 1, 2, 4000, 'device', 'ep:platform', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:57:56', '1', '2025-02-27 08:39:49', b'0');
2206 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4002, '产品管理', '', 2, 2, 4001, 'product', 'fa-solid:tools', 'iot/product/product/index', 'IoTProduct', 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '1', '2024-12-07 18:47:53', b'0'); 2237 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4002, '产品管理', '', 2, 1, 4001, 'product', 'fa-solid:tools', 'iot/product/product/index', 'IoTProduct', 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '1', '2025-06-15 20:56:06', b'0');
2207 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4003, '产品查询', 'iot:product:query', 3, 1, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:00', b'0'); 2238 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4003, '产品查询', 'iot:product:query', 3, 1, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:00', b'0');
2208 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4004, '产品创建', 'iot:product:create', 3, 2, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:03', b'0'); 2239 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4004, '产品创建', 'iot:product:create', 3, 2, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:03', b'0');
2209 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4005, '产品更新', 'iot:product:update', 3, 3, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:05', b'0'); 2240 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4005, '产品更新', 'iot:product:update', 3, 3, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:05', b'0');
2210 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4006, '产品删除', 'iot:product:delete', 3, 4, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:06', b'0'); 2241 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4006, '产品删除', 'iot:product:delete', 3, 4, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:06', b'0');
2211 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4007, '产品导出', 'iot:product:export', 3, 5, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:13', b'0'); 2242 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4007, '产品导出', 'iot:product:export', 3, 5, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:13', b'0');
2212 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4008, '设备管理', '', 2, 4, 4001, 'device', 'fa:mobile', 'iot/device/device/index', 'IoTDevice', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-14 11:39:30', b'0'); 2243 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4008, '设备管理', '', 2, 2, 4001, 'device', 'fa:mobile', 'iot/device/device/index', 'IoTDevice', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2025-06-15 20:56:10', b'0');
2213 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4009, '设备查询', 'iot:device:query', 3, 1, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:40', b'0'); 2244 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4009, '设备查询', 'iot:device:query', 3, 1, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:40', b'0');
2214 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4010, '设备创建', 'iot:device:create', 3, 2, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:41', b'0'); 2245 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4010, '设备创建', 'iot:device:create', 3, 2, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:41', b'0');
2215 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4011, '设备更新', 'iot:device:update', 3, 3, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:42', b'0'); 2246 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4011, '设备更新', 'iot:device:update', 3, 3, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:42', b'0');
2216 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4012, '设备删除', 'iot:device:delete', 3, 4, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:43', b'0'); 2247 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4012, '设备删除', 'iot:device:delete', 3, 4, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:43', b'0');
2217 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4013, '设备导出', 'iot:device:export', 3, 5, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:44', b'0'); 2248 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4013, '设备导出', 'iot:device:export', 3, 5, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:44', b'0');
2218 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4014, '产品分类', '', 2, 1, 4001, 'product-category', 'ep:notebook', 'iot/product/category/index', 'IotProductCategory', 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '1', '2024-12-07 16:31:52', b'0'); 2249 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4014, '产品分类', '', 2, 3, 4001, 'product-category', 'ep:notebook', 'iot/product/category/index', 'IotProductCategory', 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '1', '2025-06-15 20:56:39', b'0');
2219 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4015, '产品分类查询', 'iot:product-category:query', 3, 1, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0'); 2250 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4015, '产品分类查询', 'iot:product-category:query', 3, 1, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0');
2220 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4016, '产品分类创建', 'iot:product-category:create', 3, 2, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0'); 2251 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4016, '产品分类创建', 'iot:product-category:create', 3, 2, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0');
2221 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4017, '产品分类更新', 'iot:product-category:update', 3, 3, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0'); 2252 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4017, '产品分类更新', 'iot:product-category:update', 3, 3, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0');
2222 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4018, '产品分类删除', 'iot:product-category:delete', 3, 4, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0'); 2253 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4018, '产品分类删除', 'iot:product-category:delete', 3, 4, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0');
2223 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4025, '插件管理', '', 2, 5, 4047, 'plugin-config', 'ep:folder-opened', 'iot/plugin/index', 'IoTPlugin', 0, b'1', b'1', b'1', '', '2024-12-09 21:25:06', '1', '2025-02-05 22:23:12', b'0');  
2224 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4026, '插件查询', 'iot:plugin-config:query', 3, 1, 4025, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:20', b'0');  
2225 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4027, '插件创建', 'iot:plugin-config:create', 3, 2, 4025, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:16', b'0');  
2226 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4028, '插件更新', 'iot:plugin-config:update', 3, 3, 4025, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:12', b'0');  
2227 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4029, '插件删除', 'iot:plugin-config:delete', 3, 4, 4025, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:09', b'0');  
2228 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4030, '插件导出', 'iot:plugin-config:export', 3, 5, 4025, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:06', b'0');  
2229 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4031, '设备分组', '', 2, 3, 4001, 'device-group', 'fa-solid:layer-group', 'iot/device/group/index', 'IotDeviceGroup', 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '1', '2024-12-14 17:09:17', b'0'); 2254 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4031, '设备分组', '', 2, 3, 4001, 'device-group', 'fa-solid:layer-group', 'iot/device/group/index', 'IotDeviceGroup', 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '1', '2024-12-14 17:09:17', b'0');
2230 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4032, '设备分组查询', 'iot:device-group:query', 3, 1, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0'); 2255 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4032, '设备分组查询', 'iot:device-group:query', 3, 1, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0');
2231 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4033, '设备分组创建', 'iot:device-group:create', 3, 2, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0'); 2256 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4033, '设备分组创建', 'iot:device-group:create', 3, 2, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0');
2232 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4034, '设备分组更新', 'iot:device-group:update', 3, 3, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0'); 2257 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4034, '设备分组更新', 'iot:device-group:update', 3, 3, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0');
2233 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4035, '设备分组删除', 'iot:device-group:delete', 3, 4, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0'); 2258 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4035, '设备分组删除', 'iot:device-group:delete', 3, 4, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0');
2234 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4036, '设备导入', 'iot:device:import', 3, 6, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-12-15 10:35:47', '1', '2024-12-15 10:35:47', b'0'); 2259 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4036, '设备导入', 'iot:device:import', 3, 6, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-12-15 10:35:47', '1', '2024-12-15 10:35:47', b'0');
2235 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4037, '产品物模型', '', 2, 2, 4001, 'thing-model', 'ep:mostly-cloudy', 'iot/thingmodel/index', 'IoTThingModel', 0, b'0', b'0', b'0', '', '2024-12-16 17:17:50', '1', '2024-12-27 11:03:37', b'0'); 2260 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4037, '产品物模型', '', 2, 99, 4001, 'thing-model', 'ep:mostly-cloudy', 'iot/thingmodel/index', 'IoTThingModel', 0, b'0', b'0', b'0', '', '2024-12-16 17:17:50', '1', '2025-06-15 20:56:19', b'0');
2236 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4038, '产品物模型功能查询', 'iot:thing-model:query', 3, 1, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:51', '', '2025-03-17 09:14:54', b'0'); 2261 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4038, '产品物模型功能查询', 'iot:thing-model:query', 3, 1, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:51', '', '2025-03-17 09:14:54', b'0');
2237 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4039, '产品物模型功能创建', 'iot:thing-model:create', 3, 2, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:14:58', b'0'); 2262 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4039, '产品物模型功能创建', 'iot:thing-model:create', 3, 2, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:14:58', b'0');
2238 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4040, '产品物模型功能更新', 'iot:thing-model:update', 3, 3, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:03', b'0'); 2263 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4040, '产品物模型功能更新', 'iot:thing-model:update', 3, 3, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:03', b'0');
2239 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4041, '产品物模型功能删除', 'iot:thing-model:delete', 3, 4, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:06', b'0'); 2264 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4041, '产品物模型功能删除', 'iot:thing-model:delete', 3, 4, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:06', b'0');
2240 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4042, '产品物模型功能导出', 'iot:thing-model:export', 3, 5, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:53', '', '2025-03-17 09:15:09', b'0'); 2265 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4042, '产品物模型功能导出', 'iot:thing-model:export', 3, 5, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:53', '', '2025-03-17 09:15:09', b'0');
2241 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4043, '设备上行', 'iot:device:upstream', 3, 7, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-28 04:40:16', '1', '2025-01-31 22:45:53', b'0'); 2266 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4043, '设备消息发送', 'iot:device:message-send', 3, 12, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-28 04:40:16', '1', '2025-06-14 14:09:26', b'0');
2242 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4044, '设备属性查询', 'iot:device:property-query', 3, 10, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-28 11:52:54', '1', '2025-01-28 11:52:54', b'0'); 2267 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4044, '设备属性查询', 'iot:device:property-query', 3, 10, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-28 11:52:54', '1', '2025-01-28 11:52:54', b'0');
2243 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4045, '设备日志查询', 'iot:device:log-query', 3, 11, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-28 11:53:22', '1', '2025-01-28 11:53:22', b'0');  
2244 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4046, '设备下行', 'iot:device:downstream', 3, 8, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-31 22:46:11', '1', '2025-01-31 22:46:11', b'0');  
2245 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4047, '运维管理', '', 1, 2, 4000, 'operations', 'fa:cog', '', '', 0, b'1', b'1', b'1', '1', '2025-02-05 22:21:37', '1', '2025-02-05 22:22:53', b'0'); 2268 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4045, '设备消息查询', 'iot:device:message-query', 3, 11, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-28 11:53:22', '1', '2025-06-14 11:11:20', b'0');
  2269 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4047, '运维管理', '', 1, 4, 4000, 'operation', 'fa:align-center', '', '', 0, b'1', b'1', b'1', '1', '2025-02-05 22:21:37', '\"1\"', '2025-06-30 20:12:48', b'0');
2246 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4048, '规则引擎', '', 1, 3, 4000, 'rule', 'fa-solid:cogs', '', '', 0, b'1', b'1', b'1', '1', '2025-02-11 14:10:54', '1', '2025-02-11 14:10:54', b'0'); 2270 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4048, '规则引擎', '', 1, 3, 4000, 'rule', 'fa-solid:cogs', '', '', 0, b'1', b'1', b'1', '1', '2025-02-11 14:10:54', '1', '2025-02-11 14:10:54', b'0');
2247 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4049, '场景联动', '', 2, 1, 4048, 'scene', 'ep:link', 'iot/rule/scene/index', 'Scene', 0, b'1', b'1', b'1', '1', '2025-02-11 14:12:44', '1', '2025-02-12 10:15:36', b'0');  
2248 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4050, 'IoT首页', '', 2, 1, 4000, 'home', 'ep:home-filled', 'iot/home/index', 'IotHome', 0, b'1', b'1', b'1', '1', '2025-02-27 08:39:35', '1', '2025-02-27 08:40:28', b'0');  
2249 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4051, '数据桥梁', '', 2, 0, 4048, 'data-bridge', 'ep:guide', 'iot/rule/databridge/index', 'IotDataBridge', 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '1', '2025-03-09 13:47:51', b'0');  
2250 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4052, 'IoT 数据桥梁查询', 'iot:data-bridge:query', 3, 1, 4051, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', b'0');  
2251 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4053, 'IoT 数据桥梁创建', 'iot:data-bridge:create', 3, 2, 4051, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', b'0');  
2252 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4054, 'IoT 数据桥梁更新', 'iot:data-bridge:update', 3, 3, 4051, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', b'0');  
2253 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4055, 'IoT 数据桥梁删除', 'iot:data-bridge:delete', 3, 4, 4051, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-09 13:47:12', '', '2025-03-09 13:47:12', b'0');  
2254 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4056, 'IoT 数据桥梁导出', 'iot:data-bridge:export', 3, 5, 4051, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-09 13:47:12', '', '2025-03-09 13:47:12', b'0'); 2271 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4049, '场景联动', '', 2, 1, 4048, 'scene', 'ep:link', 'iot/rule/scene/index', 'IoTSceneRule', 0, b'1', b'1', b'1', '1', '2025-02-11 14:12:44', '\"1\"', '2025-08-09 15:38:32', b'0');
  2272 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4050, 'IoT 首页', '', 2, 1, 4000, 'home', 'ep:home-filled', 'iot/home/index', 'IotHome', 0, b'1', b'1', b'1', '1', '2025-02-27 08:39:35', '1', '2025-06-24 14:22:50', b'0');
  2273 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4051, '数据流转', '', 2, 2, 4048, 'data-rule', 'ep:guide', 'iot/rule/data/index', 'IoTDataRule', 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '\"1\"', '2025-08-09 15:38:37', b'0');
  2274 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4052, '数据流转规则查询', 'iot:data-rule:query', 3, 1, 4051, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '1', '2025-06-24 20:48:04', b'0');
  2275 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4053, '数据流转规则创建', 'iot:data-rule:create', 3, 2, 4051, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '1', '2025-06-24 20:48:08', b'0');
  2276 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4054, '数据流转规则更新', 'iot:data-rule:update', 3, 3, 4051, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '1', '2025-06-24 20:48:11', b'0');
  2277 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4055, '数据流转规则删除', 'iot:data-rule:delete', 3, 4, 4051, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-03-09 13:47:12', '1', '2025-06-24 20:48:15', b'0');
2255 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5000, 'AI 工作流', '', 2, 5, 2758, 'workflow', 'fa:hand-grab-o', 'ai/workflow/index.vue', 'AiWorkflow', 0, b'1', b'1', b'1', '1', '2025-03-25 09:50:27', '1', '2025-05-03 18:55:12', b'0'); 2278 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5000, 'AI 工作流', '', 2, 5, 2758, 'workflow', 'fa:hand-grab-o', 'ai/workflow/index.vue', 'AiWorkflow', 0, b'1', b'1', b'1', '1', '2025-03-25 09:50:27', '1', '2025-05-03 18:55:12', b'0');
2256 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5001, 'AI 工作流查询', 'ai:workflow:query', 3, 1, 5000, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-03-25 09:51:11', '1', '2025-03-25 09:51:11', b'0'); 2279 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5001, 'AI 工作流查询', 'ai:workflow:query', 3, 1, 5000, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-03-25 09:51:11', '1', '2025-03-25 09:51:11', b'0');
2257 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5002, 'AI 工作流创建', 'ai:workflow:create', 3, 2, 5000, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-03-25 09:51:28', '1', '2025-03-25 09:51:28', b'0'); 2280 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5002, 'AI 工作流创建', 'ai:workflow:create', 3, 2, 5000, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-03-25 09:51:28', '1', '2025-03-25 09:51:28', b'0');
@@ -2262,6 +2285,34 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i @@ -2262,6 +2285,34 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
2262 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5010, '租户切换', 'system:tenant:visit', 3, 999, 1138, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-05-05 15:25:32', '1', '2025-05-05 15:25:32', b'0'); 2285 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5010, '租户切换', 'system:tenant:visit', 3, 999, 1138, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-05-05 15:25:32', '1', '2025-05-05 15:25:32', b'0');
2263 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5011, '转账订单查询', 'pay:transfer:query', 3, 1, 2559, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-05-08 12:46:53', '1', '2025-05-08 12:46:53', b'0'); 2286 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5011, '转账订单查询', 'pay:transfer:query', 3, 1, 2559, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-05-08 12:46:53', '1', '2025-05-08 12:46:53', b'0');
2264 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5012, '转账订单导出', 'pay:transfer:export', 3, 2, 2559, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-05-10 17:00:28', '1', '2025-05-10 17:00:28', b'0'); 2287 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5012, '转账订单导出', 'pay:transfer:export', 3, 2, 2559, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-05-10 17:00:28', '1', '2025-05-10 17:00:28', b'0');
  2288 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5013, '场景联动查询', 'iot:rule-scene:query', 3, 1, 4049, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-20 16:53:01', '1', '2025-06-20 16:53:01', b'0');
  2289 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5014, '场景联动创建', 'iot:rule-scene:create', 3, 2, 4049, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-20 16:54:31', '1', '2025-06-20 16:54:31', b'0');
  2290 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5015, '场景联动更新', 'iot:rule-scene:update', 3, 3, 4049, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-20 16:54:47', '1', '2025-06-20 16:54:47', b'0');
  2291 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5016, '场景联动删除', 'iot:rule-scene:delete', 3, 4, 4049, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-20 16:55:04', '1', '2025-06-20 16:55:27', b'0');
  2292 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5017, '场景联动导出', 'iot:rule-scene:export', 3, 5, 4049, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-20 16:57:56', '1', '2025-06-20 16:57:56', b'0');
  2293 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5018, '数据流转目的查询', 'iot:data-sink:query', 3, 11, 4051, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-24 20:48:40', '1', '2025-06-24 20:48:40', b'0');
  2294 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5019, '数据流转目的创建', 'iot:data-sink:create', 3, 12, 4051, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-24 20:48:57', '1', '2025-06-24 20:48:57', b'0');
  2295 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5020, '数据流转目的更新', 'iot:data-sink:update', 3, 13, 4051, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-24 20:49:10', '1', '2025-06-24 20:49:10', b'0');
  2296 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5021, '数据流转目的删除', 'iot:data-sink:delete', 3, 14, 4051, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-06-24 20:49:23', '1', '2025-06-24 20:49:23', b'0');
  2297 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5022, '告警配置', '', 2, 1, 5028, 'config', 'fa:connectdevelop', 'iot/alert/config/index', 'IotAlertConfig', 0, b'1', b'1', b'1', '', '2025-06-27 14:28:59', '1', '2025-06-27 22:31:19', b'0');
  2298 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5023, '告警配置查询', 'iot:alert-config:query', 3, 1, 5022, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-27 14:28:59', '1', '2025-06-28 16:00:31', b'0');
  2299 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5024, '告警配置创建', 'iot:alert-config:create', 3, 2, 5022, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-27 14:28:59', '1', '2025-06-28 16:00:35', b'0');
  2300 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5025, '告警配置更新', 'iot:alert-config:update', 3, 3, 5022, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-27 14:28:59', '1', '2025-06-28 16:00:43', b'0');
  2301 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5026, '告警配置删除', 'iot:alert-config:delete', 3, 4, 5022, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-27 14:29:00', '1', '2025-06-28 16:00:39', b'0');
  2302 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5028, '告警中心', '', 1, 3, 4000, 'alert', 'fa:soundcloud', '', '', 0, b'1', b'1', b'1', '1', '2025-06-27 22:30:04', '1', '2025-06-27 22:30:19', b'0');
  2303 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5029, '告警记录', '', 2, 2, 5028, 'record', 'fa-solid:record-vinyl', 'iot/alert/record/index', 'IotAlertRecord', 0, b'1', b'1', b'1', '', '2025-06-28 07:59:32', '1', '2025-06-28 16:01:48', b'0');
  2304 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5030, '告警记录查询', 'iot:alert-record:query', 3, 1, 5029, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-28 07:59:32', '1', '2025-06-28 16:00:53', b'0');
  2305 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5031, '告警记录处理', 'iot:alert-record:process', 3, 2, 5029, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-28 07:59:32', '1', '2025-06-28 16:01:04', b'0');
  2306 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5032, 'OTA 固件', '', 2, 1, 4047, 'ota/firmware', 'fa-solid:award', 'iot/ota/firmware/index', 'IoTOtaFirmware', 0, b'1', b'1', b'1', '', '2025-06-30 07:50:29', '\"1\"', '2025-06-30 20:13:28', b'0');
  2307 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5033, 'OTA 固件查询', 'iot:ota-firmware:query', 3, 1, 5032, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-30 07:50:29', '\"1\"', '2025-06-30 17:38:12', b'0');
  2308 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5034, 'OTA 固件创建', 'iot:ota-firmware:create', 3, 2, 5032, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-30 07:50:29', '\"1\"', '2025-06-30 17:38:21', b'0');
  2309 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5035, 'OTA 固件更新', 'iot:ota-firmware:update', 3, 3, 5032, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-30 07:50:29', '\"1\"', '2025-06-30 17:38:29', b'0');
  2310 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5036, 'OTA 固件删除', 'iot:ota-firmware:delete', 3, 4, 5032, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-30 07:50:29', '\"1\"', '2025-06-30 17:38:37', b'0');
  2311 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5037, 'OTA 升级任务查询', 'iot:ota-task:create', 3, 11, 5032, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-07-02 23:56:56', '1', '2025-07-02 23:56:56', b'0');
  2312 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5038, 'OTA 升级任务取消', 'iot:ota-task:cancel', 3, 13, 5032, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-07-02 23:57:26', '1', '2025-07-02 23:57:26', b'0');
  2313 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5039, 'OTA 升级任务创建', 'iot:ota-task:create', 3, 12, 5032, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-07-02 23:57:52', '1', '2025-07-02 23:57:52', b'0');
  2314 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5040, 'OTA 升级记录查询', 'iot:ota-task-record:query', 3, 21, 5032, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-07-02 23:58:30', '1', '2025-07-02 23:58:30', b'0');
  2315 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5041, 'OTA 升级记录取消', 'iot:ota-task-record:cancel', 3, 23, 5032, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-07-02 23:59:18', '1', '2025-07-02 23:59:18', b'0');
2265 COMMIT; 2316 COMMIT;
2266 2317
2267 -- ---------------------------- 2318 -- ----------------------------
@@ -2287,7 +2338,7 @@ CREATE TABLE `system_notice` ( @@ -2287,7 +2338,7 @@ CREATE TABLE `system_notice` (
2287 -- Records of system_notice 2338 -- Records of system_notice
2288 -- ---------------------------- 2339 -- ----------------------------
2289 BEGIN; 2340 BEGIN;
2290 -INSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, '芋道的公众', '<p>新版本内容133</p>', 1, 0, 'admin', '2021-01-05 17:03:48', '1', '2022-05-04 21:00:20', b'0', 1); 2341 +INSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, '芋道的公众', '<p>新版本内容133222</p>', 1, 0, 'admin', '2021-01-05 17:03:48', '\"1\"', '2025-08-31 09:38:22', b'0', 1);
2291 INSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, '维护通知:2018-07-01 系统凌晨维护', '<p><img src=\"http://test.yudao.iocoder.cn/b7cb3cf49b4b3258bf7309a09dd2f4e5.jpg\" alt=\"\" data-href=\"\">11112222<img src=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\" alt=\"image\" data-href=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\">3333</p>', 2, 1, 'admin', '2021-01-05 17:03:48', '1', '2025-04-18 23:56:40', b'0', 1); 2342 INSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, '维护通知:2018-07-01 系统凌晨维护', '<p><img src=\"http://test.yudao.iocoder.cn/b7cb3cf49b4b3258bf7309a09dd2f4e5.jpg\" alt=\"\" data-href=\"\">11112222<img src=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\" alt=\"image\" data-href=\"http://test.yudao.iocoder.cn/fe44fc7bdb82ca421184b2eebbaee9e2148d4a1827479a4eb4521e11d2a062ba.png\">3333</p>', 2, 1, 'admin', '2021-01-05 17:03:48', '1', '2025-04-18 23:56:40', b'0', 1);
2292 INSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, '我是测试标题', '<p>哈哈哈哈123</p>', 1, 0, '110', '2022-02-22 01:01:25', '110', '2022-02-22 01:01:46', b'0', 121); 2343 INSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, '我是测试标题', '<p>哈哈哈哈123</p>', 1, 0, '110', '2022-02-22 01:01:25', '110', '2022-02-22 01:01:46', b'0', 121);
2293 COMMIT; 2344 COMMIT;
@@ -2383,7 +2434,7 @@ CREATE TABLE `system_oauth2_access_token` ( @@ -2383,7 +2434,7 @@ CREATE TABLE `system_oauth2_access_token` (
2383 PRIMARY KEY (`id`) USING BTREE, 2434 PRIMARY KEY (`id`) USING BTREE,
2384 INDEX `idx_access_token`(`access_token` ASC) USING BTREE, 2435 INDEX `idx_access_token`(`access_token` ASC) USING BTREE,
2385 INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE 2436 INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE
2386 -) ENGINE = InnoDB AUTO_INCREMENT = 16697 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌'; 2437 +) ENGINE = InnoDB AUTO_INCREMENT = 20179 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
2387 2438
2388 -- ---------------------------- 2439 -- ----------------------------
2389 -- Records of system_oauth2_access_token 2440 -- Records of system_oauth2_access_token
@@ -2451,7 +2502,7 @@ CREATE TABLE `system_oauth2_client` ( @@ -2451,7 +2502,7 @@ CREATE TABLE `system_oauth2_client` (
2451 -- Records of system_oauth2_client 2502 -- Records of system_oauth2_client
2452 -- ---------------------------- 2503 -- ----------------------------
2453 BEGIN; 2504 BEGIN;
2454 -INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, 'default', 'admin123', '芋道源码', 'http://test.yudao.iocoder.cn/20250502/sort2_1746189740718.png', '我是描述', 0, 1800, 2592000, '[\"https://www.iocoder.cn\",\"https://doc.iocoder.cn\"]', '[\"password\",\"authorization_code\",\"implicit\",\"refresh_token\"]', '[\"user.read\",\"user.write\"]', '[]', '[\"user.read\",\"user.write\"]', '[]', '{}', '1', '2022-05-11 21:47:12', '1', '2025-05-02 20:42:22', b'0'); 2505 +INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, 'default', 'admin123', '芋道源码', 'http://test.yudao.iocoder.cn/20250502/sort2_1746189740718.png', '我是描述', 0, 1800, 2592000, '[\"https://www.iocoder.cn\",\"https://doc.iocoder.cn\"]', '[\"password\",\"authorization_code\",\"implicit\",\"refresh_token\",\"client_credentials\"]', '[\"user.read\",\"user.write\"]', '[]', '[\"user.read\",\"user.write\"]', '[]', '{}', '1', '2022-05-11 21:47:12', '1', '2025-08-21 10:04:50', b'0');
2455 INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (40, 'test', 'test2', 'biubiu', 'http://test.yudao.iocoder.cn/xx/20250502/ed07110a37464b5299f8bd7c67ad65c7_1746187077009.jpg', '啦啦啦啦', 0, 1800, 43200, '[\"https://www.iocoder.cn\"]', '[\"password\",\"authorization_code\",\"implicit\"]', '[\"user_info\",\"projects\"]', '[\"user_info\"]', '[]', '[]', '{}', '1', '2022-05-12 00:28:20', '1', '2025-05-02 19:58:08', b'0'); 2506 INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (40, 'test', 'test2', 'biubiu', 'http://test.yudao.iocoder.cn/xx/20250502/ed07110a37464b5299f8bd7c67ad65c7_1746187077009.jpg', '啦啦啦啦', 0, 1800, 43200, '[\"https://www.iocoder.cn\"]', '[\"password\",\"authorization_code\",\"implicit\"]', '[\"user_info\",\"projects\"]', '[\"user_info\"]', '[]', '[]', '{}', '1', '2022-05-12 00:28:20', '1', '2025-05-02 19:58:08', b'0');
2456 INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (41, 'yudao-sso-demo-by-code', 'test', '基于授权码模式,如何实现 SSO 单点登录?', 'http://test.yudao.iocoder.cn/it/20250502/sign_1746181948685.png', NULL, 0, 1800, 43200, '[\"http://127.0.0.1:18080\"]', '[\"authorization_code\",\"refresh_token\"]', '[\"user.read\",\"user.write\"]', '[]', '[]', '[]', NULL, '1', '2022-09-29 13:28:31', '1', '2025-05-02 18:32:30', b'0'); 2507 INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (41, 'yudao-sso-demo-by-code', 'test', '基于授权码模式,如何实现 SSO 单点登录?', 'http://test.yudao.iocoder.cn/it/20250502/sign_1746181948685.png', NULL, 0, 1800, 43200, '[\"http://127.0.0.1:18080\"]', '[\"authorization_code\",\"refresh_token\"]', '[\"user.read\",\"user.write\"]', '[]', '[]', '[]', NULL, '1', '2022-09-29 13:28:31', '1', '2025-05-02 18:32:30', b'0');
2457 INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (42, 'yudao-sso-demo-by-password', 'test', '基于密码模式,如何实现 SSO 单点登录?', 'http://test.yudao.iocoder.cn/604bdc695e13b3b22745be704d1f2aa8ee05c5f26f9fead6d1ca49005afbc857.jpeg', NULL, 0, 1800, 43200, '[\"http://127.0.0.1:18080\"]', '[\"password\",\"refresh_token\"]', '[\"user.read\",\"user.write\"]', '[]', '[]', '[]', NULL, '1', '2022-10-04 17:40:16', '1', '2025-05-04 16:00:46', b'0'); 2508 INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (42, 'yudao-sso-demo-by-password', 'test', '基于密码模式,如何实现 SSO 单点登录?', 'http://test.yudao.iocoder.cn/604bdc695e13b3b22745be704d1f2aa8ee05c5f26f9fead6d1ca49005afbc857.jpeg', NULL, 0, 1800, 43200, '[\"http://127.0.0.1:18080\"]', '[\"password\",\"refresh_token\"]', '[\"user.read\",\"user.write\"]', '[]', '[]', '[]', NULL, '1', '2022-10-04 17:40:16', '1', '2025-05-04 16:00:46', b'0');
@@ -2505,7 +2556,7 @@ CREATE TABLE `system_oauth2_refresh_token` ( @@ -2505,7 +2556,7 @@ CREATE TABLE `system_oauth2_refresh_token` (
2505 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 2556 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
2506 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', 2557 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
2507 PRIMARY KEY (`id`) USING BTREE 2558 PRIMARY KEY (`id`) USING BTREE
2508 -) ENGINE = InnoDB AUTO_INCREMENT = 2036 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌'; 2559 +) ENGINE = InnoDB AUTO_INCREMENT = 2201 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
2509 2560
2510 -- ---------------------------- 2561 -- ----------------------------
2511 -- Records of system_oauth2_refresh_token 2562 -- Records of system_oauth2_refresh_token
@@ -2539,7 +2590,7 @@ CREATE TABLE `system_operate_log` ( @@ -2539,7 +2590,7 @@ CREATE TABLE `system_operate_log` (
2539 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 2590 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
2540 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', 2591 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
2541 PRIMARY KEY (`id`) USING BTREE 2592 PRIMARY KEY (`id`) USING BTREE
2542 -) ENGINE = InnoDB AUTO_INCREMENT = 9090 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本'; 2593 +) ENGINE = InnoDB AUTO_INCREMENT = 9134 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本';
2543 2594
2544 -- ---------------------------- 2595 -- ----------------------------
2545 -- Records of system_operate_log 2596 -- Records of system_operate_log
@@ -2607,11 +2658,9 @@ BEGIN; @@ -2607,11 +2658,9 @@ BEGIN;
2607 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, '超级管理员', 'super_admin', 1, 1, '', 0, 1, '超级管理员', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:21', b'0', 1); 2658 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, '超级管理员', 'super_admin', 1, 1, '', 0, 1, '超级管理员', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:21', b'0', 1);
2608 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, '普通角色', 'common', 2, 2, '', 0, 1, '普通角色', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:20', b'0', 1); 2659 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, '普通角色', 'common', 2, 2, '', 0, 1, '普通角色', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:20', b'0', 1);
2609 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3, 'CRM 管理员', 'crm_admin', 2, 1, '', 0, 1, 'CRM 专属角色', '1', '2024-02-24 10:51:13', '1', '2024-02-24 02:51:32', b'0', 1); 2660 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3, 'CRM 管理员', 'crm_admin', 2, 1, '', 0, 1, 'CRM 专属角色', '1', '2024-02-24 10:51:13', '1', '2024-02-24 02:51:32', b'0', 1);
2610 -INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (101, '测试账号', 'test', 0, 1, '[]', 0, 2, '123', '', '2021-01-06 13:49:35', '1', '2025-04-30 17:38:28', b'0', 1);  
2611 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (109, '租户管理员', 'tenant_admin', 0, 1, '', 0, 1, '系统自动生成', '1', '2022-02-22 00:56:14', '1', '2022-02-22 00:56:14', b'0', 121); 2661 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (109, '租户管理员', 'tenant_admin', 0, 1, '', 0, 1, '系统自动生成', '1', '2022-02-22 00:56:14', '1', '2022-02-22 00:56:14', b'0', 121);
2612 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (111, '租户管理员', 'tenant_admin', 0, 1, '', 0, 1, '系统自动生成', '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', b'0', 122); 2662 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (111, '租户管理员', 'tenant_admin', 0, 1, '', 0, 1, '系统自动生成', '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', b'0', 122);
2613 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (155, '测试数据权限', 'test-dp', 3, 2, '[100,102,103,104,105,108]', 0, 2, '', '1', '2025-03-31 14:58:06', '1', '2025-04-17 23:07:44', b'0', 1); 2663 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (155, '测试数据权限', 'test-dp', 3, 2, '[100,102,103,104,105,108]', 0, 2, '', '1', '2025-03-31 14:58:06', '1', '2025-04-17 23:07:44', b'0', 1);
2614 -INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (158, '2', '3', 4, 1, '', 0, 2, NULL, '1', '2025-04-17 20:08:08', '1', '2025-04-17 23:05:31', b'0', 1);  
2615 COMMIT; 2664 COMMIT;
2616 2665
2617 -- ---------------------------- 2666 -- ----------------------------
@@ -2670,87 +2719,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t @@ -2670,87 +2719,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
2670 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (707, 2, 113, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', b'0', 1); 2719 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (707, 2, 113, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', b'0', 1);
2671 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1296, 110, 1, '110', '2022-02-23 00:23:55', '110', '2022-02-23 00:23:55', b'0', 121); 2720 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1296, 110, 1, '110', '2022-02-23 00:23:55', '110', '2022-02-23 00:23:55', b'0', 121);
2672 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1578, 111, 1, '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', b'0', 122); 2721 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1578, 111, 1, '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', b'0', 122);
2673 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1604, 101, 1216, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2674 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1605, 101, 1217, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2675 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1606, 101, 1218, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2676 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1607, 101, 1219, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2677 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1608, 101, 1220, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2678 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1609, 101, 1221, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2679 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1610, 101, 5, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2680 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1611, 101, 1222, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2681 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1612, 101, 1118, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2682 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1613, 101, 1119, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2683 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1614, 101, 1120, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2684 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1615, 101, 1185, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2685 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1616, 101, 1186, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2686 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1617, 101, 1187, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2687 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1618, 101, 1188, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2688 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1619, 101, 1189, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2689 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1620, 101, 1190, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2690 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1621, 101, 1191, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2691 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1622, 101, 1192, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2692 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1623, 101, 1193, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2693 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1624, 101, 1194, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2694 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1625, 101, 1195, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2695 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1627, 101, 1197, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2696 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1628, 101, 1198, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2697 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1629, 101, 1199, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2698 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1630, 101, 1200, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2699 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1631, 101, 1201, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2700 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1632, 101, 1202, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2701 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1633, 101, 1207, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2702 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1634, 101, 1208, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2703 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1635, 101, 1209, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2704 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1636, 101, 1210, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2705 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1637, 101, 1211, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2706 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1638, 101, 1212, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2707 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1639, 101, 1213, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2708 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1640, 101, 1215, '1', '2022-03-19 21:45:52', '1', '2022-03-19 21:45:52', b'0', 1);  
2709 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1641, 101, 2, '1', '2022-04-01 22:21:24', '1', '2022-04-01 22:21:24', b'0', 1);  
2710 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1642, 101, 1031, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2711 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1643, 101, 1032, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2712 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1644, 101, 1033, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2713 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1645, 101, 1034, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2714 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1646, 101, 1035, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2715 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1647, 101, 1050, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2716 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1648, 101, 1051, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2717 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1649, 101, 1052, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2718 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1650, 101, 1053, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2719 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1651, 101, 1054, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2720 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1652, 101, 1056, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2721 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1653, 101, 1057, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2722 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1654, 101, 1058, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2723 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1655, 101, 1059, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2724 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1656, 101, 1060, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2725 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1657, 101, 1066, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2726 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1658, 101, 1067, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2727 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1659, 101, 1070, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2728 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1664, 101, 1075, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2729 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1666, 101, 1077, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2730 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1667, 101, 1078, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2731 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1668, 101, 1082, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2732 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1669, 101, 1083, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2733 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1670, 101, 1084, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2734 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1671, 101, 1085, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2735 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1672, 101, 1086, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2736 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1673, 101, 1087, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2737 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1674, 101, 1088, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2738 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1675, 101, 1089, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2739 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1679, 101, 1237, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2740 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1680, 101, 1238, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2741 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1681, 101, 1239, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2742 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1682, 101, 1240, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2743 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1683, 101, 1241, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2744 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1684, 101, 1242, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2745 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1685, 101, 1243, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2746 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1687, 101, 106, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2747 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1688, 101, 110, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2748 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1689, 101, 111, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2749 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1690, 101, 112, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2750 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1691, 101, 113, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2751 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1692, 101, 114, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2752 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1693, 101, 115, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2753 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1694, 101, 116, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);  
2754 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1729, 109, 100, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121); 2722 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1729, 109, 100, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);
2755 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1730, 109, 101, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121); 2723 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1730, 109, 101, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);
2756 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1731, 109, 1063, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121); 2724 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1731, 109, 1063, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);
@@ -3030,119 +2998,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t @@ -3030,119 +2998,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
3030 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2182, 2, 2016, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1); 2998 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2182, 2, 2016, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1);
3031 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2183, 2, 2017, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1); 2999 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2183, 2, 2017, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1);
3032 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2184, 2, 2018, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1); 3000 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2184, 2, 2018, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1);
3033 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2188, 101, 1024, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3034 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2189, 101, 1, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3035 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2190, 101, 1025, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3036 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2191, 101, 1026, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3037 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2192, 101, 1027, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3038 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2193, 101, 1028, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3039 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2194, 101, 1029, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3040 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2195, 101, 1030, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3041 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2196, 101, 1036, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3042 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2197, 101, 1037, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3043 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2198, 101, 1038, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3044 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2199, 101, 1039, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3045 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2200, 101, 1040, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3046 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2201, 101, 1042, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3047 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2202, 101, 1043, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3048 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2203, 101, 1045, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3049 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2204, 101, 1046, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3050 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2205, 101, 1048, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3051 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2206, 101, 2083, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3052 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2207, 101, 1063, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3053 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2208, 101, 1064, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3054 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2209, 101, 1065, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3055 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2210, 101, 1093, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3056 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2211, 101, 1094, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3057 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2212, 101, 1095, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3058 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2213, 101, 1096, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3059 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2214, 101, 1097, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3060 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2215, 101, 1098, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3061 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2216, 101, 1100, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3062 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2217, 101, 1101, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3063 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2218, 101, 1102, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3064 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2219, 101, 1103, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3065 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2220, 101, 1104, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3066 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2221, 101, 1105, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3067 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2222, 101, 1106, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3068 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2223, 101, 2130, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3069 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2224, 101, 1107, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3070 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2225, 101, 2131, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3071 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2226, 101, 1108, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3072 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2227, 101, 2132, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3073 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2228, 101, 1109, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3074 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2229, 101, 2133, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3075 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2230, 101, 2134, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3076 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2232, 101, 2135, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3077 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2234, 101, 2136, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3078 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2236, 101, 2137, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3079 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2238, 101, 2138, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3080 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2240, 101, 2139, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3081 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2242, 101, 2140, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3082 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2243, 101, 2141, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3083 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2244, 101, 2142, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3084 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2245, 101, 2143, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3085 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2246, 101, 2144, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3086 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2247, 101, 2145, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3087 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2248, 101, 2146, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3088 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2249, 101, 2147, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3089 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2250, 101, 100, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3090 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2251, 101, 2148, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3091 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2252, 101, 101, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3092 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2253, 101, 2149, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3093 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2254, 101, 102, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3094 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2255, 101, 2150, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3095 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2256, 101, 103, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3096 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2257, 101, 2151, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3097 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2258, 101, 104, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3098 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2259, 101, 2152, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3099 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2260, 101, 105, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3100 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2261, 101, 107, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3101 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2262, 101, 108, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3102 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2263, 101, 109, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3103 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2264, 101, 1138, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3104 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2265, 101, 1139, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3105 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2266, 101, 1140, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3106 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2267, 101, 1141, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3107 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2268, 101, 1142, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3108 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2269, 101, 1143, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3109 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2270, 101, 1224, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3110 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2271, 101, 1225, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3111 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2272, 101, 1226, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3112 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2273, 101, 1227, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3113 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2274, 101, 1228, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3114 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2275, 101, 1229, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3115 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2282, 101, 1261, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3116 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2283, 101, 1263, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3117 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2284, 101, 1264, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3118 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2285, 101, 1265, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3119 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2286, 101, 1266, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3120 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2287, 101, 1267, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3121 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2288, 101, 1001, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3122 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2289, 101, 1002, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3123 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2290, 101, 1003, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3124 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2291, 101, 1004, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3125 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2292, 101, 1005, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3126 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2293, 101, 1006, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3127 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2294, 101, 1007, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3128 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2295, 101, 1008, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3129 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2296, 101, 1009, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3130 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2297, 101, 1010, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3131 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2298, 101, 1011, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3132 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2299, 101, 1012, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3133 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2300, 101, 500, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3134 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2301, 101, 1013, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3135 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2302, 101, 501, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3136 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2303, 101, 1014, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3137 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2304, 101, 1015, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3138 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2305, 101, 1016, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3139 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2306, 101, 1017, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3140 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2307, 101, 1018, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3141 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2308, 101, 1019, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3142 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2309, 101, 1020, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3143 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2310, 101, 1021, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3144 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2311, 101, 1022, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3145 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2312, 101, 1023, '1', '2023-02-09 23:49:46', '1', '2023-02-09 23:49:46', b'0', 1);  
3146 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2929, 109, 1224, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121); 3001 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2929, 109, 1224, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);
3147 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2930, 109, 1225, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121); 3002 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2930, 109, 1225, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);
3148 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2931, 109, 1226, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121); 3003 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2931, 109, 1226, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);
@@ -3360,7 +3215,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t @@ -3360,7 +3215,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
3360 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4193, 109, 2730, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121); 3215 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4193, 109, 2730, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);
3361 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4194, 109, 1195, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121); 3216 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4194, 109, 1195, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);
3362 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4195, 109, 2731, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121); 3217 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4195, 109, 2731, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);
3363 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4196, 109, 1196, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);  
3364 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4197, 109, 2732, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121); 3218 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4197, 109, 2732, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);
3365 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4198, 109, 1197, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121); 3219 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4198, 109, 1197, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);
3366 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4199, 109, 2733, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121); 3220 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4199, 109, 2733, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);
@@ -3419,7 +3273,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t @@ -3419,7 +3273,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
3419 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4252, 111, 2730, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122); 3273 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4252, 111, 2730, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
3420 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4253, 111, 1195, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122); 3274 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4253, 111, 1195, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
3421 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4254, 111, 2731, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122); 3275 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4254, 111, 2731, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
3422 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4255, 111, 1196, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);  
3423 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4256, 111, 2732, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122); 3276 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4256, 111, 2732, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
3424 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4257, 111, 1197, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122); 3277 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4257, 111, 1197, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
3425 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4258, 111, 2733, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122); 3278 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4258, 111, 2733, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
@@ -3445,8 +3298,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t @@ -3445,8 +3298,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
3445 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4278, 111, 1220, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122); 3298 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4278, 111, 1220, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
3446 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4279, 111, 1221, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122); 3299 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4279, 111, 1221, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
3447 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4280, 111, 1222, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122); 3300 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4280, 111, 1222, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
3448 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5777, 101, 2739, '1', '2024-04-30 09:38:37', '1', '2024-04-30 09:38:37', b'0', 1);  
3449 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5778, 101, 2740, '1', '2024-04-30 09:38:37', '1', '2024-04-30 09:38:37', b'0', 1);  
3450 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5779, 2, 2739, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1); 3301 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5779, 2, 2739, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
3451 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5780, 2, 2740, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1); 3302 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5780, 2, 2740, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
3452 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5781, 2, 2758, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1); 3303 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5781, 2, 2758, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
@@ -3454,9 +3305,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t @@ -3454,9 +3305,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
3454 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5783, 2, 2362, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1); 3305 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5783, 2, 2362, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
3455 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5784, 2, 2387, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1); 3306 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5784, 2, 2387, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
3456 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5785, 2, 2030, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1); 3307 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5785, 2, 2030, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
3457 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5786, 101, 2758, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', b'0', 1);  
3458 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5787, 101, 2759, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', b'0', 1);  
3459 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5788, 101, 2783, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', b'0', 1);  
3460 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5789, 109, 2739, '1', '2024-07-13 22:37:24', '1', '2024-07-13 22:37:24', b'0', 121); 3308 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5789, 109, 2739, '1', '2024-07-13 22:37:24', '1', '2024-07-13 22:37:24', b'0', 121);
3461 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5790, 109, 2740, '1', '2024-07-13 22:37:24', '1', '2024-07-13 22:37:24', b'0', 121); 3309 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5790, 109, 2740, '1', '2024-07-13 22:37:24', '1', '2024-07-13 22:37:24', b'0', 121);
3462 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5791, 111, 2739, '1', '2024-07-13 22:37:24', '1', '2024-07-13 22:37:24', b'0', 122); 3310 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5791, 111, 2739, '1', '2024-07-13 22:37:24', '1', '2024-07-13 22:37:24', b'0', 122);
@@ -3477,7 +3325,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t @@ -3477,7 +3325,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
3477 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6115, 155, 4043, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1); 3325 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6115, 155, 4043, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);
3478 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6116, 155, 4044, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1); 3326 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6116, 155, 4044, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);
3479 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6117, 155, 4045, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1); 3327 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6117, 155, 4045, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);
3480 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6118, 155, 4046, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);  
3481 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6119, 155, 4001, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1); 3328 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6119, 155, 4001, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);
3482 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6120, 155, 4002, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1); 3329 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6120, 155, 4002, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);
3483 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6121, 155, 4003, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1); 3330 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6121, 155, 4003, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);
@@ -3497,7 +3344,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t @@ -3497,7 +3344,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
3497 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6135, 155, 4017, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1); 3344 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6135, 155, 4017, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);
3498 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6136, 155, 4018, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1); 3345 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6136, 155, 4018, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);
3499 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6137, 155, 4031, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1); 3346 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6137, 155, 4031, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);
3500 -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6138, 101, 5010, '1', '2025-05-05 17:49:17', '1', '2025-05-05 17:49:17', b'0', 1);  
3501 COMMIT; 3347 COMMIT;
3502 3348
3503 -- ---------------------------- 3349 -- ----------------------------
@@ -3552,7 +3398,7 @@ CREATE TABLE `system_sms_code` ( @@ -3552,7 +3398,7 @@ CREATE TABLE `system_sms_code` (
3552 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', 3398 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
3553 PRIMARY KEY (`id`) USING BTREE, 3399 PRIMARY KEY (`id`) USING BTREE,
3554 INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号' 3400 INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'
3555 -) ENGINE = InnoDB AUTO_INCREMENT = 666 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码'; 3401 +) ENGINE = InnoDB AUTO_INCREMENT = 681 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
3556 3402
3557 -- ---------------------------- 3403 -- ----------------------------
3558 -- Records of system_sms_code 3404 -- Records of system_sms_code
@@ -3593,7 +3439,7 @@ CREATE TABLE `system_sms_log` ( @@ -3593,7 +3439,7 @@ CREATE TABLE `system_sms_log` (
3593 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 3439 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
3594 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 3440 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
3595 PRIMARY KEY (`id`) USING BTREE 3441 PRIMARY KEY (`id`) USING BTREE
3596 -) ENGINE = InnoDB AUTO_INCREMENT = 1290 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志'; 3442 +) ENGINE = InnoDB AUTO_INCREMENT = 1491 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
3597 3443
3598 -- ---------------------------- 3444 -- ----------------------------
3599 -- Records of system_sms_log 3445 -- Records of system_sms_log
@@ -3726,7 +3572,7 @@ CREATE TABLE `system_social_user_bind` ( @@ -3726,7 +3572,7 @@ CREATE TABLE `system_social_user_bind` (
3726 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 3572 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
3727 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', 3573 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
3728 PRIMARY KEY (`id`) USING BTREE 3574 PRIMARY KEY (`id`) USING BTREE
3729 -) ENGINE = InnoDB AUTO_INCREMENT = 164 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交绑定表'; 3575 +) ENGINE = InnoDB AUTO_INCREMENT = 165 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交绑定表';
3730 3576
3731 -- ---------------------------- 3577 -- ----------------------------
3732 -- Records of system_social_user_bind 3578 -- Records of system_social_user_bind
@@ -3744,8 +3590,8 @@ CREATE TABLE `system_tenant` ( @@ -3744,8 +3590,8 @@ CREATE TABLE `system_tenant` (
3744 `contact_user_id` bigint NULL DEFAULT NULL COMMENT '联系人的用户编号', 3590 `contact_user_id` bigint NULL DEFAULT NULL COMMENT '联系人的用户编号',
3745 `contact_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '联系人', 3591 `contact_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '联系人',
3746 `contact_mobile` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '联系手机', 3592 `contact_mobile` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '联系手机',
3747 - `status` tinyint NOT NULL DEFAULT 0 COMMENT '租户状态(0正常 1停用)',  
3748 - `websites` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '绑定域名数组', 3593 + `status` tinyint NOT NULL DEFAULT 0 COMMENT '租户状态',
  3594 + `websites` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '绑定域名数组',
3749 `package_id` bigint NOT NULL COMMENT '租户套餐编号', 3595 `package_id` bigint NOT NULL COMMENT '租户套餐编号',
3750 `expire_time` datetime NOT NULL COMMENT '过期时间', 3596 `expire_time` datetime NOT NULL COMMENT '过期时间',
3751 `account_count` int NOT NULL COMMENT '账号数量', 3597 `account_count` int NOT NULL COMMENT '账号数量',
@@ -3761,9 +3607,9 @@ CREATE TABLE `system_tenant` ( @@ -3761,9 +3607,9 @@ CREATE TABLE `system_tenant` (
3761 -- Records of system_tenant 3607 -- Records of system_tenant
3762 -- ---------------------------- 3608 -- ----------------------------
3763 BEGIN; 3609 BEGIN;
3764 -INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `websites`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '芋道源码', NULL, '芋艿', '17321315478', 0, 'www.iocoder.cn', 0, '2099-02-19 17:14:16', 9999, '1', '2021-01-05 17:03:47', '1', '2023-11-06 11:41:41', b'0');  
3765 -INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `websites`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, '小租户', 110, '小王2', '15601691300', 0, 'zsxq.iocoder.cn', 111, '2026-07-10 00:00:00', 30, '1', '2022-02-22 00:56:14', '1', '2025-04-03 21:33:01', b'0');  
3766 -INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `websites`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'test.iocoder.cn', 111, '2022-04-29 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2024-09-22 12:10:50', b'0'); 3610 +INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `websites`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '芋道源码', NULL, '芋艿', '17321315478', 0, 'www.iocoder.cn,127.0.0.1:3000,wxc4598c446f8a9cb3', 0, '2099-02-19 17:14:16', 9999, '1', '2021-01-05 17:03:47', '1', '2025-08-19 05:18:41', b'0');
  3611 +INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `websites`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, '小租户', 110, '小王2', '15601691300', 0, 'zsxq.iocoder.cn,123321', 111, '2026-07-10 00:00:00', 30, '1', '2022-02-22 00:56:14', '1', '2025-08-19 21:19:29', b'0');
  3612 +INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `websites`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'test.iocoder.cn', 111, '2022-04-29 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2025-08-24 14:46:15', b'0');
3767 COMMIT; 3613 COMMIT;
3768 3614
3769 -- ---------------------------- 3615 -- ----------------------------
@@ -3788,8 +3634,7 @@ CREATE TABLE `system_tenant_package` ( @@ -3788,8 +3634,7 @@ CREATE TABLE `system_tenant_package` (
3788 -- Records of system_tenant_package 3634 -- Records of system_tenant_package
3789 -- ---------------------------- 3635 -- ----------------------------
3790 BEGIN; 3636 BEGIN;
3791 -INSERT INTO `system_tenant_package` (`id`, `name`, `status`, `remark`, `menu_ids`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (111, '普通套餐', 0, '小功能', '[1,2,5,1031,1032,1033,1034,1035,1036,1037,1038,1039,1050,1051,1052,1053,1054,1056,1057,1058,1059,1060,1063,1064,1065,1066,1067,1070,1075,1077,1078,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1118,1119,1120,100,101,102,103,106,107,110,111,112,113,1138,114,1139,115,1140,116,1141,1142,1143,2713,2714,2715,2716,2717,2718,2720,1185,2721,1186,2722,1187,2723,1188,2724,1189,2725,1190,2726,1191,2727,2472,1192,2728,1193,2729,1194,2730,1195,2731,1196,2732,1197,2733,2478,1198,2734,2479,1199,2735,2480,1200,2481,1201,2482,1202,2483,2739,2484,2740,2485,2486,2487,1207,2488,1208,2489,1209,2490,1210,2491,1211,2492,1212,2493,1213,2494,2495,1215,1216,2497,1217,1218,1219,1220,1221,1222,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,2525,1255,1256,1001,1257,1002,1258,1003,1259,1004,1260,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020]', '1', '2022-02-22 00:54:00', '1', '2024-07-13 22:37:24', b'0');  
3792 -INSERT INTO `system_tenant_package` (`id`, `name`, `status`, `remark`, `menu_ids`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (112, '再来一个套餐', 0, '1234', '[1024,1,1025,1026,2,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1042,1043,1045,1046,1048,1050,1051,1052,1053,1054,1056,1057,1058,2083,1059,1060,1063,1064,1065,1066,1067,1070,1075,1077,1078,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1100,1101,1102,1103,1104,1105,1106,2130,1107,2131,1108,2132,1109,2133,2134,2135,2136,2137,2138,2139,2140,2141,2142,2143,2144,2145,2146,2147,100,2148,101,2149,102,2150,103,2151,104,2152,105,106,107,108,109,110,111,112,113,1138,114,1139,115,1140,116,1141,1142,1143,2739,2740,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,1255,1256,1257,1258,1259,1260,1261,1263,1264,1265,1266,1267,2447,2448,2449,2450,2451,2452,2453,2472,2478,2479,2480,2481,2482,2483,2484,2485,2486,2487,2488,2489,2490,2491,2492,2493,2494,2495,2497,2525,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012,500,1013,501,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023]', '1', '2025-04-04 08:15:02', '1', '2025-04-04 08:15:21', b'0'); 3637 +INSERT INTO `system_tenant_package` (`id`, `name`, `status`, `remark`, `menu_ids`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (111, '普通套餐', 0, '小功能', '[1,2,5,1031,1032,1033,1034,1035,1036,1037,1038,1039,1050,1051,1052,1053,1054,1056,1057,1058,1059,1060,1063,1064,1065,1066,1067,1070,1075,1077,1078,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1118,1119,1120,100,101,102,103,106,107,110,111,112,113,114,1138,1139,115,1140,116,1141,1142,1143,2713,2714,2715,2716,2717,2718,2720,2721,1185,2722,1186,1187,2723,1188,2724,1189,2725,1190,2726,1191,2727,2472,1192,2728,2729,1193,1194,2730,1195,2731,2732,1197,2733,2478,1198,2734,2479,1199,2735,2480,1200,2481,1201,2482,1202,2739,2483,2484,2740,2485,2486,2487,1207,2488,1208,2489,1209,2490,1210,2491,1211,2492,1212,2493,1213,2494,2495,1215,1216,2497,1217,1218,1219,1220,1221,1222,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,2525,1255,1256,1001,1257,1002,1258,1003,1259,1004,1260,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020]', '1', '2022-02-22 00:54:00', '1', '2025-05-14 14:06:34', b'0');
3793 COMMIT; 3638 COMMIT;
3794 3639
3795 -- ---------------------------- 3640 -- ----------------------------
@@ -3839,7 +3684,7 @@ CREATE TABLE `system_user_role` ( @@ -3839,7 +3684,7 @@ CREATE TABLE `system_user_role` (
3839 `deleted` bit(1) NULL DEFAULT b'0' COMMENT '是否删除', 3684 `deleted` bit(1) NULL DEFAULT b'0' COMMENT '是否删除',
3840 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', 3685 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
3841 PRIMARY KEY (`id`) USING BTREE 3686 PRIMARY KEY (`id`) USING BTREE
3842 -) ENGINE = InnoDB AUTO_INCREMENT = 49 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户和角色关联表'; 3687 +) ENGINE = InnoDB AUTO_INCREMENT = 50 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户和角色关联表';
3843 3688
3844 -- ---------------------------- 3689 -- ----------------------------
3845 -- Records of system_user_role 3690 -- Records of system_user_role
@@ -3847,7 +3692,6 @@ CREATE TABLE `system_user_role` ( @@ -3847,7 +3692,6 @@ CREATE TABLE `system_user_role` (
3847 BEGIN; 3692 BEGIN;
3848 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 1, 1, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:17', b'0', 1); 3693 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 1, 1, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:17', b'0', 1);
3849 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, 2, 2, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:13', b'0', 1); 3694 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, 2, 2, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:13', b'0', 1);
3850 -INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, 100, 101, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:13', b'0', 1);  
3851 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5, 100, 1, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:12', b'0', 1); 3695 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5, 100, 1, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:12', b'0', 1);
3852 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6, 100, 2, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:11', b'0', 1); 3696 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6, 100, 2, '', '2022-01-11 13:19:45', '', '2022-05-12 12:35:11', b'0', 1);
3853 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (10, 103, 1, '1', '2022-01-11 13:19:45', '1', '2022-01-11 13:19:45', b'0', 1); 3697 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (10, 103, 1, '1', '2022-01-11 13:19:45', '1', '2022-01-11 13:19:45', b'0', 1);
@@ -3858,10 +3702,10 @@ INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_t @@ -3858,10 +3702,10 @@ INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_t
3858 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (22, 115, 2, '1', '2022-07-21 22:08:30', '1', '2022-07-21 22:08:30', b'0', 1); 3702 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (22, 115, 2, '1', '2022-07-21 22:08:30', '1', '2022-07-21 22:08:30', b'0', 1);
3859 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (35, 112, 1, '1', '2024-03-15 20:00:24', '1', '2024-03-15 20:00:24', b'0', 1); 3703 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (35, 112, 1, '1', '2024-03-15 20:00:24', '1', '2024-03-15 20:00:24', b'0', 1);
3860 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (36, 118, 1, '1', '2024-03-17 09:12:08', '1', '2024-03-17 09:12:08', b'0', 1); 3704 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (36, 118, 1, '1', '2024-03-17 09:12:08', '1', '2024-03-17 09:12:08', b'0', 1);
3861 -INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (38, 114, 101, '1', '2024-03-24 22:23:03', '1', '2024-03-24 22:23:03', b'0', 1);  
3862 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (46, 117, 1, '1', '2024-10-02 10:16:11', '1', '2024-10-02 10:16:11', b'0', 1); 3705 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (46, 117, 1, '1', '2024-10-02 10:16:11', '1', '2024-10-02 10:16:11', b'0', 1);
3863 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (47, 104, 2, '1', '2025-01-04 10:40:33', '1', '2025-01-04 10:40:33', b'0', 1); 3706 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (47, 104, 2, '1', '2025-01-04 10:40:33', '1', '2025-01-04 10:40:33', b'0', 1);
3864 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (48, 100, 155, '1', '2025-04-04 10:41:14', '1', '2025-04-04 10:41:14', b'0', 1); 3707 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (48, 100, 155, '1', '2025-04-04 10:41:14', '1', '2025-04-04 10:41:14', b'0', 1);
  3708 +INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (49, 142, 1, '1', '2025-07-23 09:11:42', '1', '2025-07-23 09:11:42', b'0', 1);
3865 COMMIT; 3709 COMMIT;
3866 3710
3867 -- ---------------------------- 3711 -- ----------------------------
@@ -3890,15 +3734,15 @@ CREATE TABLE `system_users` ( @@ -3890,15 +3734,15 @@ CREATE TABLE `system_users` (
3890 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 3734 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
3891 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', 3735 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
3892 PRIMARY KEY (`id`) USING BTREE 3736 PRIMARY KEY (`id`) USING BTREE
3893 -) ENGINE = InnoDB AUTO_INCREMENT = 142 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户信息表'; 3737 +) ENGINE = InnoDB AUTO_INCREMENT = 143 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户信息表';
3894 3738
3895 -- ---------------------------- 3739 -- ----------------------------
3896 -- Records of system_users 3740 -- Records of system_users
3897 -- ---------------------------- 3741 -- ----------------------------
3898 BEGIN; 3742 BEGIN;
3899 -INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$04$KljJDa/LK7QfDm0lF5OhuePhlPfjRH3tB2Wu351Uidz.oQGJXevPi', '芋道源码', '管理员', 103, '[1,2]', '11aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/test/20250502/avatar_1746154660449.png', 0, '0:0:0:0:0:0:0:1', '2025-05-10 18:03:15', 'admin', '2021-01-05 17:03:47', NULL, '2025-05-10 18:03:15', b'0', 1); 3743 +INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$04$KljJDa/LK7QfDm0lF5OhuePhlPfjRH3tB2Wu351Uidz.oQGJXevPi', '芋道源码', '管理员', 103, '[1,2]', '11aoteman@126.com', '18818260272', 2, 'http://test.yudao.iocoder.cn/user/avatar/20250709/blob_1752042302026.jpg', 0, '0:0:0:0:0:0:0:1', '2025-08-30 19:57:52', 'admin', '2021-01-05 17:03:47', NULL, '2025-08-30 19:57:52', b'0', 1);
3900 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$04$h.aaPKgO.odHepnk5PCsWeEwKdojFWdTItxGKfx1r0e1CSeBzsTJ6', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-04-08 09:36:40', '', '2021-01-07 09:07:17', NULL, '2025-04-21 14:23:08', b'0', 1); 3744 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$04$h.aaPKgO.odHepnk5PCsWeEwKdojFWdTItxGKfx1r0e1CSeBzsTJ6', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-04-08 09:36:40', '', '2021-01-07 09:07:17', NULL, '2025-04-21 14:23:08', b'0', 1);
3901 -INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$04$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2025-04-21 14:23:08', b'0', 1); 3745 +INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$04$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', '1', '2025-07-09 23:41:58', b'0', 1);
3902 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$BrwaYn303hjA/6TnXqdGoOLhyHOAA0bVrAFu6.1dJKycqKUnIoRz2', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-03-28 20:01:16', '', '2021-01-21 02:13:53', NULL, '2025-04-21 14:23:08', b'0', 1); 3746 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$BrwaYn303hjA/6TnXqdGoOLhyHOAA0bVrAFu6.1dJKycqKUnIoRz2', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-03-28 20:01:16', '', '2021-01-21 02:13:53', NULL, '2025-04-21 14:23:08', b'0', 1);
3903 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (107, 'admin107', '$2a$10$dYOOBKMO93v/.ReCqzyFg.o67Tqk.bbc2bhrpyBGkIw9aypCtr2pm', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '', NULL, '1', '2022-02-20 22:59:33', '1', '2025-04-21 14:23:08', b'0', 118); 3747 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (107, 'admin107', '$2a$10$dYOOBKMO93v/.ReCqzyFg.o67Tqk.bbc2bhrpyBGkIw9aypCtr2pm', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '', NULL, '1', '2022-02-20 22:59:33', '1', '2025-04-21 14:23:08', b'0', 118);
3904 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (108, 'admin108', '$2a$10$y6mfvKoNYL1GXWak8nYwVOH.kCWqjactkzdoIDgiKl93WN3Ejg.Lu', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '', NULL, '1', '2022-02-20 23:00:50', '1', '2025-04-21 14:23:08', b'0', 119); 3748 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (108, 'admin108', '$2a$10$y6mfvKoNYL1GXWak8nYwVOH.kCWqjactkzdoIDgiKl93WN3Ejg.Lu', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '', NULL, '1', '2022-02-20 23:00:50', '1', '2025-04-21 14:23:08', b'0', 119);
@@ -3909,11 +3753,11 @@ INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, @@ -3909,11 +3753,11 @@ INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`,
3909 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (113, 'aoteman', '$2a$10$0acJOIk2D25/oC87nyclE..0lzeu9DtQ/n3geP4fkun/zIVRhHJIO', '芋道1', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '127.0.0.1', '2022-03-19 18:38:51', '1', '2022-03-07 21:37:58', '1', '2025-05-05 15:30:53', b'0', 122); 3753 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (113, 'aoteman', '$2a$10$0acJOIk2D25/oC87nyclE..0lzeu9DtQ/n3geP4fkun/zIVRhHJIO', '芋道1', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '127.0.0.1', '2022-03-19 18:38:51', '1', '2022-03-07 21:37:58', '1', '2025-05-05 15:30:53', b'0', 122);
3910 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (114, 'hrmgr', '$2a$10$TR4eybBioGRhBmDBWkqWLO6NIh3mzYa8KBKDDB5woiGYFVlRAi.fu', 'hr 小姐姐', NULL, NULL, '[5]', '', '15601691236', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-03-24 22:21:05', '1', '2022-03-19 21:50:58', NULL, '2025-04-21 14:23:08', b'0', 1); 3754 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (114, 'hrmgr', '$2a$10$TR4eybBioGRhBmDBWkqWLO6NIh3mzYa8KBKDDB5woiGYFVlRAi.fu', 'hr 小姐姐', NULL, NULL, '[5]', '', '15601691236', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-03-24 22:21:05', '1', '2022-03-19 21:50:58', NULL, '2025-04-21 14:23:08', b'0', 1);
3911 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (115, 'aotemane', '$2a$04$GcyP0Vyzb2F2Yni5PuIK9ueGxM0tkZGMtDwVRwrNbtMvorzbpNsV2', '阿呆', '11222', 102, '[1,2]', '7648@qq.com', '15601691229', 2, NULL, 0, '', NULL, '1', '2022-04-30 02:55:43', '1', '2025-04-21 14:23:08', b'0', 1); 3755 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (115, 'aotemane', '$2a$04$GcyP0Vyzb2F2Yni5PuIK9ueGxM0tkZGMtDwVRwrNbtMvorzbpNsV2', '阿呆', '11222', 102, '[1,2]', '7648@qq.com', '15601691229', 2, NULL, 0, '', NULL, '1', '2022-04-30 02:55:43', '1', '2025-04-21 14:23:08', b'0', 1);
3912 -INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (117, 'admin123', '$2a$04$sEtimsHu9YCkYY4/oqElHem2Ijc9ld20eYO6lN.g/21NfLUTDLB9W', '测试号02', '1111', 100, '[2]', '', '15601691234', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-10-02 10:16:20', '1', '2022-07-09 17:40:26', NULL, '2025-04-21 14:23:08', b'0', 1);  
3913 -INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (118, 'goudan', '$2a$04$jth0yOj8cSJq84D6vrzusOHDwW/LpBfgBnQ6bfFlD8zNZfM632Ta2', '狗蛋', NULL, 103, '[1]', '', '15601691239', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-03-17 09:10:27', '1', '2022-07-09 17:44:43', '1', '2025-04-21 14:23:08', b'0', 1);  
3914 -INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (131, 'hh', '$2a$04$jyH9h6.gaw8mpOjPfHIpx.8as2Rzfcmdlj5rlJFwgCw4rsv/MTb2K', '呵呵', NULL, 100, '[]', '777@qq.com', '15601882312', 1, NULL, 0, '', NULL, '1', '2024-04-27 08:45:56', '1', '2025-04-21 14:23:08', b'0', 1); 3756 +INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (117, 'admin123', '$2a$04$sEtimsHu9YCkYY4/oqElHem2Ijc9ld20eYO6lN.g/21NfLUTDLB9W', '测试号02', '1111', 100, '[2]', '', '15601691234', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-10-02 10:16:20', '1', '2022-07-09 17:40:26', '1', '2025-05-14 09:56:04', b'0', 1);
  3757 +INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (118, 'goudan', '$2a$04$jth0yOj8cSJq84D6vrzusOHDwW/LpBfgBnQ6bfFlD8zNZfM632Ta2', '狗蛋', NULL, 103, '[1]', '', '15601691239', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-03-17 09:10:27', '1', '2022-07-09 17:44:43', '1', '2025-07-09 23:52:36', b'0', 1);
3915 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (139, 'wwbwwb', '$2a$04$aOHoFbQU6zfBk/1Z9raF/ugTdhjNdx7culC1HhO0zvoczAnahCiMq', '小秃头', NULL, NULL, NULL, '', '', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-09-10 21:03:58', NULL, '2024-09-10 21:03:58', NULL, '2025-04-21 14:23:08', b'0', 1); 3758 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (139, 'wwbwwb', '$2a$04$aOHoFbQU6zfBk/1Z9raF/ugTdhjNdx7culC1HhO0zvoczAnahCiMq', '小秃头', NULL, NULL, NULL, '', '', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-09-10 21:03:58', NULL, '2024-09-10 21:03:58', NULL, '2025-04-21 14:23:08', b'0', 1);
3916 -INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (141, 'admin1', '$2a$04$oj6F6d7HrZ70kYVD3TNzEu.m3TPUzajOVuC66zdKna8KRerK1FmVa', '新用户', NULL, NULL, NULL, '', '', 0, '', 0, '0:0:0:0:0:0:0:1', '2025-04-08 13:09:07', '1', '2025-04-08 13:09:07', '1', '2025-04-08 13:09:07', b'0', 1); 3759 +INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (141, 'admin1', '$2a$04$oj6F6d7HrZ70kYVD3TNzEu.m3TPUzajOVuC66zdKna8KRerK1FmVa', '新用户', NULL, NULL, NULL, '', '', 0, '', 0, '0:0:0:0:0:0:0:1', '2025-04-08 13:09:07', '1', '2025-04-08 13:09:07', '1', '2025-05-14 19:11:48', b'0', 1);
  3760 +INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (142, 'test01', '$2a$04$vxfBq9d3vVIfUbo6Dw7Mt.KOJfvG7RJ.l2/B3Kw09NtNyAZ1vbB1m', 'test01', '', NULL, '[]', '', '', 1, '', 0, '0:0:0:0:0:0:0:1', '2025-07-29 19:47:17', '1', '2025-07-09 21:07:10', NULL, '2025-07-29 19:47:17', b'0', 1);
3917 COMMIT; 3761 COMMIT;
3918 3762
3919 -- ---------------------------- 3763 -- ----------------------------
yudao-dependencies/pom.xml
@@ -14,57 +14,58 @@ @@ -14,57 +14,58 @@
14 <url>https://github.com/YunaiV/ruoyi-vue-pro</url> 14 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
15 15
16 <properties> 16 <properties>
17 - <revision>2025.08-SNAPSHOT</revision>  
18 - <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version> 17 + <revision>2025.09-SNAPSHOT</revision>
  18 + <flatten-maven-plugin.version>1.7.2</flatten-maven-plugin.version>
19 <!-- 统一依赖管理 --> 19 <!-- 统一依赖管理 -->
20 - <spring.boot.version>3.4.5</spring.boot.version> 20 + <spring.boot.version>3.5.5</spring.boot.version>
21 <!-- Web 相关 --> 21 <!-- Web 相关 -->
22 - <springdoc.version>2.8.3</springdoc.version>  
23 - <knife4j.version>4.6.0</knife4j.version> 22 + <springdoc.version>2.8.11</springdoc.version>
  23 + <knife4j.version>4.5.0</knife4j.version>
24 <!-- DB 相关 --> 24 <!-- DB 相关 -->
25 - <druid.version>1.2.24</druid.version> 25 + <druid.version>1.2.27</druid.version>
26 <mybatis.version>3.5.19</mybatis.version> 26 <mybatis.version>3.5.19</mybatis.version>
27 - <mybatis-plus.version>3.5.12</mybatis-plus.version> 27 + <mybatis-plus.version>3.5.14</mybatis-plus.version>
28 <mybatis-plus-join.version>1.5.4</mybatis-plus-join.version> 28 <mybatis-plus-join.version>1.5.4</mybatis-plus-join.version>
29 <dynamic-datasource.version>4.3.1</dynamic-datasource.version> 29 <dynamic-datasource.version>4.3.1</dynamic-datasource.version>
30 <easy-trans.version>3.0.6</easy-trans.version> 30 <easy-trans.version>3.0.6</easy-trans.version>
31 - <redisson.version>3.41.0</redisson.version> 31 + <redisson.version>3.51.0</redisson.version>
32 <dm8.jdbc.version>8.1.3.140</dm8.jdbc.version> 32 <dm8.jdbc.version>8.1.3.140</dm8.jdbc.version>
33 <kingbase.jdbc.version>8.6.0</kingbase.jdbc.version> 33 <kingbase.jdbc.version>8.6.0</kingbase.jdbc.version>
34 <opengauss.jdbc.version>5.1.0</opengauss.jdbc.version> 34 <opengauss.jdbc.version>5.1.0</opengauss.jdbc.version>
35 - <taos.version>3.3.3</taos.version> 35 + <taos.version>3.7.3</taos.version>
36 <!-- 消息队列 --> 36 <!-- 消息队列 -->
37 - <rocketmq-spring.version>2.3.2</rocketmq-spring.version> 37 + <rocketmq-spring.version>2.3.4</rocketmq-spring.version>
38 <!-- 服务保障相关 --> 38 <!-- 服务保障相关 -->
39 <lock4j.version>2.2.7</lock4j.version> 39 <lock4j.version>2.2.7</lock4j.version>
40 <!-- 监控相关 --> 40 <!-- 监控相关 -->
41 - <skywalking.version>9.0.0</skywalking.version>  
42 - <spring-boot-admin.version>3.4.5</spring-boot-admin.version> 41 + <skywalking.version>9.5.0</skywalking.version>
  42 + <spring-boot-admin.version>3.5.2</spring-boot-admin.version>
43 <opentracing.version>0.33.0</opentracing.version> 43 <opentracing.version>0.33.0</opentracing.version>
44 <!-- Test 测试相关 --> 44 <!-- Test 测试相关 -->
45 <podam.version>8.0.2.RELEASE</podam.version> 45 <podam.version>8.0.2.RELEASE</podam.version>
46 - <jedis-mock.version>1.1.8</jedis-mock.version> 46 + <jedis-mock.version>1.1.11</jedis-mock.version>
47 <mockito-inline.version>5.2.0</mockito-inline.version> 47 <mockito-inline.version>5.2.0</mockito-inline.version>
48 <!-- Bpm 工作流相关 --> 48 <!-- Bpm 工作流相关 -->
49 <flowable.version>7.0.1</flowable.version> 49 <flowable.version>7.0.1</flowable.version>
50 <!-- 工具类相关 --> 50 <!-- 工具类相关 -->
51 <anji-plus-captcha.version>1.4.0</anji-plus-captcha.version> 51 <anji-plus-captcha.version>1.4.0</anji-plus-captcha.version>
52 - <jsoup.version>1.18.3</jsoup.version> 52 + <jsoup.version>1.21.2</jsoup.version>
53 <lombok.version>1.18.38</lombok.version> 53 <lombok.version>1.18.38</lombok.version>
54 <mapstruct.version>1.6.3</mapstruct.version> 54 <mapstruct.version>1.6.3</mapstruct.version>
55 - <hutool-5.version>5.8.35</hutool-5.version>  
56 - <hutool-6.version>6.0.0-M19</hutool-6.version>  
57 - <fastexcel.version>1.2.0</fastexcel.version> 55 + <hutool-5.version>5.8.40</hutool-5.version>
  56 + <hutool-6.version>6.0.0-M22</hutool-6.version>
  57 + <fastexcel.version>1.3.0</fastexcel.version>
58 <velocity.version>2.4.1</velocity.version> 58 <velocity.version>2.4.1</velocity.version>
59 <fastjson.version>1.2.83</fastjson.version> 59 <fastjson.version>1.2.83</fastjson.version>
60 <guava.version>33.4.8-jre</guava.version> 60 <guava.version>33.4.8-jre</guava.version>
61 <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version> 61 <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
62 <commons-net.version>3.11.1</commons-net.version> 62 <commons-net.version>3.11.1</commons-net.version>
63 - <jsch.version>0.1.55</jsch.version>  
64 - <tika-core.version>3.1.0</tika-core.version> 63 + <commons-lang3.version>3.18.0</commons-lang3.version>
  64 + <jsch.version>2.27.3</jsch.version>
  65 + <tika-core.version>3.2.2</tika-core.version>
65 <ip2region.version>2.7.0</ip2region.version> 66 <ip2region.version>2.7.0</ip2region.version>
66 <bizlog-sdk.version>3.0.6</bizlog-sdk.version> 67 <bizlog-sdk.version>3.0.6</bizlog-sdk.version>
67 - <netty.version>4.1.118.Final</netty.version> 68 + <netty.version>4.2.4.Final</netty.version>
68 <mqtt.version>1.2.5</mqtt.version> 69 <mqtt.version>1.2.5</mqtt.version>
69 <pf4j-spring.version>0.9.0</pf4j-spring.version> 70 <pf4j-spring.version>0.9.0</pf4j-spring.version>
70 <vertx.version>4.5.13</vertx.version> 71 <vertx.version>4.5.13</vertx.version>
@@ -72,9 +73,9 @@ @@ -72,9 +73,9 @@
72 <awssdk.version>2.30.14</awssdk.version> 73 <awssdk.version>2.30.14</awssdk.version>
73 <justauth.version>1.16.7</justauth.version> 74 <justauth.version>1.16.7</justauth.version>
74 <justauth-starter.version>1.4.0</justauth-starter.version> 75 <justauth-starter.version>1.4.0</justauth-starter.version>
75 - <jimureport.version>2.0.0</jimureport.version>  
76 - <jimubi.version>1.9.5</jimubi.version>  
77 - <weixin-java.version>4.7.5.B</weixin-java.version> 76 + <jimureport.version>2.1.1</jimureport.version>
  77 + <jimubi.version>2.1.0</jimubi.version>
  78 + <weixin-java.version>4.7.7-20250808.182223</weixin-java.version>
78 </properties> 79 </properties>
79 80
80 <dependencyManagement> 81 <dependencyManagement>
@@ -151,13 +152,19 @@ @@ -151,13 +152,19 @@
151 </dependency> 152 </dependency>
152 153
153 <dependency> 154 <dependency>
154 - <groupId>com.github.xingfudeshi</groupId> <!-- TODO 芋艿:https://github.com/xiaoymin/knife4j/issues/874 --> 155 + <groupId>com.github.xiaoymin</groupId>
155 <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> 156 <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
156 <version>${knife4j.version}</version> 157 <version>${knife4j.version}</version>
  158 + <exclusions>
  159 + <exclusion>
  160 + <groupId>org.springdoc</groupId>
  161 + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
  162 + </exclusion>
  163 + </exclusions>
157 </dependency> 164 </dependency>
158 <dependency> 165 <dependency>
159 <groupId>org.springdoc</groupId> 166 <groupId>org.springdoc</groupId>
160 - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> 167 + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
161 <version>${springdoc.version}</version> 168 <version>${springdoc.version}</version>
162 </dependency> 169 </dependency>
163 170
@@ -518,14 +525,19 @@ @@ -518,14 +525,19 @@
518 <artifactId>commons-net</artifactId> <!-- 解决 ftp 连接 --> 525 <artifactId>commons-net</artifactId> <!-- 解决 ftp 连接 -->
519 <version>${commons-net.version}</version> 526 <version>${commons-net.version}</version>
520 </dependency> 527 </dependency>
521 -  
522 <dependency> 528 <dependency>
523 - <groupId>com.jcraft</groupId> 529 + <groupId>com.github.mwiede</groupId>
524 <artifactId>jsch</artifactId> <!-- 解决 sftp 连接 --> 530 <artifactId>jsch</artifactId> <!-- 解决 sftp 连接 -->
525 <version>${jsch.version}</version> 531 <version>${jsch.version}</version>
526 </dependency> 532 </dependency>
527 533
528 <dependency> 534 <dependency>
  535 + <groupId>org.apache.commons</groupId>
  536 + <artifactId>commons-lang3</artifactId>
  537 + <version>${commons-lang3.version}</version> <!-- 解决 CVE-2025-48924 漏洞 -->
  538 + </dependency>
  539 +
  540 + <dependency>
529 <groupId>com.anji-plus</groupId> 541 <groupId>com.anji-plus</groupId>
530 <artifactId>captcha-spring-boot-starter</artifactId> <!-- 验证码,一般用于登录使用 --> 542 <artifactId>captcha-spring-boot-starter</artifactId> <!-- 验证码,一般用于登录使用 -->
531 <version>${anji-plus-captcha.version}</version> 543 <version>${anji-plus-captcha.version}</version>
@@ -599,6 +611,10 @@ @@ -599,6 +611,10 @@
599 <groupId>com.github.jsqlparser</groupId> 611 <groupId>com.github.jsqlparser</groupId>
600 <artifactId>jsqlparser</artifactId> 612 <artifactId>jsqlparser</artifactId>
601 </exclusion> 613 </exclusion>
  614 + <exclusion>
  615 + <groupId>cn.hutool</groupId>
  616 + <artifactId>hutool-core</artifactId>
  617 + </exclusion>
602 </exclusions> 618 </exclusions>
603 </dependency> 619 </dependency>
604 620
yudao-framework/yudao-common/pom.xml
@@ -60,7 +60,7 @@ @@ -60,7 +60,7 @@
60 60
61 <dependency> 61 <dependency>
62 <groupId>org.springdoc</groupId> 62 <groupId>org.springdoc</groupId>
63 - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> 63 + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
64 <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 --> 64 <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 -->
65 </dependency> 65 </dependency>
66 66
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenRespDTO.java
1 package cn.iocoder.yudao.framework.common.biz.system.oauth2.dto; 1 package cn.iocoder.yudao.framework.common.biz.system.oauth2.dto;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 -import lombok.experimental.Accessors;  
5 4
6 import java.io.Serializable; 5 import java.io.Serializable;
7 import java.time.LocalDateTime; 6 import java.time.LocalDateTime;
@@ -12,7 +11,6 @@ import java.time.LocalDateTime; @@ -12,7 +11,6 @@ import java.time.LocalDateTime;
12 * @author 芋道源码 11 * @author 芋道源码
13 */ 12 */
14 @Data 13 @Data
15 -@Accessors(chain = true)  
16 public class OAuth2AccessTokenRespDTO implements Serializable { 14 public class OAuth2AccessTokenRespDTO implements Serializable {
17 15
18 /** 16 /**
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java
@@ -16,6 +16,7 @@ import java.util.Arrays; @@ -16,6 +16,7 @@ import java.util.Arrays;
16 @AllArgsConstructor 16 @AllArgsConstructor
17 public enum DateIntervalEnum implements ArrayValuable<Integer> { 17 public enum DateIntervalEnum implements ArrayValuable<Integer> {
18 18
  19 + HOUR(0, "小时"), // 特殊:字典里,暂时不会有这个枚举!!!因为大多数情况下,用不到这个间隔
19 DAY(1, "天"), 20 DAY(1, "天"),
20 WEEK(2, "周"), 21 WEEK(2, "周"),
21 MONTH(3, "月"), 22 MONTH(3, "月"),
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
@@ -8,6 +8,7 @@ import cn.hutool.core.lang.Assert; @@ -8,6 +8,7 @@ import cn.hutool.core.lang.Assert;
8 import cn.hutool.core.util.StrUtil; 8 import cn.hutool.core.util.StrUtil;
9 import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; 9 import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
10 10
  11 +import java.sql.Timestamp;
11 import java.time.*; 12 import java.time.*;
12 import java.time.format.DateTimeFormatter; 13 import java.time.format.DateTimeFormatter;
13 import java.time.format.DateTimeParseException; 14 import java.time.format.DateTimeParseException;
@@ -16,8 +17,7 @@ import java.time.temporal.TemporalAdjusters; @@ -16,8 +17,7 @@ import java.time.temporal.TemporalAdjusters;
16 import java.util.ArrayList; 17 import java.util.ArrayList;
17 import java.util.List; 18 import java.util.List;
18 19
19 -import static cn.hutool.core.date.DatePattern.UTC_MS_WITH_XXX_OFFSET_PATTERN;  
20 -import static cn.hutool.core.date.DatePattern.createFormatter; 20 +import static cn.hutool.core.date.DatePattern.*;
21 21
22 /** 22 /**
23 * 时间工具类,用于 {@link LocalDateTime} 23 * 时间工具类,用于 {@link LocalDateTime}
@@ -90,6 +90,21 @@ public class LocalDateTimeUtils { @@ -90,6 +90,21 @@ public class LocalDateTimeUtils {
90 * @param time 指定时间 90 * @param time 指定时间
91 * @return 是否 91 * @return 是否
92 */ 92 */
  93 + public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, Timestamp time) {
  94 + if (startTime == null || endTime == null || time == null) {
  95 + return false;
  96 + }
  97 + return LocalDateTimeUtil.isIn(LocalDateTimeUtil.of(time), startTime, endTime);
  98 + }
  99 +
  100 + /**
  101 + * 判指定断时间,是否在该时间范围内
  102 + *
  103 + * @param startTime 开始时间
  104 + * @param endTime 结束时间
  105 + * @param time 指定时间
  106 + * @return 是否
  107 + */
93 public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) { 108 public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) {
94 if (startTime == null || endTime == null || time == null) { 109 if (startTime == null || endTime == null || time == null) {
95 return false; 110 return false;
@@ -234,6 +249,11 @@ public class LocalDateTimeUtils { @@ -234,6 +249,11 @@ public class LocalDateTimeUtils {
234 // 2. 循环,生成时间范围 249 // 2. 循环,生成时间范围
235 List<LocalDateTime[]> timeRanges = new ArrayList<>(); 250 List<LocalDateTime[]> timeRanges = new ArrayList<>();
236 switch (intervalEnum) { 251 switch (intervalEnum) {
  252 + case HOUR:
  253 + while (startTime.isBefore(endTime)) {
  254 + timeRanges.add(new LocalDateTime[]{startTime, startTime.plusHours(1).minusNanos(1)});
  255 + startTime = startTime.plusHours(1);
  256 + }
237 case DAY: 257 case DAY:
238 while (startTime.isBefore(endTime)) { 258 while (startTime.isBefore(endTime)) {
239 timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)}); 259 timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)});
@@ -297,6 +317,8 @@ public class LocalDateTimeUtils { @@ -297,6 +317,8 @@ public class LocalDateTimeUtils {
297 317
298 // 2. 循环,生成时间范围 318 // 2. 循环,生成时间范围
299 switch (intervalEnum) { 319 switch (intervalEnum) {
  320 + case HOUR:
  321 + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATETIME_MINUTE_PATTERN);
300 case DAY: 322 case DAY:
301 return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN); 323 return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN);
302 case WEEK: 324 case WEEK:
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java
@@ -3,18 +3,23 @@ package cn.iocoder.yudao.framework.common.util.json; @@ -3,18 +3,23 @@ package cn.iocoder.yudao.framework.common.util.json;
3 import cn.hutool.core.util.ArrayUtil; 3 import cn.hutool.core.util.ArrayUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
5 import cn.hutool.json.JSONUtil; 5 import cn.hutool.json.JSONUtil;
  6 +import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer;
  7 +import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;
6 import com.fasterxml.jackson.annotation.JsonInclude; 8 import com.fasterxml.jackson.annotation.JsonInclude;
7 import com.fasterxml.jackson.core.type.TypeReference; 9 import com.fasterxml.jackson.core.type.TypeReference;
8 import com.fasterxml.jackson.databind.DeserializationFeature; 10 import com.fasterxml.jackson.databind.DeserializationFeature;
9 import com.fasterxml.jackson.databind.JsonNode; 11 import com.fasterxml.jackson.databind.JsonNode;
10 import com.fasterxml.jackson.databind.ObjectMapper; 12 import com.fasterxml.jackson.databind.ObjectMapper;
11 import com.fasterxml.jackson.databind.SerializationFeature; 13 import com.fasterxml.jackson.databind.SerializationFeature;
  14 +import com.fasterxml.jackson.databind.module.SimpleModule;
12 import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 15 import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
  16 +import lombok.Getter;
13 import lombok.SneakyThrows; 17 import lombok.SneakyThrows;
14 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
15 19
16 import java.io.IOException; 20 import java.io.IOException;
17 import java.lang.reflect.Type; 21 import java.lang.reflect.Type;
  22 +import java.time.LocalDateTime;
18 import java.util.ArrayList; 23 import java.util.ArrayList;
19 import java.util.List; 24 import java.util.List;
20 25
@@ -26,13 +31,18 @@ import java.util.List; @@ -26,13 +31,18 @@ import java.util.List;
26 @Slf4j 31 @Slf4j
27 public class JsonUtils { 32 public class JsonUtils {
28 33
  34 + @Getter
29 private static ObjectMapper objectMapper = new ObjectMapper(); 35 private static ObjectMapper objectMapper = new ObjectMapper();
30 36
31 static { 37 static {
32 objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); 38 objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
33 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 39 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
34 objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 值 40 objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 值
35 - objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化 41 + // 解决 LocalDateTime 的序列化
  42 + SimpleModule simpleModule = new JavaTimeModule()
  43 + .addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE)
  44 + .addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);
  45 + objectMapper.registerModules(simpleModule);
36 } 46 }
37 47
38 /** 48 /**
@@ -99,6 +109,18 @@ public class JsonUtils { @@ -99,6 +109,18 @@ public class JsonUtils {
99 } 109 }
100 } 110 }
101 111
  112 + public static <T> T parseObject(byte[] text, Type type) {
  113 + if (ArrayUtil.isEmpty(text)) {
  114 + return null;
  115 + }
  116 + try {
  117 + return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type));
  118 + } catch (IOException e) {
  119 + log.error("json parse err,json:{}", text, e);
  120 + throw new RuntimeException(e);
  121 + }
  122 + }
  123 +
102 /** 124 /**
103 * 将字符串解析成指定类型的对象 125 * 将字符串解析成指定类型的对象
104 * 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下, 126 * 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下,
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/ObjectUtils.java
@@ -60,4 +60,8 @@ public class ObjectUtils { @@ -60,4 +60,8 @@ public class ObjectUtils {
60 return Arrays.asList(array).contains(obj); 60 return Arrays.asList(array).contains(obj);
61 } 61 }
62 62
  63 + public static boolean isNotAllEmpty(Object... objs) {
  64 + return !ObjectUtil.isAllEmpty(objs);
  65 + }
  66 +
63 } 67 }
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java
@@ -44,8 +44,7 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfo; @@ -44,8 +44,7 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
44 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 44 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
45 import org.springframework.web.util.pattern.PathPattern; 45 import org.springframework.web.util.pattern.PathPattern;
46 46
47 -import java.util.Map;  
48 -import java.util.Objects; 47 +import java.util.*;
49 48
50 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 49 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
51 50
@@ -84,41 +83,13 @@ public class YudaoTenantAutoConfiguration { @@ -84,41 +83,13 @@ public class YudaoTenantAutoConfiguration {
84 // ========== WEB ========== 83 // ========== WEB ==========
85 84
86 @Bean 85 @Bean
87 - public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter(TenantProperties tenantProperties) { 86 + public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter() {
88 FilterRegistrationBean<TenantContextWebFilter> registrationBean = new FilterRegistrationBean<>(); 87 FilterRegistrationBean<TenantContextWebFilter> registrationBean = new FilterRegistrationBean<>();
89 registrationBean.setFilter(new TenantContextWebFilter()); 88 registrationBean.setFilter(new TenantContextWebFilter());
90 registrationBean.setOrder(WebFilterOrderEnum.TENANT_CONTEXT_FILTER); 89 registrationBean.setOrder(WebFilterOrderEnum.TENANT_CONTEXT_FILTER);
91 - addIgnoreUrls(tenantProperties);  
92 return registrationBean; 90 return registrationBean;
93 } 91 }
94 92
95 - /**  
96 - * 如果 Controller 接口上,有 {@link TenantIgnore} 注解,那么添加到忽略的 URL 中  
97 - *  
98 - * @param tenantProperties 租户配置  
99 - */  
100 - private void addIgnoreUrls(TenantProperties tenantProperties) {  
101 - // 获得接口对应的 HandlerMethod 集合  
102 - RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)  
103 - applicationContext.getBean("requestMappingHandlerMapping");  
104 - Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();  
105 - // 获得有 @TenantIgnore 注解的接口  
106 - for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) {  
107 - HandlerMethod handlerMethod = entry.getValue();  
108 - if (!handlerMethod.hasMethodAnnotation(TenantIgnore.class)) {  
109 - continue;  
110 - }  
111 - // 添加到忽略的 URL 中  
112 - if (entry.getKey().getPatternsCondition() != null) {  
113 - tenantProperties.getIgnoreUrls().addAll(entry.getKey().getPatternsCondition().getPatterns());  
114 - }  
115 - if (entry.getKey().getPathPatternsCondition() != null) {  
116 - tenantProperties.getIgnoreUrls().addAll(  
117 - convertList(entry.getKey().getPathPatternsCondition().getPatterns(), PathPattern::getPatternString));  
118 - }  
119 - }  
120 - }  
121 -  
122 @Bean 93 @Bean
123 public TenantVisitContextInterceptor tenantVisitContextInterceptor(TenantProperties tenantProperties, 94 public TenantVisitContextInterceptor tenantVisitContextInterceptor(TenantProperties tenantProperties,
124 SecurityFrameworkService securityFrameworkService) { 95 SecurityFrameworkService securityFrameworkService) {
@@ -146,12 +117,42 @@ public class YudaoTenantAutoConfiguration { @@ -146,12 +117,42 @@ public class YudaoTenantAutoConfiguration {
146 GlobalExceptionHandler globalExceptionHandler, 117 GlobalExceptionHandler globalExceptionHandler,
147 TenantFrameworkService tenantFrameworkService) { 118 TenantFrameworkService tenantFrameworkService) {
148 FilterRegistrationBean<TenantSecurityWebFilter> registrationBean = new FilterRegistrationBean<>(); 119 FilterRegistrationBean<TenantSecurityWebFilter> registrationBean = new FilterRegistrationBean<>();
149 - registrationBean.setFilter(new TenantSecurityWebFilter(tenantProperties, webProperties, 120 + registrationBean.setFilter(new TenantSecurityWebFilter(webProperties, tenantProperties, getTenantIgnoreUrls(),
150 globalExceptionHandler, tenantFrameworkService)); 121 globalExceptionHandler, tenantFrameworkService));
151 registrationBean.setOrder(WebFilterOrderEnum.TENANT_SECURITY_FILTER); 122 registrationBean.setOrder(WebFilterOrderEnum.TENANT_SECURITY_FILTER);
152 return registrationBean; 123 return registrationBean;
153 } 124 }
154 125
  126 + /**
  127 + * 如果 Controller 接口上,有 {@link TenantIgnore} 注解,则添加到忽略租户的 URL 集合中
  128 + *
  129 + * @return 忽略租户的 URL 集合
  130 + */
  131 + private Set<String> getTenantIgnoreUrls() {
  132 + Set<String> ignoreUrls = new HashSet<>();
  133 + // 获得接口对应的 HandlerMethod 集合
  134 + RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)
  135 + applicationContext.getBean("requestMappingHandlerMapping");
  136 + Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
  137 + // 获得有 @TenantIgnore 注解的接口
  138 + for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) {
  139 + HandlerMethod handlerMethod = entry.getValue();
  140 + if (!handlerMethod.hasMethodAnnotation(TenantIgnore.class) // 方法级
  141 + && !handlerMethod.getBeanType().isAnnotationPresent(TenantIgnore.class)) { // 接口级
  142 + continue;
  143 + }
  144 + // 添加到忽略的 URL 中
  145 + if (entry.getKey().getPatternsCondition() != null) {
  146 + ignoreUrls.addAll(entry.getKey().getPatternsCondition().getPatterns());
  147 + }
  148 + if (entry.getKey().getPathPatternsCondition() != null) {
  149 + ignoreUrls.addAll(
  150 + convertList(entry.getKey().getPathPatternsCondition().getPatterns(), PathPattern::getPatternString));
  151 + }
  152 + }
  153 + return ignoreUrls;
  154 + }
  155 +
155 // ========== MQ ========== 156 // ========== MQ ==========
156 157
157 @Bean 158 @Bean
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java
@@ -75,7 +75,7 @@ public class TenantDatabaseInterceptor implements TenantLineHandler { @@ -75,7 +75,7 @@ public class TenantDatabaseInterceptor implements TenantLineHandler {
75 if (TenantBaseDO.class.isAssignableFrom(tableInfo.getEntityType())) { 75 if (TenantBaseDO.class.isAssignableFrom(tableInfo.getEntityType())) {
76 return false; 76 return false;
77 } 77 }
78 - // 如果添加了 @TenantIgnore 注解,显然也不忽略租户 78 + // 如果添加了 @TenantIgnore 注解,则忽略租户
79 TenantIgnore tenantIgnore = tableInfo.getEntityType().getAnnotation(TenantIgnore.class); 79 TenantIgnore tenantIgnore = tableInfo.getEntityType().getAnnotation(TenantIgnore.class);
80 return tenantIgnore != null; 80 return tenantIgnore != null;
81 } 81 }
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQInitializer.java
@@ -12,6 +12,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor; @@ -12,6 +12,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
12 public class TenantRabbitMQInitializer implements BeanPostProcessor { 12 public class TenantRabbitMQInitializer implements BeanPostProcessor {
13 13
14 @Override 14 @Override
  15 + @SuppressWarnings("PatternVariableCanBeUsed")
15 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 16 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
16 if (bean instanceof RabbitTemplate) { 17 if (bean instanceof RabbitTemplate) {
17 RabbitTemplate rabbitTemplate = (RabbitTemplate) bean; 18 RabbitTemplate rabbitTemplate = (RabbitTemplate) bean;
@@ -20,4 +21,4 @@ public class TenantRabbitMQInitializer implements BeanPostProcessor { @@ -20,4 +21,4 @@ public class TenantRabbitMQInitializer implements BeanPostProcessor {
20 return bean; 21 return bean;
21 } 22 }
22 23
23 -}  
24 \ No newline at end of file 24 \ No newline at end of file
  25 +}
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQInitializer.java
@@ -17,6 +17,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor; @@ -17,6 +17,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
17 public class TenantRocketMQInitializer implements BeanPostProcessor { 17 public class TenantRocketMQInitializer implements BeanPostProcessor {
18 18
19 @Override 19 @Override
  20 + @SuppressWarnings("PatternVariableCanBeUsed")
20 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 21 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
21 if (bean instanceof DefaultRocketMQListenerContainer) { 22 if (bean instanceof DefaultRocketMQListenerContainer) {
22 DefaultRocketMQListenerContainer container = (DefaultRocketMQListenerContainer) bean; 23 DefaultRocketMQListenerContainer container = (DefaultRocketMQListenerContainer) bean;
@@ -50,4 +51,4 @@ public class TenantRocketMQInitializer implements BeanPostProcessor { @@ -50,4 +51,4 @@ public class TenantRocketMQInitializer implements BeanPostProcessor {
50 consumerImpl.registerConsumeMessageHook(new TenantRocketMQConsumeMessageHook()); 51 consumerImpl.registerConsumeMessageHook(new TenantRocketMQConsumeMessageHook());
51 } 52 }
52 53
53 -}  
54 \ No newline at end of file 54 \ No newline at end of file
  55 +}
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security/TenantSecurityWebFilter.java
@@ -12,15 +12,16 @@ import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; @@ -12,15 +12,16 @@ import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
12 import cn.iocoder.yudao.framework.web.config.WebProperties; 12 import cn.iocoder.yudao.framework.web.config.WebProperties;
13 import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter; 13 import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter;
14 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; 14 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
15 -import lombok.extern.slf4j.Slf4j;  
16 -import org.springframework.util.AntPathMatcher;  
17 -  
18 import jakarta.servlet.FilterChain; 15 import jakarta.servlet.FilterChain;
19 import jakarta.servlet.ServletException; 16 import jakarta.servlet.ServletException;
20 import jakarta.servlet.http.HttpServletRequest; 17 import jakarta.servlet.http.HttpServletRequest;
21 import jakarta.servlet.http.HttpServletResponse; 18 import jakarta.servlet.http.HttpServletResponse;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.util.AntPathMatcher;
  21 +
22 import java.io.IOException; 22 import java.io.IOException;
23 import java.util.Objects; 23 import java.util.Objects;
  24 +import java.util.Set;
24 25
25 /** 26 /**
26 * 多租户 Security Web 过滤器 27 * 多租户 Security Web 过滤器
@@ -35,17 +36,26 @@ public class TenantSecurityWebFilter extends ApiRequestFilter { @@ -35,17 +36,26 @@ public class TenantSecurityWebFilter extends ApiRequestFilter {
35 36
36 private final TenantProperties tenantProperties; 37 private final TenantProperties tenantProperties;
37 38
  39 + /**
  40 + * 允许忽略租户的 URL 列表
  41 + *
  42 + * 目的:解决 <a href="https://gitee.com/zhijiantianya/yudao-cloud/issues/ICUQL9">修改配置会导致 @TenantIgnore Controller 接口过滤失效</>
  43 + */
  44 + private final Set<String> ignoreUrls;
  45 +
38 private final AntPathMatcher pathMatcher; 46 private final AntPathMatcher pathMatcher;
39 47
40 private final GlobalExceptionHandler globalExceptionHandler; 48 private final GlobalExceptionHandler globalExceptionHandler;
41 private final TenantFrameworkService tenantFrameworkService; 49 private final TenantFrameworkService tenantFrameworkService;
42 50
43 - public TenantSecurityWebFilter(TenantProperties tenantProperties,  
44 - WebProperties webProperties, 51 + public TenantSecurityWebFilter(WebProperties webProperties,
  52 + TenantProperties tenantProperties,
  53 + Set<String> ignoreUrls,
45 GlobalExceptionHandler globalExceptionHandler, 54 GlobalExceptionHandler globalExceptionHandler,
46 TenantFrameworkService tenantFrameworkService) { 55 TenantFrameworkService tenantFrameworkService) {
47 super(webProperties); 56 super(webProperties);
48 this.tenantProperties = tenantProperties; 57 this.tenantProperties = tenantProperties;
  58 + this.ignoreUrls = ignoreUrls;
49 this.pathMatcher = new AntPathMatcher(); 59 this.pathMatcher = new AntPathMatcher();
50 this.globalExceptionHandler = globalExceptionHandler; 60 this.globalExceptionHandler = globalExceptionHandler;
51 this.tenantFrameworkService = tenantFrameworkService; 61 this.tenantFrameworkService = tenantFrameworkService;
@@ -101,13 +111,20 @@ public class TenantSecurityWebFilter extends ApiRequestFilter { @@ -101,13 +111,20 @@ public class TenantSecurityWebFilter extends ApiRequestFilter {
101 } 111 }
102 112
103 private boolean isIgnoreUrl(HttpServletRequest request) { 113 private boolean isIgnoreUrl(HttpServletRequest request) {
  114 + String apiUri = request.getRequestURI().substring(request.getContextPath().length());
104 // 快速匹配,保证性能 115 // 快速匹配,保证性能
105 - if (CollUtil.contains(tenantProperties.getIgnoreUrls(), request.getRequestURI())) { 116 + if (CollUtil.contains(tenantProperties.getIgnoreUrls(), apiUri)
  117 + || CollUtil.contains(ignoreUrls, apiUri)) {
106 return true; 118 return true;
107 } 119 }
108 // 逐个 Ant 路径匹配 120 // 逐个 Ant 路径匹配
109 for (String url : tenantProperties.getIgnoreUrls()) { 121 for (String url : tenantProperties.getIgnoreUrls()) {
110 - if (pathMatcher.match(url, request.getRequestURI())) { 122 + if (pathMatcher.match(url, apiUri)) {
  123 + return true;
  124 + }
  125 + }
  126 + for (String url : ignoreUrls) {
  127 + if (pathMatcher.match(url, apiUri)) {
111 return true; 128 return true;
112 } 129 }
113 } 130 }
yudao-framework/yudao-spring-boot-starter-job/src/main/java/cn/iocoder/yudao/framework/quartz/config/YudaoAsyncAutoConfiguration.java
@@ -21,6 +21,7 @@ public class YudaoAsyncAutoConfiguration { @@ -21,6 +21,7 @@ public class YudaoAsyncAutoConfiguration {
21 return new BeanPostProcessor() { 21 return new BeanPostProcessor() {
22 22
23 @Override 23 @Override
  24 + @SuppressWarnings("PatternVariableCanBeUsed")
24 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 25 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
25 // 处理 ThreadPoolTaskExecutor 26 // 处理 ThreadPoolTaskExecutor
26 if (bean instanceof ThreadPoolTaskExecutor) { 27 if (bean instanceof ThreadPoolTaskExecutor) {
yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoTracerAutoConfiguration.java
1 package cn.iocoder.yudao.framework.tracer.config; 1 package cn.iocoder.yudao.framework.tracer.config;
2 2
3 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; 3 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
4 -import cn.iocoder.yudao.framework.tracer.core.aop.BizTraceAspect;  
5 import cn.iocoder.yudao.framework.tracer.core.filter.TraceFilter; 4 import cn.iocoder.yudao.framework.tracer.core.filter.TraceFilter;
6 -import io.opentracing.Tracer;  
7 -import io.opentracing.util.GlobalTracer;  
8 -import org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer;  
9 import org.springframework.boot.autoconfigure.AutoConfiguration; 5 import org.springframework.boot.autoconfigure.AutoConfiguration;
10 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
11 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 7 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -20,32 +16,28 @@ import org.springframework.context.annotation.Bean; @@ -20,32 +16,28 @@ import org.springframework.context.annotation.Bean;
20 */ 16 */
21 @AutoConfiguration 17 @AutoConfiguration
22 @ConditionalOnClass(name = { 18 @ConditionalOnClass(name = {
23 - "org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer",  
24 - "io.opentracing.Tracer", 19 + "org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer", // 来自 apm-toolkit-opentracing.jar
  20 +// "io.opentracing.Tracer", // 来自 opentracing-api.jar
25 "jakarta.servlet.Filter" 21 "jakarta.servlet.Filter"
26 }) 22 })
27 @EnableConfigurationProperties(TracerProperties.class) 23 @EnableConfigurationProperties(TracerProperties.class)
28 @ConditionalOnProperty(prefix = "yudao.tracer", value = "enable", matchIfMissing = true) 24 @ConditionalOnProperty(prefix = "yudao.tracer", value = "enable", matchIfMissing = true)
29 public class YudaoTracerAutoConfiguration { 25 public class YudaoTracerAutoConfiguration {
30 26
31 - @Bean  
32 - public TracerProperties bizTracerProperties() {  
33 - return new TracerProperties();  
34 - }  
35 -  
36 - @Bean  
37 - public BizTraceAspect bizTracingAop() {  
38 - return new BizTraceAspect(tracer());  
39 - }  
40 -  
41 - @Bean  
42 - public Tracer tracer() {  
43 - // 创建 SkywalkingTracer 对象  
44 - SkywalkingTracer tracer = new SkywalkingTracer();  
45 - // 设置为 GlobalTracer 的追踪器  
46 - GlobalTracer.registerIfAbsent(tracer);  
47 - return tracer;  
48 - } 27 + // TODO @芋艿:skywalking 不兼容最新的 opentracing 版本。同时,opentracing 也停止了维护,尬住了!后续换 opentelemetry 即可!
  28 +// @Bean
  29 +// public BizTraceAspect bizTracingAop() {
  30 +// return new BizTraceAspect(tracer());
  31 +// }
  32 +//
  33 +// @Bean
  34 +// public Tracer tracer() {
  35 +// // 创建 SkywalkingTracer 对象
  36 +// SkywalkingTracer tracer = new SkywalkingTracer();
  37 +// // 设置为 GlobalTracer 的追踪器
  38 +// GlobalTracer.registerIfAbsent(tracer);
  39 +// return tracer;
  40 +// }
49 41
50 /** 42 /**
51 * 创建 TraceFilter 过滤器,响应 header 设置 traceId 43 * 创建 TraceFilter 过滤器,响应 header 设置 traceId
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/config/YudaoRedisMQConsumerAutoConfiguration.java
@@ -69,9 +69,8 @@ public class YudaoRedisMQConsumerAutoConfiguration { @@ -69,9 +69,8 @@ public class YudaoRedisMQConsumerAutoConfiguration {
69 @ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 69 @ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听
70 public RedisPendingMessageResendJob redisPendingMessageResendJob(List<AbstractRedisStreamMessageListener<?>> listeners, 70 public RedisPendingMessageResendJob redisPendingMessageResendJob(List<AbstractRedisStreamMessageListener<?>> listeners,
71 RedisMQTemplate redisTemplate, 71 RedisMQTemplate redisTemplate,
72 - @Value("${spring.application.name}") String groupName,  
73 RedissonClient redissonClient) { 72 RedissonClient redissonClient) {
74 - return new RedisPendingMessageResendJob(listeners, redisTemplate, groupName, redissonClient); 73 + return new RedisPendingMessageResendJob(listeners, redisTemplate, redissonClient);
75 } 74 }
76 75
77 /** 76 /**
@@ -141,14 +140,14 @@ public class YudaoRedisMQConsumerAutoConfiguration { @@ -141,14 +140,14 @@ public class YudaoRedisMQConsumerAutoConfiguration {
141 * 140 *
142 * @return 消费者名字 141 * @return 消费者名字
143 */ 142 */
144 - private static String buildConsumerName() { 143 + public static String buildConsumerName() {
145 return String.format("%s@%d", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID()); 144 return String.format("%s@%d", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID());
146 } 145 }
147 146
148 /** 147 /**
149 * 校验 Redis 版本号,是否满足最低的版本号要求! 148 * 校验 Redis 版本号,是否满足最低的版本号要求!
150 */ 149 */
151 - private static void checkRedisVersion(RedisTemplate<String, ?> redisTemplate) { 150 + public static void checkRedisVersion(RedisTemplate<String, ?> redisTemplate) {
152 // 获得 Redis 版本 151 // 获得 Redis 版本
153 Properties info = redisTemplate.execute((RedisCallback<Properties>) RedisServerCommands::info); 152 Properties info = redisTemplate.execute((RedisCallback<Properties>) RedisServerCommands::info);
154 String version = MapUtil.getStr(info, "redis_version"); 153 String version = MapUtil.getStr(info, "redis_version");
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/job/RedisPendingMessageResendJob.java
@@ -35,7 +35,6 @@ public class RedisPendingMessageResendJob { @@ -35,7 +35,6 @@ public class RedisPendingMessageResendJob {
35 35
36 private final List<AbstractRedisStreamMessageListener<?>> listeners; 36 private final List<AbstractRedisStreamMessageListener<?>> listeners;
37 private final RedisMQTemplate redisTemplate; 37 private final RedisMQTemplate redisTemplate;
38 - private final String groupName;  
39 private final RedissonClient redissonClient; 38 private final RedissonClient redissonClient;
40 39
41 /** 40 /**
@@ -64,13 +63,13 @@ public class RedisPendingMessageResendJob { @@ -64,13 +63,13 @@ public class RedisPendingMessageResendJob {
64 private void execute() { 63 private void execute() {
65 StreamOperations<String, Object, Object> ops = redisTemplate.getRedisTemplate().opsForStream(); 64 StreamOperations<String, Object, Object> ops = redisTemplate.getRedisTemplate().opsForStream();
66 listeners.forEach(listener -> { 65 listeners.forEach(listener -> {
67 - PendingMessagesSummary pendingMessagesSummary = Objects.requireNonNull(ops.pending(listener.getStreamKey(), groupName)); 66 + PendingMessagesSummary pendingMessagesSummary = Objects.requireNonNull(ops.pending(listener.getStreamKey(), listener.getGroup()));
68 // 每个消费者的 pending 队列消息数量 67 // 每个消费者的 pending 队列消息数量
69 Map<String, Long> pendingMessagesPerConsumer = pendingMessagesSummary.getPendingMessagesPerConsumer(); 68 Map<String, Long> pendingMessagesPerConsumer = pendingMessagesSummary.getPendingMessagesPerConsumer();
70 pendingMessagesPerConsumer.forEach((consumerName, pendingMessageCount) -> { 69 pendingMessagesPerConsumer.forEach((consumerName, pendingMessageCount) -> {
71 log.info("[processPendingMessage][消费者({}) 消息数量({})]", consumerName, pendingMessageCount); 70 log.info("[processPendingMessage][消费者({}) 消息数量({})]", consumerName, pendingMessageCount);
72 // 每个消费者的 pending消息的详情信息 71 // 每个消费者的 pending消息的详情信息
73 - PendingMessages pendingMessages = ops.pending(listener.getStreamKey(), Consumer.from(groupName, consumerName), Range.unbounded(), pendingMessageCount); 72 + PendingMessages pendingMessages = ops.pending(listener.getStreamKey(), Consumer.from(listener.getGroup(), consumerName), Range.unbounded(), pendingMessageCount);
74 if (pendingMessages.isEmpty()) { 73 if (pendingMessages.isEmpty()) {
75 return; 74 return;
76 } 75 }
@@ -91,7 +90,7 @@ public class RedisPendingMessageResendJob { @@ -91,7 +90,7 @@ public class RedisPendingMessageResendJob {
91 .ofObject(records.get(0).getValue()) // 设置内容 90 .ofObject(records.get(0).getValue()) // 设置内容
92 .withStreamKey(listener.getStreamKey())); 91 .withStreamKey(listener.getStreamKey()));
93 // ack 消息消费完成 92 // ack 消息消费完成
94 - redisTemplate.getRedisTemplate().opsForStream().acknowledge(groupName, records.get(0)); 93 + redisTemplate.getRedisTemplate().opsForStream().acknowledge(listener.getGroup(), records.get(0));
95 log.info("[processPendingMessage][消息({})重新投递成功]", records.get(0).getId()); 94 log.info("[processPendingMessage][消息({})重新投递成功]", records.get(0).getId());
96 }); 95 });
97 }); 96 });
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/stream/AbstractRedisStreamMessageListener.java
@@ -53,6 +53,12 @@ public abstract class AbstractRedisStreamMessageListener&lt;T extends AbstractRedis @@ -53,6 +53,12 @@ public abstract class AbstractRedisStreamMessageListener&lt;T extends AbstractRedis
53 this.streamKey = messageType.getDeclaredConstructor().newInstance().getStreamKey(); 53 this.streamKey = messageType.getDeclaredConstructor().newInstance().getStreamKey();
54 } 54 }
55 55
  56 + protected AbstractRedisStreamMessageListener(String streamKey, String group) {
  57 + this.messageType = null;
  58 + this.streamKey = streamKey;
  59 + this.group = group;
  60 + }
  61 +
56 @Override 62 @Override
57 public void onMessage(ObjectRecord<String, String> message) { 63 public void onMessage(ObjectRecord<String, String> message) {
58 // 消费消息 64 // 消费消息
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java
1 package cn.iocoder.yudao.framework.mybatis.config; 1 package cn.iocoder.yudao.framework.mybatis.config;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
4 import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler; 6 import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler;
5 import com.baomidou.mybatisplus.annotation.DbType; 7 import com.baomidou.mybatisplus.annotation.DbType;
6 import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; 8 import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
  9 +import com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler;
7 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; 10 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
8 import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator; 11 import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
  12 +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
9 import com.baomidou.mybatisplus.extension.incrementer.*; 13 import com.baomidou.mybatisplus.extension.incrementer.*;
10 import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal; 14 import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;
11 import com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlParseCache; 15 import com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlParseCache;
12 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 16 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
13 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; 17 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
  18 +import com.fasterxml.jackson.databind.ObjectMapper;
14 import org.apache.ibatis.annotations.Mapper; 19 import org.apache.ibatis.annotations.Mapper;
15 import org.mybatis.spring.annotation.MapperScan; 20 import org.mybatis.spring.annotation.MapperScan;
16 import org.springframework.boot.autoconfigure.AutoConfiguration; 21 import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -18,6 +23,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -18,6 +23,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
18 import org.springframework.context.annotation.Bean; 23 import org.springframework.context.annotation.Bean;
19 import org.springframework.core.env.ConfigurableEnvironment; 24 import org.springframework.core.env.ConfigurableEnvironment;
20 25
  26 +import java.util.List;
21 import java.util.concurrent.TimeUnit; 27 import java.util.concurrent.TimeUnit;
22 28
23 /** 29 /**
@@ -42,6 +48,8 @@ public class YudaoMybatisAutoConfiguration { @@ -42,6 +48,8 @@ public class YudaoMybatisAutoConfiguration {
42 public MybatisPlusInterceptor mybatisPlusInterceptor() { 48 public MybatisPlusInterceptor mybatisPlusInterceptor() {
43 MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); 49 MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
44 mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); // 分页插件 50 mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); // 分页插件
  51 + // ↓↓↓ 按需开启,可能会影响到 updateBatch 的地方:例如说文件配置管理 ↓↓↓
  52 + // mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); // 拦截没有指定条件的 update 和 delete 语句
45 return mybatisPlusInterceptor; 53 return mybatisPlusInterceptor;
46 } 54 }
47 55
@@ -73,4 +81,15 @@ public class YudaoMybatisAutoConfiguration { @@ -73,4 +81,15 @@ public class YudaoMybatisAutoConfiguration {
73 throw new IllegalArgumentException(StrUtil.format("DbType{} 找不到合适的 IKeyGenerator 实现类", dbType)); 81 throw new IllegalArgumentException(StrUtil.format("DbType{} 找不到合适的 IKeyGenerator 实现类", dbType));
74 } 82 }
75 83
  84 + @Bean // 特殊:返回结果使用 Object 而不用 JacksonTypeHandler 的原因,避免因为 JacksonTypeHandler 被 mybatis 全局使用!
  85 + public Object jacksonTypeHandler(List<ObjectMapper> objectMappers) {
  86 + // 特殊:设置 JacksonTypeHandler 的 ObjectMapper!
  87 + ObjectMapper objectMapper = CollUtil.getFirst(objectMappers);
  88 + if (objectMapper == null) {
  89 + objectMapper = JsonUtils.getObjectMapper();
  90 + }
  91 + JacksonTypeHandler.setObjectMapper(objectMapper);
  92 + return new JacksonTypeHandler(Object.class);
  93 + }
  94 +
76 } 95 }
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java
@@ -18,6 +18,7 @@ import java.util.Objects; @@ -18,6 +18,7 @@ import java.util.Objects;
18 public class DefaultDBFieldHandler implements MetaObjectHandler { 18 public class DefaultDBFieldHandler implements MetaObjectHandler {
19 19
20 @Override 20 @Override
  21 + @SuppressWarnings("PatternVariableCanBeUsed")
21 public void insertFill(MetaObject metaObject) { 22 public void insertFill(MetaObject metaObject) {
22 if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) { 23 if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) {
23 BaseDO baseDO = (BaseDO) metaObject.getOriginalObject(); 24 BaseDO baseDO = (BaseDO) metaObject.getOriginalObject();
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/aop/RateLimiterAspect.java
@@ -39,7 +39,7 @@ public class RateLimiterAspect { @@ -39,7 +39,7 @@ public class RateLimiterAspect {
39 39
40 @Before("@annotation(rateLimiter)") 40 @Before("@annotation(rateLimiter)")
41 public void beforePointCut(JoinPoint joinPoint, RateLimiter rateLimiter) { 41 public void beforePointCut(JoinPoint joinPoint, RateLimiter rateLimiter) {
42 - // 获得 IdempotentKeyResolver 对象 42 + // 获得 RateLimiterKeyResolver 对象
43 RateLimiterKeyResolver keyResolver = keyResolvers.get(rateLimiter.keyResolver()); 43 RateLimiterKeyResolver keyResolver = keyResolvers.get(rateLimiter.keyResolver());
44 Assert.notNull(keyResolver, "找不到对应的 RateLimiterKeyResolver"); 44 Assert.notNull(keyResolver, "找不到对应的 RateLimiterKeyResolver");
45 // 解析 Key 45 // 解析 Key
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
@@ -165,7 +165,8 @@ public class YudaoWebSecurityConfigurerAdapter { @@ -165,7 +165,8 @@ public class YudaoWebSecurityConfigurerAdapter {
165 // 获得有 @PermitAll 注解的接口 165 // 获得有 @PermitAll 注解的接口
166 for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) { 166 for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) {
167 HandlerMethod handlerMethod = entry.getValue(); 167 HandlerMethod handlerMethod = entry.getValue();
168 - if (!handlerMethod.hasMethodAnnotation(PermitAll.class)) { 168 + if (!handlerMethod.hasMethodAnnotation(PermitAll.class) // 方法级
  169 + && !handlerMethod.getBeanType().isAnnotationPresent(PermitAll.class)) { // 接口级
169 continue; 170 continue;
170 } 171 }
171 Set<String> urls = new HashSet<>(); 172 Set<String> urls = new HashSet<>();
yudao-framework/yudao-spring-boot-starter-web/pom.xml
@@ -39,12 +39,12 @@ @@ -39,12 +39,12 @@
39 </dependency> 39 </dependency>
40 40
41 <dependency> 41 <dependency>
42 - <groupId>com.github.xingfudeshi</groupId> 42 + <groupId>com.github.xiaoymin</groupId>
43 <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> 43 <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
44 </dependency> 44 </dependency>
45 <dependency> 45 <dependency>
46 <groupId>org.springdoc</groupId> 46 <groupId>org.springdoc</groupId>
47 - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> 47 + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
48 </dependency> 48 </dependency>
49 49
50 <dependency> 50 <dependency>
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/encrypt/core/filter/ApiEncryptFilter.java
@@ -23,6 +23,7 @@ import org.springframework.http.HttpMethod; @@ -23,6 +23,7 @@ import org.springframework.http.HttpMethod;
23 import org.springframework.web.method.HandlerMethod; 23 import org.springframework.web.method.HandlerMethod;
24 import org.springframework.web.servlet.HandlerExecutionChain; 24 import org.springframework.web.servlet.HandlerExecutionChain;
25 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 25 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
  26 +import org.springframework.web.util.ServletRequestPathUtils;
26 27
27 import java.io.IOException; 28 import java.io.IOException;
28 29
@@ -131,6 +132,12 @@ public class ApiEncryptFilter extends ApiRequestFilter { @@ -131,6 +132,12 @@ public class ApiEncryptFilter extends ApiRequestFilter {
131 @SuppressWarnings("PatternVariableCanBeUsed") 132 @SuppressWarnings("PatternVariableCanBeUsed")
132 private ApiEncrypt getApiEncrypt(HttpServletRequest request) { 133 private ApiEncrypt getApiEncrypt(HttpServletRequest request) {
133 try { 134 try {
  135 + // 特殊:兼容 SpringBoot 2.X 版本会报错的问题 https://t.zsxq.com/kqyiB
  136 + if (!ServletRequestPathUtils.hasParsedRequestPath(request)) {
  137 + ServletRequestPathUtils.parseAndCache(request);
  138 + }
  139 +
  140 + // 解析 @ApiEncrypt 注解
134 HandlerExecutionChain mappingHandler = requestMappingHandlerMapping.getHandler(request); 141 HandlerExecutionChain mappingHandler = requestMappingHandlerMapping.getHandler(request);
135 if (mappingHandler == null) { 142 if (mappingHandler == null) {
136 return null; 143 return null;
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java
1 package cn.iocoder.yudao.framework.jackson.config; 1 package cn.iocoder.yudao.framework.jackson.config;
2 2
3 -import cn.hutool.core.collection.CollUtil;  
4 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 3 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
5 import cn.iocoder.yudao.framework.common.util.json.databind.NumberSerializer; 4 import cn.iocoder.yudao.framework.common.util.json.databind.NumberSerializer;
6 import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer; 5 import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer;
7 import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer; 6 import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;
  7 +import com.fasterxml.jackson.databind.Module;
8 import com.fasterxml.jackson.databind.ObjectMapper; 8 import com.fasterxml.jackson.databind.ObjectMapper;
9 import com.fasterxml.jackson.databind.module.SimpleModule; 9 import com.fasterxml.jackson.databind.module.SimpleModule;
10 import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; 10 import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
@@ -13,39 +13,65 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; @@ -13,39 +13,65 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
13 import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; 13 import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
14 import lombok.extern.slf4j.Slf4j; 14 import lombok.extern.slf4j.Slf4j;
15 import org.springframework.boot.autoconfigure.AutoConfiguration; 15 import org.springframework.boot.autoconfigure.AutoConfiguration;
  16 +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
  17 +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
16 import org.springframework.context.annotation.Bean; 18 import org.springframework.context.annotation.Bean;
17 19
18 import java.time.LocalDate; 20 import java.time.LocalDate;
19 import java.time.LocalDateTime; 21 import java.time.LocalDateTime;
20 import java.time.LocalTime; 22 import java.time.LocalTime;
21 -import java.util.List;  
22 23
23 -@AutoConfiguration 24 +@AutoConfiguration(after = JacksonAutoConfiguration.class)
24 @Slf4j 25 @Slf4j
25 public class YudaoJacksonAutoConfiguration { 26 public class YudaoJacksonAutoConfiguration {
26 27
  28 + /**
  29 + * 从 Builder 源头定制(关键:使用 *ByType,避免 handledType 要求)
  30 + */
27 @Bean 31 @Bean
28 - @SuppressWarnings("InstantiationOfUtilityClass")  
29 - public JsonUtils jsonUtils(List<ObjectMapper> objectMappers) {  
30 - // 1.1 创建 SimpleModule 对象  
31 - SimpleModule simpleModule = new SimpleModule();  
32 - simpleModule  
33 - // 新增 Long 类型序列化规则,数值超过 2^53-1,在 JS 会出现精度丢失问题,因此 Long 自动序列化为字符串类型  
34 - .addSerializer(Long.class, NumberSerializer.INSTANCE)  
35 - .addSerializer(Long.TYPE, NumberSerializer.INSTANCE)  
36 - .addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE)  
37 - .addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE)  
38 - .addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE)  
39 - .addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE)  
40 - // 新增 LocalDateTime 序列化、反序列化规则,使用 Long 时间戳  
41 - .addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE)  
42 - .addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);  
43 - // 1.2 注册到 objectMapper  
44 - objectMappers.forEach(objectMapper -> objectMapper.registerModule(simpleModule)); 32 + public Jackson2ObjectMapperBuilderCustomizer ldtEpochMillisCustomizer() {
  33 + return builder -> builder
  34 + // Long -> Number
  35 + .serializerByType(Long.class, NumberSerializer.INSTANCE)
  36 + .serializerByType(Long.TYPE, NumberSerializer.INSTANCE)
  37 + // LocalDate / LocalTime
  38 + .serializerByType(LocalDate.class, LocalDateSerializer.INSTANCE)
  39 + .deserializerByType(LocalDate.class, LocalDateDeserializer.INSTANCE)
  40 + .serializerByType(LocalTime.class, LocalTimeSerializer.INSTANCE)
  41 + .deserializerByType(LocalTime.class, LocalTimeDeserializer.INSTANCE)
  42 + // LocalDateTime < - > EpochMillis
  43 + .serializerByType(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE)
  44 + .deserializerByType(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);
  45 + }
45 46
46 - // 2. 设置 objectMapper 到 JsonUtils  
47 - JsonUtils.init(CollUtil.getFirst(objectMappers));  
48 - log.info("[init][初始化 JsonUtils 成功]"); 47 + /**
  48 + * 以 Bean 形式暴露 Module(Boot 会自动注册到所有 ObjectMapper)
  49 + */
  50 + @Bean
  51 + public Module timestampSupportModuleBean() {
  52 + SimpleModule m = new SimpleModule("TimestampSupportModule");
  53 + // Long -> Number,避免前端精度丢失
  54 + m.addSerializer(Long.class, NumberSerializer.INSTANCE);
  55 + m.addSerializer(Long.TYPE, NumberSerializer.INSTANCE);
  56 + // LocalDate / LocalTime
  57 + m.addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE);
  58 + m.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE);
  59 + m.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE);
  60 + m.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE);
  61 + // LocalDateTime < - > EpochMillis
  62 + m.addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE);
  63 + m.addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);
  64 + return m;
  65 + }
  66 +
  67 + /**
  68 + * 初始化全局 JsonUtils,直接使用主 ObjectMapper
  69 + */
  70 + @Bean
  71 + @SuppressWarnings("InstantiationOfUtilityClass")
  72 + public JsonUtils jsonUtils(ObjectMapper objectMapper) {
  73 + JsonUtils.init(objectMapper);
  74 + log.debug("[init][初始化 JsonUtils 成功]");
49 return new JsonUtils(); 75 return new JsonUtils();
50 } 76 }
51 77
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/Knife4jOpenApiCustomizer.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.swagger.config;
  2 +
  3 +import com.github.xiaoymin.knife4j.annotations.ApiSupport;
  4 +import com.github.xiaoymin.knife4j.core.conf.ExtensionsConstants;
  5 +import com.github.xiaoymin.knife4j.core.conf.GlobalConstants;
  6 +import com.github.xiaoymin.knife4j.spring.configuration.Knife4jProperties;
  7 +import com.github.xiaoymin.knife4j.spring.configuration.Knife4jSetting;
  8 +import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
  9 +import io.swagger.v3.oas.annotations.tags.Tag;
  10 +import io.swagger.v3.oas.models.OpenAPI;
  11 +import lombok.extern.slf4j.Slf4j;
  12 +import org.apache.commons.lang3.ArrayUtils;
  13 +import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
  14 +import org.springdoc.core.properties.SpringDocConfigProperties;
  15 +import org.springframework.beans.factory.config.BeanDefinition;
  16 +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
  17 +import org.springframework.context.annotation.Configuration;
  18 +import org.springframework.context.annotation.Primary;
  19 +import org.springframework.core.type.filter.AnnotationTypeFilter;
  20 +import org.springframework.util.CollectionUtils;
  21 +import org.springframework.web.bind.annotation.RestController;
  22 +
  23 +import java.lang.annotation.Annotation;
  24 +import java.util.*;
  25 +import java.util.stream.Collectors;
  26 +
  27 +/**
  28 + * 增强扩展属性支持
  29 + *
  30 + * 参考 <a href="https://github.com/xiaoymin/knife4j/issues/913">Spring Boot 3.4 以上版本 /v3/api-docs 解决接口报错,依赖修复</a>
  31 + *
  32 + * @since 4.1.0
  33 + * @author <a href="xiaoymin@foxmail.com">xiaoymin@foxmail.com</a>
  34 + * 2022/12/11 22:40
  35 + */
  36 +@Primary
  37 +@Configuration
  38 +@Slf4j
  39 +public class Knife4jOpenApiCustomizer extends com.github.xiaoymin.knife4j.spring.extension.Knife4jOpenApiCustomizer
  40 + implements GlobalOpenApiCustomizer {
  41 +
  42 + final Knife4jProperties knife4jProperties;
  43 + final SpringDocConfigProperties properties;
  44 +
  45 + public Knife4jOpenApiCustomizer(Knife4jProperties knife4jProperties, SpringDocConfigProperties properties) {
  46 + super(knife4jProperties,properties);
  47 + this.knife4jProperties = knife4jProperties;
  48 + this.properties = properties;
  49 + }
  50 +
  51 + @Override
  52 + public void customise(OpenAPI openApi) {
  53 + if (knife4jProperties.isEnable()) {
  54 + Knife4jSetting setting = knife4jProperties.getSetting();
  55 + OpenApiExtensionResolver openApiExtensionResolver = new OpenApiExtensionResolver(setting, knife4jProperties.getDocuments());
  56 + // 解析初始化
  57 + openApiExtensionResolver.start();
  58 + Map<String, Object> objectMap = new HashMap<>();
  59 + objectMap.put(GlobalConstants.EXTENSION_OPEN_SETTING_NAME, setting);
  60 + objectMap.put(GlobalConstants.EXTENSION_OPEN_MARKDOWN_NAME, openApiExtensionResolver.getMarkdownFiles());
  61 + openApi.addExtension(GlobalConstants.EXTENSION_OPEN_API_NAME, objectMap);
  62 + addOrderExtension(openApi);
  63 + }
  64 + }
  65 +
  66 + /**
  67 + * 往 OpenAPI 内 tags 字段添加 x-order 属性
  68 + *
  69 + * @param openApi openApi
  70 + */
  71 + private void addOrderExtension(OpenAPI openApi) {
  72 + if (CollectionUtils.isEmpty(properties.getGroupConfigs())) {
  73 + return;
  74 + }
  75 + // 获取包扫描路径
  76 + Set<String> packagesToScan =
  77 + properties.getGroupConfigs().stream()
  78 + .map(SpringDocConfigProperties.GroupConfig::getPackagesToScan)
  79 + .filter(toScan -> !CollectionUtils.isEmpty(toScan))
  80 + .flatMap(List::stream)
  81 + .collect(Collectors.toSet());
  82 + if (CollectionUtils.isEmpty(packagesToScan)) {
  83 + return;
  84 + }
  85 + // 扫描包下被 ApiSupport 注解的 RestController Class
  86 + Set<Class<?>> classes = packagesToScan.stream()
  87 + .map(packageToScan -> scanPackageByAnnotation(packageToScan, RestController.class))
  88 + .flatMap(Set::stream)
  89 + .filter(clazz -> clazz.isAnnotationPresent(ApiSupport.class))
  90 + .collect(Collectors.toSet());
  91 + if (!CollectionUtils.isEmpty(classes)) {
  92 + // ApiSupport oder 值存入 tagSortMap<Tag.name,ApiSupport.order>
  93 + Map<String, Integer> tagOrderMap = new HashMap<>();
  94 + classes.forEach(clazz -> {
  95 + Tag tag = getTag(clazz);
  96 + if (Objects.nonNull(tag)) {
  97 + ApiSupport apiSupport = clazz.getAnnotation(ApiSupport.class);
  98 + tagOrderMap.putIfAbsent(tag.name(), apiSupport.order());
  99 + }
  100 + });
  101 + // 往 openApi tags 字段添加 x-order 增强属性
  102 + if (openApi.getTags() != null) {
  103 + openApi.getTags().forEach(tag -> {
  104 + if (tagOrderMap.containsKey(tag.getName())) {
  105 + tag.addExtension(ExtensionsConstants.EXTENSION_ORDER, tagOrderMap.get(tag.getName()));
  106 + }
  107 + });
  108 + }
  109 + }
  110 + }
  111 +
  112 + private Tag getTag(Class<?> clazz) {
  113 + // 从类上获取
  114 + Tag tag = clazz.getAnnotation(Tag.class);
  115 + if (Objects.isNull(tag)) {
  116 + // 从接口上获取
  117 + Class<?>[] interfaces = clazz.getInterfaces();
  118 + if (ArrayUtils.isNotEmpty(interfaces)) {
  119 + for (Class<?> interfaceClazz : interfaces) {
  120 + Tag anno = interfaceClazz.getAnnotation(Tag.class);
  121 + if (Objects.nonNull(anno)) {
  122 + tag = anno;
  123 + break;
  124 + }
  125 + }
  126 + }
  127 + }
  128 + return tag;
  129 + }
  130 +
  131 + private Set<Class<?>> scanPackageByAnnotation(String packageName, final Class<? extends Annotation> annotationClass) {
  132 + ClassPathScanningCandidateComponentProvider scanner =
  133 + new ClassPathScanningCandidateComponentProvider(false);
  134 + scanner.addIncludeFilter(new AnnotationTypeFilter(annotationClass));
  135 + Set<Class<?>> classes = new HashSet<>();
  136 + for (BeanDefinition beanDefinition : scanner.findCandidateComponents(packageName)) {
  137 + try {
  138 + Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
  139 + classes.add(clazz);
  140 + } catch (ClassNotFoundException ignore) {
  141 + }
  142 + }
  143 + return classes;
  144 + }
  145 +
  146 +}
0 \ No newline at end of file 147 \ No newline at end of file
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
1 package cn.iocoder.yudao.framework.swagger.config; 1 package cn.iocoder.yudao.framework.swagger.config;
2 2
  3 +import com.github.xiaoymin.knife4j.spring.configuration.Knife4jAutoConfiguration;
3 import io.swagger.v3.oas.models.Components; 4 import io.swagger.v3.oas.models.Components;
4 import io.swagger.v3.oas.models.OpenAPI; 5 import io.swagger.v3.oas.models.OpenAPI;
5 import io.swagger.v3.oas.models.info.Contact; 6 import io.swagger.v3.oas.models.info.Contact;
@@ -11,6 +12,7 @@ import io.swagger.v3.oas.models.parameters.Parameter; @@ -11,6 +12,7 @@ import io.swagger.v3.oas.models.parameters.Parameter;
11 import io.swagger.v3.oas.models.security.SecurityRequirement; 12 import io.swagger.v3.oas.models.security.SecurityRequirement;
12 import io.swagger.v3.oas.models.security.SecurityScheme; 13 import io.swagger.v3.oas.models.security.SecurityScheme;
13 import org.springdoc.core.customizers.OpenApiBuilderCustomizer; 14 import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
  15 +import org.springdoc.core.customizers.OperationCustomizer;
14 import org.springdoc.core.customizers.ServerBaseUrlCustomizer; 16 import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
15 import org.springdoc.core.models.GroupedOpenApi; 17 import org.springdoc.core.models.GroupedOpenApi;
16 import org.springdoc.core.properties.SpringDocConfigProperties; 18 import org.springdoc.core.properties.SpringDocConfigProperties;
@@ -23,6 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -23,6 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
23 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 25 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
24 import org.springframework.boot.context.properties.EnableConfigurationProperties; 26 import org.springframework.boot.context.properties.EnableConfigurationProperties;
25 import org.springframework.context.annotation.Bean; 27 import org.springframework.context.annotation.Bean;
  28 +import org.springframework.context.annotation.Import;
26 import org.springframework.context.annotation.Primary; 29 import org.springframework.context.annotation.Primary;
27 import org.springframework.http.HttpHeaders; 30 import org.springframework.http.HttpHeaders;
28 31
@@ -42,10 +45,11 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_ @@ -42,10 +45,11 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_
42 * 45 *
43 * @author 芋道源码 46 * @author 芋道源码
44 */ 47 */
45 -@AutoConfiguration 48 +@AutoConfiguration(before = Knife4jAutoConfiguration.class) // before 原因,保证覆写的 Knife4jOpenApiCustomizer 先生效!相关 https://github.com/YunaiV/ruoyi-vue-pro/issues/954 讨论
46 @ConditionalOnClass({OpenAPI.class}) 49 @ConditionalOnClass({OpenAPI.class})
47 @EnableConfigurationProperties(SwaggerProperties.class) 50 @EnableConfigurationProperties(SwaggerProperties.class)
48 @ConditionalOnProperty(prefix = "springdoc.api-docs", name = "enabled", havingValue = "true", matchIfMissing = true) // 设置为 false 时,禁用 51 @ConditionalOnProperty(prefix = "springdoc.api-docs", name = "enabled", havingValue = "true", matchIfMissing = true) // 设置为 false 时,禁用
  52 +@Import(Knife4jOpenApiCustomizer.class)
49 public class YudaoSwaggerAutoConfiguration { 53 public class YudaoSwaggerAutoConfiguration {
50 54
51 // ========== 全局 OpenAPI 配置 ========== 55 // ========== 全局 OpenAPI 配置 ==========
@@ -125,6 +129,7 @@ public class YudaoSwaggerAutoConfiguration { @@ -125,6 +129,7 @@ public class YudaoSwaggerAutoConfiguration {
125 .addOperationCustomizer((operation, handlerMethod) -> operation 129 .addOperationCustomizer((operation, handlerMethod) -> operation
126 .addParametersItem(buildTenantHeaderParameter()) 130 .addParametersItem(buildTenantHeaderParameter())
127 .addParametersItem(buildSecurityHeaderParameter())) 131 .addParametersItem(buildSecurityHeaderParameter()))
  132 + .addOperationCustomizer(buildOperationIdCustomizer())
128 .build(); 133 .build();
129 } 134 }
130 135
@@ -156,5 +161,26 @@ public class YudaoSwaggerAutoConfiguration { @@ -156,5 +161,26 @@ public class YudaoSwaggerAutoConfiguration {
156 .schema(new StringSchema()._default("Bearer test1").name(HEADER_TENANT_ID).description("认证 Token")); // 默认:使用用户编号为 1 161 .schema(new StringSchema()._default("Bearer test1").name(HEADER_TENANT_ID).description("认证 Token")); // 默认:使用用户编号为 1
157 } 162 }
158 163
  164 + /**
  165 + * 核心:自定义OperationId生成规则,组合「类名前缀 + 方法名」
  166 + *
  167 + * @see <a href="https://github.com/YunaiV/ruoyi-vue-pro/issues/957">app-api 前缀不生效,都是使用 admin-api</a>
  168 + */
  169 + private static OperationCustomizer buildOperationIdCustomizer() {
  170 + return (operation, handlerMethod) -> {
  171 + // 1. 获取控制器类名(如 UserController)
  172 + String className = handlerMethod.getBeanType().getSimpleName();
  173 + // 2. 提取类名前缀(去除 Controller 后缀,如 UserController -> User)
  174 + String classPrefix = className.replaceAll("Controller$", "");
  175 + // 3. 获取方法名(如 list)
  176 + String methodName = handlerMethod.getMethod().getName();
  177 + // 4. 组合生成 operationId(如 User_list)
  178 + String operationId = classPrefix + "_" + methodName;
  179 + // 5. 设置自定义 operationId
  180 + operation.setOperationId(operationId);
  181 + return operation;
  182 + };
  183 + }
  184 +
159 } 185 }
160 186
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java
1 package cn.iocoder.yudao.framework.web.config; 1 package cn.iocoder.yudao.framework.web.config;
2 2
  3 +import cn.hutool.core.util.StrUtil;
3 import cn.iocoder.yudao.framework.common.biz.infra.logger.ApiErrorLogCommonApi; 4 import cn.iocoder.yudao.framework.common.biz.infra.logger.ApiErrorLogCommonApi;
4 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; 5 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
5 import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyFilter; 6 import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyFilter;
@@ -7,6 +8,7 @@ import cn.iocoder.yudao.framework.web.core.filter.DemoFilter; @@ -7,6 +8,7 @@ import cn.iocoder.yudao.framework.web.core.filter.DemoFilter;
7 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; 8 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
8 import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler; 9 import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler;
9 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; 10 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
  11 +import com.google.common.collect.Maps;
10 import jakarta.annotation.Resource; 12 import jakarta.annotation.Resource;
11 import jakarta.servlet.Filter; 13 import jakarta.servlet.Filter;
12 import org.springframework.beans.factory.annotation.Value; 14 import org.springframework.beans.factory.annotation.Value;
@@ -14,6 +16,7 @@ import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -14,6 +16,7 @@ import org.springframework.boot.autoconfigure.AutoConfiguration;
14 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 16 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
15 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 17 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
16 import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration; 18 import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
  19 +import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
17 import org.springframework.boot.context.properties.EnableConfigurationProperties; 20 import org.springframework.boot.context.properties.EnableConfigurationProperties;
18 import org.springframework.boot.web.client.RestTemplateBuilder; 21 import org.springframework.boot.web.client.RestTemplateBuilder;
19 import org.springframework.boot.web.servlet.FilterRegistrationBean; 22 import org.springframework.boot.web.servlet.FilterRegistrationBean;
@@ -26,35 +29,57 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; @@ -26,35 +29,57 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
26 import org.springframework.web.filter.CorsFilter; 29 import org.springframework.web.filter.CorsFilter;
27 import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; 30 import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
28 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 31 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  32 +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
  33 +
  34 +import java.util.Map;
  35 +import java.util.function.Predicate;
29 36
30 @AutoConfiguration 37 @AutoConfiguration
31 @EnableConfigurationProperties(WebProperties.class) 38 @EnableConfigurationProperties(WebProperties.class)
32 -public class YudaoWebAutoConfiguration implements WebMvcConfigurer { 39 +public class YudaoWebAutoConfiguration {
33 40
34 - @Resource  
35 - private WebProperties webProperties;  
36 /** 41 /**
37 * 应用名 42 * 应用名
38 */ 43 */
39 @Value("${spring.application.name}") 44 @Value("${spring.application.name}")
40 private String applicationName; 45 private String applicationName;
41 46
42 - @Override  
43 - public void configurePathMatch(PathMatchConfigurer configurer) {  
44 - configurePathMatch(configurer, webProperties.getAdminApi());  
45 - configurePathMatch(configurer, webProperties.getAppApi());  
46 - } 47 + @Bean
  48 + public WebMvcRegistrations webMvcRegistrations(WebProperties webProperties) {
  49 + return new WebMvcRegistrations() {
47 50
48 - /**  
49 - * 设置 API 前缀,仅仅匹配 controller 包下的  
50 - *  
51 - * @param configurer 配置  
52 - * @param api API 配置  
53 - */  
54 - private void configurePathMatch(PathMatchConfigurer configurer, WebProperties.Api api) {  
55 - AntPathMatcher antPathMatcher = new AntPathMatcher(".");  
56 - configurer.addPathPrefix(api.getPrefix(), clazz -> clazz.isAnnotationPresent(RestController.class)  
57 - && antPathMatcher.match(api.getController(), clazz.getPackage().getName())); // 仅仅匹配 controller 包 51 + @Override
  52 + public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
  53 + var mapping = new RequestMappingHandlerMapping();
  54 + // 实例化时就带上前缀
  55 + mapping.setPathPrefixes(buildPathPrefixes(webProperties));
  56 + return mapping;
  57 + }
  58 +
  59 + /**
  60 + * 构建 prefix → 匹配条件的映射
  61 + */
  62 + private Map<String, Predicate<Class<?>>> buildPathPrefixes(WebProperties webProperties) {
  63 + AntPathMatcher antPathMatcher = new AntPathMatcher(".");
  64 + Map<String, Predicate<Class<?>>> pathPrefixes = Maps.newLinkedHashMapWithExpectedSize(2);
  65 + putPathPrefix(pathPrefixes, webProperties.getAdminApi(), antPathMatcher);
  66 + putPathPrefix(pathPrefixes, webProperties.getAppApi(), antPathMatcher);
  67 + return pathPrefixes;
  68 + }
  69 +
  70 + /**
  71 + * 设置 API 前缀,仅仅匹配 controller 包下的
  72 + */
  73 + private void putPathPrefix(Map<String, Predicate<Class<?>>> pathPrefixes, WebProperties.Api api, AntPathMatcher matcher) {
  74 + if (api == null || StrUtil.isEmpty(api.getPrefix())) {
  75 + return;
  76 + }
  77 + pathPrefixes.put(api.getPrefix(), // api 前缀
  78 + clazz -> clazz.isAnnotationPresent(RestController.class)
  79 + && matcher.match(api.getController(), clazz.getPackage().getName()));
  80 + }
  81 +
  82 + };
58 } 83 }
59 84
60 @Bean 85 @Bean
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
@@ -182,6 +182,7 @@ public class GlobalExceptionHandler { @@ -182,6 +182,7 @@ public class GlobalExceptionHandler {
182 * 例如说,接口上设置了 @RequestBody 实体中 xx 属性类型为 Integer,结果传递 xx 参数类型为 String 182 * 例如说,接口上设置了 @RequestBody 实体中 xx 属性类型为 Integer,结果传递 xx 参数类型为 String
183 */ 183 */
184 @ExceptionHandler(HttpMessageNotReadableException.class) 184 @ExceptionHandler(HttpMessageNotReadableException.class)
  185 + @SuppressWarnings("PatternVariableCanBeUsed")
185 public CommonResult<?> methodArgumentTypeInvalidFormatExceptionHandler(HttpMessageNotReadableException ex) { 186 public CommonResult<?> methodArgumentTypeInvalidFormatExceptionHandler(HttpMessageNotReadableException ex) {
186 log.warn("[methodArgumentTypeInvalidFormatExceptionHandler]", ex); 187 log.warn("[methodArgumentTypeInvalidFormatExceptionHandler]", ex);
187 if (ex.getCause() instanceof InvalidFormatException) { 188 if (ex.getCause() instanceof InvalidFormatException) {
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
@@ -144,6 +144,7 @@ public class WebFrameworkUtils { @@ -144,6 +144,7 @@ public class WebFrameworkUtils {
144 return (CommonResult<?>) request.getAttribute(REQUEST_ATTRIBUTE_COMMON_RESULT); 144 return (CommonResult<?>) request.getAttribute(REQUEST_ATTRIBUTE_COMMON_RESULT);
145 } 145 }
146 146
  147 + @SuppressWarnings("PatternVariableCanBeUsed")
147 public static HttpServletRequest getRequest() { 148 public static HttpServletRequest getRequest() {
148 RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); 149 RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
149 if (!(requestAttributes instanceof ServletRequestAttributes)) { 150 if (!(requestAttributes instanceof ServletRequestAttributes)) {
yudao-module-ai/pom.xml
@@ -19,8 +19,8 @@ @@ -19,8 +19,8 @@
19 国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno 19 国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno
20 </description> 20 </description>
21 <properties> 21 <properties>
22 - <spring-ai.version>1.0.0</spring-ai.version>  
23 - <alibaba-ai.version>1.0.0.2</alibaba-ai.version> 22 + <spring-ai.version>1.0.1</spring-ai.version>
  23 + <alibaba-ai.version>1.0.0.3</alibaba-ai.version>
24 <tinyflow.version>1.0.2</tinyflow.version> 24 <tinyflow.version>1.0.2</tinyflow.version>
25 </properties> 25 </properties>
26 26
@@ -78,6 +78,12 @@ @@ -78,6 +78,12 @@
78 <groupId>org.springframework.ai</groupId> 78 <groupId>org.springframework.ai</groupId>
79 <artifactId>spring-ai-starter-model-openai</artifactId> 79 <artifactId>spring-ai-starter-model-openai</artifactId>
80 <version>${spring-ai.version}</version> 80 <version>${spring-ai.version}</version>
  81 + <exclusions>
  82 + <exclusion>
  83 + <groupId>io.swagger.core.v3</groupId>
  84 + <artifactId>swagger-annotations-jakarta</artifactId>
  85 + </exclusion>
  86 + </exclusions>
81 </dependency> 87 </dependency>
82 <dependency> 88 <dependency>
83 <groupId>org.springframework.ai</groupId> 89 <groupId>org.springframework.ai</groupId>
@@ -86,6 +92,11 @@ @@ -86,6 +92,11 @@
86 </dependency> 92 </dependency>
87 <dependency> 93 <dependency>
88 <groupId>org.springframework.ai</groupId> 94 <groupId>org.springframework.ai</groupId>
  95 + <artifactId>spring-ai-starter-model-anthropic</artifactId>
  96 + <version>${spring-ai.version}</version>
  97 + </dependency>
  98 + <dependency>
  99 + <groupId>org.springframework.ai</groupId>
89 <artifactId>spring-ai-starter-model-deepseek</artifactId> 100 <artifactId>spring-ai-starter-model-deepseek</artifactId>
90 <version>${spring-ai.version}</version> 101 <version>${spring-ai.version}</version>
91 </dependency> 102 </dependency>
@@ -182,6 +193,24 @@ @@ -182,6 +193,24 @@
182 </exclusions> 193 </exclusions>
183 </dependency> 194 </dependency>
184 195
  196 + <!-- MCP 相关 -->
  197 + <!--
  198 + 特殊说明:不能使用 spring-ai-starter-mcp-server-webflux 或 spring-ai-starter-mcp-client-webflux !!!
  199 + 原因:项目使用了 SpringMVC,而不是 WebFlux。引入上述 2 个,会导致 SSE Server 失效。
  200 + -->
  201 + <dependency>
  202 + <!-- 服务端 -->
  203 + <groupId>org.springframework.ai</groupId>
  204 + <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
  205 + <version>${spring-ai.version}</version>
  206 + </dependency>
  207 + <dependency>
  208 + <!-- 客户端 -->
  209 + <groupId>org.springframework.ai</groupId>
  210 + <artifactId>spring-ai-starter-mcp-client</artifactId>
  211 + <version>${spring-ai.version}</version>
  212 + </dependency>
  213 +
185 <!-- TinyFlow:AI 工作流 --> 214 <!-- TinyFlow:AI 工作流 -->
186 <dependency> 215 <dependency>
187 <groupId>dev.tinyflow</groupId> 216 <groupId>dev.tinyflow</groupId>
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http
@@ -20,9 +20,46 @@ tenant-id: {{adminTenantId}} @@ -20,9 +20,46 @@ tenant-id: {{adminTenantId}}
20 "content": "1+1=?" 20 "content": "1+1=?"
21 } 21 }
22 22
  23 +### 发送消息(流式)【带文件】
  24 +POST {{baseUrl}}/ai/chat/message/send-stream
  25 +Content-Type: application/json
  26 +Authorization: {{token}}
  27 +tenant-id: {{adminTenantId}}
  28 +
  29 +{
  30 + "conversationId": "1781604279872581797",
  31 + "content": "图片里有什么?",
  32 + "attachmentUrls": ["http://test.yudao.iocoder.cn/1755531278.jpeg"]
  33 +}
  34 +
  35 +### 发送消息(流式)【追问带文件】
  36 +POST {{baseUrl}}/ai/chat/message/send-stream
  37 +Content-Type: application/json
  38 +Authorization: {{token}}
  39 +tenant-id: {{adminTenantId}}
  40 +
  41 +{
  42 + "conversationId": "1781604279872581799",
  43 + "content": "说下图片里,有哪些字?",
  44 + "useContext": true
  45 +}
  46 +
  47 +### 发送消息(流式)【联网搜索】
  48 +POST {{baseUrl}}/ai/chat/message/send-stream
  49 +Content-Type: application/json
  50 +Authorization: {{token}}
  51 +tenant-id: {{adminTenantId}}
  52 +
  53 +{
  54 + "conversationId": "1781604279872581799",
  55 + "content": "今天是周几?",
  56 + "useSearch": true
  57 +}
  58 +
23 ### 获得指定对话的消息列表 59 ### 获得指定对话的消息列表
24 -GET {{baseUrl}}/ai/chat/message/list-by-conversation-id?conversationId=1781604279872581649 60 +GET {{baseUrl}}/ai/chat/message/list-by-conversation-id?conversationId=1781604279872581799
25 Authorization: {{token}} 61 Authorization: {{token}}
  62 +tenant-id: {{adminTenantId}}
26 63
27 ### 删除消息 64 ### 删除消息
28 DELETE {{baseUrl}}/ai/chat/message/delete?id=50 65 DELETE {{baseUrl}}/ai/chat/message/delete?id=50
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java
1 package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message; 1 package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;
2 2
  3 +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;
3 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 5 import lombok.Data;
5 6
@@ -37,6 +38,9 @@ public class AiChatMessageRespVO { @@ -37,6 +38,9 @@ public class AiChatMessageRespVO {
37 @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊") 38 @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊")
38 private String content; 39 private String content;
39 40
  41 + @Schema(description = "推理内容", example = "要达到这个目标,你需要...")
  42 + private String reasoningContent;
  43 +
40 @Schema(description = "是否携带上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") 44 @Schema(description = "是否携带上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
41 private Boolean useContext; 45 private Boolean useContext;
42 46
@@ -46,6 +50,12 @@ public class AiChatMessageRespVO { @@ -46,6 +50,12 @@ public class AiChatMessageRespVO {
46 @Schema(description = "知识库段落数组") 50 @Schema(description = "知识库段落数组")
47 private List<KnowledgeSegment> segments; 51 private List<KnowledgeSegment> segments;
48 52
  53 + @Schema(description = "联网搜索的网页内容数组")
  54 + private List<AiWebSearchResponse.WebPage> webSearchPages;
  55 +
  56 + @Schema(description = "附件 URL 数组", example = "https://www.iocoder.cn/1.png")
  57 + private List<String> attachmentUrls;
  58 +
49 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51") 59 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51")
50 private LocalDateTime createTime; 60 private LocalDateTime createTime;
51 61
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendReqVO.java
@@ -3,9 +3,9 @@ package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message; @@ -3,9 +3,9 @@ package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import jakarta.validation.constraints.NotEmpty; 4 import jakarta.validation.constraints.NotEmpty;
5 import jakarta.validation.constraints.NotNull; 5 import jakarta.validation.constraints.NotNull;
6 -import jakarta.validation.constraints.Size;  
7 import lombok.Data; 6 import lombok.Data;
8 -import lombok.experimental.Accessors; 7 +
  8 +import java.util.List;
9 9
10 @Schema(description = "管理后台 - AI 聊天消息发送 Request VO") 10 @Schema(description = "管理后台 - AI 聊天消息发送 Request VO")
11 @Data 11 @Data
@@ -22,4 +22,10 @@ public class AiChatMessageSendReqVO { @@ -22,4 +22,10 @@ public class AiChatMessageSendReqVO {
22 @Schema(description = "是否携带上下文", example = "true") 22 @Schema(description = "是否携带上下文", example = "true")
23 private Boolean useContext; 23 private Boolean useContext;
24 24
  25 + @Schema(description = "是否联网搜索", example = "true")
  26 + private Boolean useSearch;
  27 +
  28 + @Schema(description = "附件 URL 数组", example = "https://www.iocoder.cn/1.png")
  29 + private List<String> attachmentUrls;
  30 +
25 } 31 }
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java
1 package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message; 1 package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;
2 2
  3 +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;
3 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 5 import lombok.Data;
5 6
@@ -29,12 +30,18 @@ public class AiChatMessageSendRespVO { @@ -29,12 +30,18 @@ public class AiChatMessageSendRespVO {
29 @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊") 30 @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊")
30 private String content; 31 private String content;
31 32
  33 + @Schema(description = "推理内容", example = "要达到这个目标,你需要...")
  34 + private String reasoningContent;
  35 +
32 @Schema(description = "知识库段落编号数组", example = "[1,2,3]") 36 @Schema(description = "知识库段落编号数组", example = "[1,2,3]")
33 private List<Long> segmentIds; 37 private List<Long> segmentIds;
34 38
35 @Schema(description = "知识库段落数组") 39 @Schema(description = "知识库段落数组")
36 private List<AiChatMessageRespVO.KnowledgeSegment> segments; 40 private List<AiChatMessageRespVO.KnowledgeSegment> segments;
37 41
  42 + @Schema(description = "联网搜索的网页内容数组")
  43 + private List<AiWebSearchResponse.WebPage> webSearchPages;
  44 +
38 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 45 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
39 private LocalDateTime createTime; 46 private LocalDateTime createTime;
40 47
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java
@@ -52,6 +52,9 @@ public class AiChatRoleRespVO implements VO { @@ -52,6 +52,9 @@ public class AiChatRoleRespVO implements VO {
52 @Schema(description = "引用的工具编号列表", example = "1,2,3") 52 @Schema(description = "引用的工具编号列表", example = "1,2,3")
53 private List<Long> toolIds; 53 private List<Long> toolIds;
54 54
  55 + @Schema(description = "引用的 MCP Client 名字列表", example = "filesystem")
  56 + private List<String> mcpClientNames;
  57 +
55 @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 58 @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
56 private Boolean publicStatus; 59 private Boolean publicStatus;
57 60
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java
@@ -37,4 +37,7 @@ public class AiChatRoleSaveMyReqVO { @@ -37,4 +37,7 @@ public class AiChatRoleSaveMyReqVO {
37 @Schema(description = "引用的工具编号列表", example = "1,2,3") 37 @Schema(description = "引用的工具编号列表", example = "1,2,3")
38 private List<Long> toolIds; 38 private List<Long> toolIds;
39 39
  40 + @Schema(description = "引用的 MCP Client 名字列表", example = "filesystem")
  41 + private List<String> mcpClientNames;
  42 +
40 } 43 }
41 \ No newline at end of file 44 \ No newline at end of file
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java
@@ -50,6 +50,9 @@ public class AiChatRoleSaveReqVO { @@ -50,6 +50,9 @@ public class AiChatRoleSaveReqVO {
50 @Schema(description = "引用的工具编号列表", example = "1,2,3") 50 @Schema(description = "引用的工具编号列表", example = "1,2,3")
51 private List<Long> toolIds; 51 private List<Long> toolIds;
52 52
  53 + @Schema(description = "引用的 MCP Client 名字列表", example = "filesystem")
  54 + private List<String> mcpClientNames;
  55 +
53 @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 56 @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
54 @NotNull(message = "是否公开不能为空") 57 @NotNull(message = "是否公开不能为空")
55 private Boolean publicStatus; 58 private Boolean publicStatus;
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java
@@ -2,14 +2,20 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.chat; @@ -2,14 +2,20 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; 4 import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
  5 +import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;
5 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; 6 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
6 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; 7 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
7 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; 8 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
  9 +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;
8 import com.baomidou.mybatisplus.annotation.KeySequence; 10 import com.baomidou.mybatisplus.annotation.KeySequence;
9 import com.baomidou.mybatisplus.annotation.TableField; 11 import com.baomidou.mybatisplus.annotation.TableField;
10 import com.baomidou.mybatisplus.annotation.TableId; 12 import com.baomidou.mybatisplus.annotation.TableId;
11 import com.baomidou.mybatisplus.annotation.TableName; 13 import com.baomidou.mybatisplus.annotation.TableName;
12 -import lombok.*; 14 +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
  15 +import lombok.AllArgsConstructor;
  16 +import lombok.Builder;
  17 +import lombok.Data;
  18 +import lombok.NoArgsConstructor;
13 import org.springframework.ai.chat.messages.MessageType; 19 import org.springframework.ai.chat.messages.MessageType;
14 20
15 import java.util.List; 21 import java.util.List;
@@ -87,6 +93,10 @@ public class AiChatMessageDO extends BaseDO { @@ -87,6 +93,10 @@ public class AiChatMessageDO extends BaseDO {
87 * 聊天内容 93 * 聊天内容
88 */ 94 */
89 private String content; 95 private String content;
  96 + /**
  97 + * 推理内容
  98 + */
  99 + private String reasoningContent;
90 100
91 /** 101 /**
92 * 是否携带上下文 102 * 是否携带上下文
@@ -101,4 +111,16 @@ public class AiChatMessageDO extends BaseDO { @@ -101,4 +111,16 @@ public class AiChatMessageDO extends BaseDO {
101 @TableField(typeHandler = LongListTypeHandler.class) 111 @TableField(typeHandler = LongListTypeHandler.class)
102 private List<Long> segmentIds; 112 private List<Long> segmentIds;
103 113
  114 + /**
  115 + * 联网搜索的网页内容数组
  116 + */
  117 + @TableField(typeHandler = JacksonTypeHandler.class)
  118 + private List<AiWebSearchResponse.WebPage> webSearchPages;
  119 +
  120 + /**
  121 + * 附件 URL 数组
  122 + */
  123 + @TableField(typeHandler = StringListTypeHandler.class)
  124 + private List<String> attachmentUrls;
  125 +
104 } 126 }
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.model; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.model;
3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5 import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; 5 import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
  6 +import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;
6 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; 7 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
7 import com.baomidou.mybatisplus.annotation.KeySequence; 8 import com.baomidou.mybatisplus.annotation.KeySequence;
8 import com.baomidou.mybatisplus.annotation.TableField; 9 import com.baomidou.mybatisplus.annotation.TableField;
@@ -80,6 +81,13 @@ public class AiChatRoleDO extends BaseDO { @@ -80,6 +81,13 @@ public class AiChatRoleDO extends BaseDO {
80 */ 81 */
81 @TableField(typeHandler = LongListTypeHandler.class) 82 @TableField(typeHandler = LongListTypeHandler.class)
82 private List<Long> toolIds; 83 private List<Long> toolIds;
  84 + /**
  85 + * 引用的 MCP Client 名字列表
  86 + *
  87 + * 关联 spring.ai.mcp.client 下的名字
  88 + */
  89 + @TableField(typeHandler = StringListTypeHandler.class)
  90 + private List<String> mcpClientNames;
83 91
84 /** 92 /**
85 * 是否公开 93 * 是否公开
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java
1 package cn.iocoder.yudao.module.ai.dal.dataobject.model; 1 package cn.iocoder.yudao.module.ai.dal.dataobject.model;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 -import cn.iocoder.yudao.module.ai.service.model.tool.DirectoryListToolFunction;  
5 -import cn.iocoder.yudao.module.ai.service.model.tool.WeatherQueryToolFunction; 4 +import cn.iocoder.yudao.module.ai.tool.function.DirectoryListToolFunction;
  5 +import cn.iocoder.yudao.module.ai.tool.function.WeatherQueryToolFunction;
6 import com.baomidou.mybatisplus.annotation.KeySequence; 6 import com.baomidou.mybatisplus.annotation.KeySequence;
7 import com.baomidou.mybatisplus.annotation.TableId; 7 import com.baomidou.mybatisplus.annotation.TableId;
8 import com.baomidou.mybatisplus.annotation.TableName; 8 import com.baomidou.mybatisplus.annotation.TableName;
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiPlatformEnum.java
@@ -33,6 +33,8 @@ public enum AiPlatformEnum implements ArrayValuable&lt;String&gt; { @@ -33,6 +33,8 @@ public enum AiPlatformEnum implements ArrayValuable&lt;String&gt; {
33 33
34 OPENAI("OpenAI", "OpenAI"), // OpenAI 官方 34 OPENAI("OpenAI", "OpenAI"), // OpenAI 官方
35 AZURE_OPENAI("AzureOpenAI", "AzureOpenAI"), // OpenAI 微软 35 AZURE_OPENAI("AzureOpenAI", "AzureOpenAI"), // OpenAI 微软
  36 + ANTHROPIC("Anthropic", "Anthropic"), // Anthropic Claude
  37 + GEMINI("Gemini", "Gemini"), // 谷歌 Gemini
36 OLLAMA("Ollama", "Ollama"), 38 OLLAMA("Ollama", "Ollama"),
37 39
38 STABLE_DIFFUSION("StableDiffusion", "StableDiffusion"), // Stability AI 40 STABLE_DIFFUSION("StableDiffusion", "StableDiffusion"), // Stability AI
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java
@@ -2,25 +2,34 @@ package cn.iocoder.yudao.module.ai.framework.ai.config; @@ -2,25 +2,34 @@ package cn.iocoder.yudao.module.ai.framework.ai.config;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 import cn.hutool.extra.spring.SpringUtil; 4 import cn.hutool.extra.spring.SpringUtil;
5 -import cn.iocoder.yudao.module.ai.framework.ai.core.AiModelFactory;  
6 -import cn.iocoder.yudao.module.ai.framework.ai.core.AiModelFactoryImpl; 5 +import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactory;
  6 +import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactoryImpl;
7 import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; 7 import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;
8 import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel; 8 import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;
  9 +import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel;
9 import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel; 10 import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;
10 import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; 11 import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
11 import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants; 12 import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;
12 import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel; 13 import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
13 import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi; 14 import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
14 import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel; 15 import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel;
  16 +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient;
  17 +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha.AiBoChaWebSearchClient;
  18 +import cn.iocoder.yudao.module.ai.tool.method.PersonService;
15 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.ai.deepseek.DeepSeekChatModel;
  21 +import org.springframework.ai.deepseek.DeepSeekChatOptions;
  22 +import org.springframework.ai.deepseek.api.DeepSeekApi;
16 import org.springframework.ai.embedding.BatchingStrategy; 23 import org.springframework.ai.embedding.BatchingStrategy;
17 import org.springframework.ai.embedding.TokenCountBatchingStrategy; 24 import org.springframework.ai.embedding.TokenCountBatchingStrategy;
18 import org.springframework.ai.model.tool.ToolCallingManager; 25 import org.springframework.ai.model.tool.ToolCallingManager;
19 import org.springframework.ai.openai.OpenAiChatModel; 26 import org.springframework.ai.openai.OpenAiChatModel;
20 import org.springframework.ai.openai.OpenAiChatOptions; 27 import org.springframework.ai.openai.OpenAiChatOptions;
21 import org.springframework.ai.openai.api.OpenAiApi; 28 import org.springframework.ai.openai.api.OpenAiApi;
  29 +import org.springframework.ai.support.ToolCallbacks;
22 import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; 30 import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
23 import org.springframework.ai.tokenizer.TokenCountEstimator; 31 import org.springframework.ai.tokenizer.TokenCountEstimator;
  32 +import org.springframework.ai.tool.ToolCallback;
24 import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientProperties; 33 import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientProperties;
25 import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreProperties; 34 import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreProperties;
26 import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreProperties; 35 import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreProperties;
@@ -30,6 +39,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties @@ -30,6 +39,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
30 import org.springframework.context.annotation.Bean; 39 import org.springframework.context.annotation.Bean;
31 import org.springframework.context.annotation.Configuration; 40 import org.springframework.context.annotation.Configuration;
32 41
  42 +import java.util.List;
  43 +
33 /** 44 /**
34 * 芋道 AI 自动配置 45 * 芋道 AI 自动配置
35 * 46 *
@@ -52,19 +63,48 @@ public class AiAutoConfiguration { @@ -52,19 +63,48 @@ public class AiAutoConfiguration {
52 // ========== 各种 AI Client 创建 ========== 63 // ========== 各种 AI Client 创建 ==========
53 64
54 @Bean 65 @Bean
  66 + @ConditionalOnProperty(value = "yudao.ai.gemini.enable", havingValue = "true")
  67 + public GeminiChatModel geminiChatModel(YudaoAiProperties yudaoAiProperties) {
  68 + YudaoAiProperties.Gemini properties = yudaoAiProperties.getGemini();
  69 + return buildGeminiChatClient(properties);
  70 + }
  71 +
  72 + public GeminiChatModel buildGeminiChatClient(YudaoAiProperties.Gemini properties) {
  73 + if (StrUtil.isEmpty(properties.getModel())) {
  74 + properties.setModel(GeminiChatModel.MODEL_DEFAULT);
  75 + }
  76 + OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
  77 + .openAiApi(OpenAiApi.builder()
  78 + .baseUrl(GeminiChatModel.BASE_URL)
  79 + .completionsPath(GeminiChatModel.COMPLETE_PATH)
  80 + .apiKey(properties.getApiKey())
  81 + .build())
  82 + .defaultOptions(OpenAiChatOptions.builder()
  83 + .model(properties.getModel())
  84 + .temperature(properties.getTemperature())
  85 + .maxTokens(properties.getMaxTokens())
  86 + .topP(properties.getTopP())
  87 + .build())
  88 + .toolCallingManager(getToolCallingManager())
  89 + .build();
  90 + return new GeminiChatModel(openAiChatModel);
  91 + }
  92 +
  93 + @Bean
55 @ConditionalOnProperty(value = "yudao.ai.doubao.enable", havingValue = "true") 94 @ConditionalOnProperty(value = "yudao.ai.doubao.enable", havingValue = "true")
56 public DouBaoChatModel douBaoChatClient(YudaoAiProperties yudaoAiProperties) { 95 public DouBaoChatModel douBaoChatClient(YudaoAiProperties yudaoAiProperties) {
57 - YudaoAiProperties.DouBaoProperties properties = yudaoAiProperties.getDoubao(); 96 + YudaoAiProperties.DouBao properties = yudaoAiProperties.getDoubao();
58 return buildDouBaoChatClient(properties); 97 return buildDouBaoChatClient(properties);
59 } 98 }
60 99
61 - public DouBaoChatModel buildDouBaoChatClient(YudaoAiProperties.DouBaoProperties properties) { 100 + public DouBaoChatModel buildDouBaoChatClient(YudaoAiProperties.DouBao properties) {
62 if (StrUtil.isEmpty(properties.getModel())) { 101 if (StrUtil.isEmpty(properties.getModel())) {
63 properties.setModel(DouBaoChatModel.MODEL_DEFAULT); 102 properties.setModel(DouBaoChatModel.MODEL_DEFAULT);
64 } 103 }
65 OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() 104 OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
66 .openAiApi(OpenAiApi.builder() 105 .openAiApi(OpenAiApi.builder()
67 .baseUrl(DouBaoChatModel.BASE_URL) 106 .baseUrl(DouBaoChatModel.BASE_URL)
  107 + .completionsPath(DouBaoChatModel.COMPLETE_PATH)
68 .apiKey(properties.getApiKey()) 108 .apiKey(properties.getApiKey())
69 .build()) 109 .build())
70 .defaultOptions(OpenAiChatOptions.builder() 110 .defaultOptions(OpenAiChatOptions.builder()
@@ -81,20 +121,20 @@ public class AiAutoConfiguration { @@ -81,20 +121,20 @@ public class AiAutoConfiguration {
81 @Bean 121 @Bean
82 @ConditionalOnProperty(value = "yudao.ai.siliconflow.enable", havingValue = "true") 122 @ConditionalOnProperty(value = "yudao.ai.siliconflow.enable", havingValue = "true")
83 public SiliconFlowChatModel siliconFlowChatClient(YudaoAiProperties yudaoAiProperties) { 123 public SiliconFlowChatModel siliconFlowChatClient(YudaoAiProperties yudaoAiProperties) {
84 - YudaoAiProperties.SiliconFlowProperties properties = yudaoAiProperties.getSiliconflow(); 124 + YudaoAiProperties.SiliconFlow properties = yudaoAiProperties.getSiliconflow();
85 return buildSiliconFlowChatClient(properties); 125 return buildSiliconFlowChatClient(properties);
86 } 126 }
87 127
88 - public SiliconFlowChatModel buildSiliconFlowChatClient(YudaoAiProperties.SiliconFlowProperties properties) { 128 + public SiliconFlowChatModel buildSiliconFlowChatClient(YudaoAiProperties.SiliconFlow properties) {
89 if (StrUtil.isEmpty(properties.getModel())) { 129 if (StrUtil.isEmpty(properties.getModel())) {
90 properties.setModel(SiliconFlowApiConstants.MODEL_DEFAULT); 130 properties.setModel(SiliconFlowApiConstants.MODEL_DEFAULT);
91 } 131 }
92 - OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()  
93 - .openAiApi(OpenAiApi.builder() 132 + DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
  133 + .deepSeekApi(DeepSeekApi.builder()
94 .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL) 134 .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL)
95 .apiKey(properties.getApiKey()) 135 .apiKey(properties.getApiKey())
96 .build()) 136 .build())
97 - .defaultOptions(OpenAiChatOptions.builder() 137 + .defaultOptions(DeepSeekChatOptions.builder()
98 .model(properties.getModel()) 138 .model(properties.getModel())
99 .temperature(properties.getTemperature()) 139 .temperature(properties.getTemperature())
100 .maxTokens(properties.getMaxTokens()) 140 .maxTokens(properties.getMaxTokens())
@@ -108,11 +148,11 @@ public class AiAutoConfiguration { @@ -108,11 +148,11 @@ public class AiAutoConfiguration {
108 @Bean 148 @Bean
109 @ConditionalOnProperty(value = "yudao.ai.hunyuan.enable", havingValue = "true") 149 @ConditionalOnProperty(value = "yudao.ai.hunyuan.enable", havingValue = "true")
110 public HunYuanChatModel hunYuanChatClient(YudaoAiProperties yudaoAiProperties) { 150 public HunYuanChatModel hunYuanChatClient(YudaoAiProperties yudaoAiProperties) {
111 - YudaoAiProperties.HunYuanProperties properties = yudaoAiProperties.getHunyuan(); 151 + YudaoAiProperties.HunYuan properties = yudaoAiProperties.getHunyuan();
112 return buildHunYuanChatClient(properties); 152 return buildHunYuanChatClient(properties);
113 } 153 }
114 154
115 - public HunYuanChatModel buildHunYuanChatClient(YudaoAiProperties.HunYuanProperties properties) { 155 + public HunYuanChatModel buildHunYuanChatClient(YudaoAiProperties.HunYuan properties) {
116 if (StrUtil.isEmpty(properties.getModel())) { 156 if (StrUtil.isEmpty(properties.getModel())) {
117 properties.setModel(HunYuanChatModel.MODEL_DEFAULT); 157 properties.setModel(HunYuanChatModel.MODEL_DEFAULT);
118 } 158 }
@@ -122,13 +162,14 @@ public class AiAutoConfiguration { @@ -122,13 +162,14 @@ public class AiAutoConfiguration {
122 StrUtil.startWithIgnoreCase(properties.getModel(), "deepseek") ? HunYuanChatModel.DEEP_SEEK_BASE_URL 162 StrUtil.startWithIgnoreCase(properties.getModel(), "deepseek") ? HunYuanChatModel.DEEP_SEEK_BASE_URL
123 : HunYuanChatModel.BASE_URL); 163 : HunYuanChatModel.BASE_URL);
124 } 164 }
125 - // 创建 OpenAiChatModel、HunYuanChatModel 对象  
126 - OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()  
127 - .openAiApi(OpenAiApi.builder() 165 + // 创建 DeepSeekChatModel、HunYuanChatModel 对象
  166 + DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
  167 + .deepSeekApi(DeepSeekApi.builder()
128 .baseUrl(properties.getBaseUrl()) 168 .baseUrl(properties.getBaseUrl())
  169 + .completionsPath(HunYuanChatModel.COMPLETE_PATH)
129 .apiKey(properties.getApiKey()) 170 .apiKey(properties.getApiKey())
130 .build()) 171 .build())
131 - .defaultOptions(OpenAiChatOptions.builder() 172 + .defaultOptions(DeepSeekChatOptions.builder()
132 .model(properties.getModel()) 173 .model(properties.getModel())
133 .temperature(properties.getTemperature()) 174 .temperature(properties.getTemperature())
134 .maxTokens(properties.getMaxTokens()) 175 .maxTokens(properties.getMaxTokens())
@@ -142,25 +183,30 @@ public class AiAutoConfiguration { @@ -142,25 +183,30 @@ public class AiAutoConfiguration {
142 @Bean 183 @Bean
143 @ConditionalOnProperty(value = "yudao.ai.xinghuo.enable", havingValue = "true") 184 @ConditionalOnProperty(value = "yudao.ai.xinghuo.enable", havingValue = "true")
144 public XingHuoChatModel xingHuoChatClient(YudaoAiProperties yudaoAiProperties) { 185 public XingHuoChatModel xingHuoChatClient(YudaoAiProperties yudaoAiProperties) {
145 - YudaoAiProperties.XingHuoProperties properties = yudaoAiProperties.getXinghuo(); 186 + YudaoAiProperties.XingHuo properties = yudaoAiProperties.getXinghuo();
146 return buildXingHuoChatClient(properties); 187 return buildXingHuoChatClient(properties);
147 } 188 }
148 189
149 - public XingHuoChatModel buildXingHuoChatClient(YudaoAiProperties.XingHuoProperties properties) { 190 + public XingHuoChatModel buildXingHuoChatClient(YudaoAiProperties.XingHuo properties) {
150 if (StrUtil.isEmpty(properties.getModel())) { 191 if (StrUtil.isEmpty(properties.getModel())) {
151 properties.setModel(XingHuoChatModel.MODEL_DEFAULT); 192 properties.setModel(XingHuoChatModel.MODEL_DEFAULT);
152 } 193 }
  194 + OpenAiApi.Builder builder = OpenAiApi.builder()
  195 + .baseUrl(XingHuoChatModel.BASE_URL_V1)
  196 + .apiKey(properties.getAppKey() + ":" + properties.getSecretKey());
  197 + if ("x1".equals(properties.getModel())) {
  198 + builder.baseUrl(XingHuoChatModel.BASE_URL_V2)
  199 + .completionsPath(XingHuoChatModel.BASE_COMPLETIONS_PATH_V2);
  200 + }
153 OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() 201 OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
154 - .openAiApi(OpenAiApi.builder()  
155 - .baseUrl(XingHuoChatModel.BASE_URL)  
156 - .apiKey(properties.getAppKey() + ":" + properties.getSecretKey())  
157 - .build()) 202 + .openAiApi(builder.build())
158 .defaultOptions(OpenAiChatOptions.builder() 203 .defaultOptions(OpenAiChatOptions.builder()
159 .model(properties.getModel()) 204 .model(properties.getModel())
160 .temperature(properties.getTemperature()) 205 .temperature(properties.getTemperature())
161 .maxTokens(properties.getMaxTokens()) 206 .maxTokens(properties.getMaxTokens())
162 .topP(properties.getTopP()) 207 .topP(properties.getTopP())
163 .build()) 208 .build())
  209 + // TODO @芋艿:星火的 function call 有 bug,会报 ToolResponseMessage must have an id 错误!!!
164 .toolCallingManager(getToolCallingManager()) 210 .toolCallingManager(getToolCallingManager())
165 .build(); 211 .build();
166 return new XingHuoChatModel(openAiChatModel); 212 return new XingHuoChatModel(openAiChatModel);
@@ -169,11 +215,11 @@ public class AiAutoConfiguration { @@ -169,11 +215,11 @@ public class AiAutoConfiguration {
169 @Bean 215 @Bean
170 @ConditionalOnProperty(value = "yudao.ai.baichuan.enable", havingValue = "true") 216 @ConditionalOnProperty(value = "yudao.ai.baichuan.enable", havingValue = "true")
171 public BaiChuanChatModel baiChuanChatClient(YudaoAiProperties yudaoAiProperties) { 217 public BaiChuanChatModel baiChuanChatClient(YudaoAiProperties yudaoAiProperties) {
172 - YudaoAiProperties.BaiChuanProperties properties = yudaoAiProperties.getBaichuan(); 218 + YudaoAiProperties.BaiChuan properties = yudaoAiProperties.getBaichuan();
173 return buildBaiChuanChatClient(properties); 219 return buildBaiChuanChatClient(properties);
174 } 220 }
175 221
176 - public BaiChuanChatModel buildBaiChuanChatClient(YudaoAiProperties.BaiChuanProperties properties) { 222 + public BaiChuanChatModel buildBaiChuanChatClient(YudaoAiProperties.BaiChuan properties) {
177 if (StrUtil.isEmpty(properties.getModel())) { 223 if (StrUtil.isEmpty(properties.getModel())) {
178 properties.setModel(BaiChuanChatModel.MODEL_DEFAULT); 224 properties.setModel(BaiChuanChatModel.MODEL_DEFAULT);
179 } 225 }
@@ -196,7 +242,7 @@ public class AiAutoConfiguration { @@ -196,7 +242,7 @@ public class AiAutoConfiguration {
196 @Bean 242 @Bean
197 @ConditionalOnProperty(value = "yudao.ai.midjourney.enable", havingValue = "true") 243 @ConditionalOnProperty(value = "yudao.ai.midjourney.enable", havingValue = "true")
198 public MidjourneyApi midjourneyApi(YudaoAiProperties yudaoAiProperties) { 244 public MidjourneyApi midjourneyApi(YudaoAiProperties yudaoAiProperties) {
199 - YudaoAiProperties.MidjourneyProperties config = yudaoAiProperties.getMidjourney(); 245 + YudaoAiProperties.Midjourney config = yudaoAiProperties.getMidjourney();
200 return new MidjourneyApi(config.getBaseUrl(), config.getApiKey(), config.getNotifyUrl()); 246 return new MidjourneyApi(config.getBaseUrl(), config.getApiKey(), config.getNotifyUrl());
201 } 247 }
202 248
@@ -222,4 +268,22 @@ public class AiAutoConfiguration { @@ -222,4 +268,22 @@ public class AiAutoConfiguration {
222 return SpringUtil.getBean(ToolCallingManager.class); 268 return SpringUtil.getBean(ToolCallingManager.class);
223 } 269 }
224 270
  271 + // ========== Web Search 相关 ==========
  272 +
  273 + @Bean
  274 + @ConditionalOnProperty(value = "yudao.ai.web-search.enable", havingValue = "true")
  275 + public AiWebSearchClient webSearchClient(YudaoAiProperties yudaoAiProperties) {
  276 + return new AiBoChaWebSearchClient(yudaoAiProperties.getWebSearch().getApiKey());
  277 + }
  278 +
  279 + // ========== MCP 相关 ==========
  280 +
  281 + /**
  282 + * 参考自 <a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-client-boot-starter-docs.html">MCP Server Boot Starter</>
  283 + */
  284 + @Bean
  285 + public List<ToolCallback> toolCallbacks(PersonService personService) {
  286 + return List.of(ToolCallbacks.from(personService));
  287 + }
  288 +
225 } 289 }
226 \ No newline at end of file 290 \ No newline at end of file
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java
@@ -14,48 +14,66 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @@ -14,48 +14,66 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
14 public class YudaoAiProperties { 14 public class YudaoAiProperties {
15 15
16 /** 16 /**
  17 + * 谷歌 Gemini
  18 + */
  19 + private Gemini gemini;
  20 +
  21 + /**
17 * 字节豆包 22 * 字节豆包
18 */ 23 */
19 - @SuppressWarnings("SpellCheckingInspection")  
20 - private DouBaoProperties doubao; 24 + private DouBao doubao;
21 25
22 /** 26 /**
23 * 腾讯混元 27 * 腾讯混元
24 */ 28 */
25 - @SuppressWarnings("SpellCheckingInspection")  
26 - private HunYuanProperties hunyuan; 29 + private HunYuan hunyuan;
27 30
28 /** 31 /**
29 * 硅基流动 32 * 硅基流动
30 */ 33 */
31 - @SuppressWarnings("SpellCheckingInspection")  
32 - private SiliconFlowProperties siliconflow; 34 + private SiliconFlow siliconflow;
33 35
34 /** 36 /**
35 * 讯飞星火 37 * 讯飞星火
36 */ 38 */
37 - @SuppressWarnings("SpellCheckingInspection")  
38 - private XingHuoProperties xinghuo; 39 + private XingHuo xinghuo;
39 40
40 /** 41 /**
41 * 百川 42 * 百川
42 */ 43 */
43 - @SuppressWarnings("SpellCheckingInspection")  
44 - private BaiChuanProperties baichuan; 44 + private BaiChuan baichuan;
45 45
46 /** 46 /**
47 * Midjourney 绘图 47 * Midjourney 绘图
48 */ 48 */
49 - private MidjourneyProperties midjourney; 49 + private Midjourney midjourney;
50 50
51 /** 51 /**
52 * Suno 音乐 52 * Suno 音乐
53 */ 53 */
54 @SuppressWarnings("SpellCheckingInspection") 54 @SuppressWarnings("SpellCheckingInspection")
55 - private SunoProperties suno; 55 + private Suno suno;
  56 +
  57 + /**
  58 + * 网络搜索
  59 + */
  60 + private WebSearch webSearch;
  61 +
  62 + @Data
  63 + public static class Gemini {
  64 +
  65 + private String enable;
  66 + private String apiKey;
  67 +
  68 + private String model;
  69 + private Double temperature;
  70 + private Integer maxTokens;
  71 + private Double topP;
  72 +
  73 + }
56 74
57 @Data 75 @Data
58 - public static class DouBaoProperties { 76 + public static class DouBao {
59 77
60 private String enable; 78 private String enable;
61 private String apiKey; 79 private String apiKey;
@@ -68,7 +86,7 @@ public class YudaoAiProperties { @@ -68,7 +86,7 @@ public class YudaoAiProperties {
68 } 86 }
69 87
70 @Data 88 @Data
71 - public static class HunYuanProperties { 89 + public static class HunYuan {
72 90
73 private String enable; 91 private String enable;
74 private String baseUrl; 92 private String baseUrl;
@@ -82,7 +100,7 @@ public class YudaoAiProperties { @@ -82,7 +100,7 @@ public class YudaoAiProperties {
82 } 100 }
83 101
84 @Data 102 @Data
85 - public static class SiliconFlowProperties { 103 + public static class SiliconFlow {
86 104
87 private String enable; 105 private String enable;
88 private String apiKey; 106 private String apiKey;
@@ -95,7 +113,7 @@ public class YudaoAiProperties { @@ -95,7 +113,7 @@ public class YudaoAiProperties {
95 } 113 }
96 114
97 @Data 115 @Data
98 - public static class XingHuoProperties { 116 + public static class XingHuo {
99 117
100 private String enable; 118 private String enable;
101 private String appId; 119 private String appId;
@@ -110,7 +128,7 @@ public class YudaoAiProperties { @@ -110,7 +128,7 @@ public class YudaoAiProperties {
110 } 128 }
111 129
112 @Data 130 @Data
113 - public static class BaiChuanProperties { 131 + public static class BaiChuan {
114 132
115 private String enable; 133 private String enable;
116 private String apiKey; 134 private String apiKey;
@@ -123,7 +141,7 @@ public class YudaoAiProperties { @@ -123,7 +141,7 @@ public class YudaoAiProperties {
123 } 141 }
124 142
125 @Data 143 @Data
126 - public static class MidjourneyProperties { 144 + public static class Midjourney {
127 145
128 private String enable; 146 private String enable;
129 private String baseUrl; 147 private String baseUrl;
@@ -134,12 +152,21 @@ public class YudaoAiProperties { @@ -134,12 +152,21 @@ public class YudaoAiProperties {
134 } 152 }
135 153
136 @Data 154 @Data
137 - public static class SunoProperties { 155 + public static class Suno {
138 156
139 - private boolean enable = false; 157 + private boolean enable;
140 158
141 private String baseUrl; 159 private String baseUrl;
142 160
143 } 161 }
144 162
  163 + @Data
  164 + public static class WebSearch {
  165 +
  166 + private boolean enable;
  167 +
  168 + private String apiKey;
  169 +
  170 + }
  171 +
145 } 172 }
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/AiModelFactory.java renamed to yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactory.java
1 -package cn.iocoder.yudao.module.ai.framework.ai.core; 1 +package cn.iocoder.yudao.module.ai.framework.ai.core.model;
2 2
3 import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; 3 import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
4 import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; 4 import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/AiModelFactoryImpl.java renamed to yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java
1 -package cn.iocoder.yudao.module.ai.framework.ai.core; 1 +package cn.iocoder.yudao.module.ai.framework.ai.core.model;
2 2
3 import cn.hutool.core.io.FileUtil; 3 import cn.hutool.core.io.FileUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
@@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.ai.framework.ai.config.AiAutoConfiguration; @@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.ai.framework.ai.config.AiAutoConfiguration;
14 import cn.iocoder.yudao.module.ai.framework.ai.config.YudaoAiProperties; 14 import cn.iocoder.yudao.module.ai.framework.ai.config.YudaoAiProperties;
15 import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; 15 import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;
16 import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel; 16 import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;
  17 +import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel;
17 import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel; 18 import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;
18 import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; 19 import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
19 import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants; 20 import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;
@@ -67,6 +68,7 @@ import org.springframework.ai.minimax.MiniMaxChatOptions; @@ -67,6 +68,7 @@ import org.springframework.ai.minimax.MiniMaxChatOptions;
67 import org.springframework.ai.minimax.MiniMaxEmbeddingModel; 68 import org.springframework.ai.minimax.MiniMaxEmbeddingModel;
68 import org.springframework.ai.minimax.MiniMaxEmbeddingOptions; 69 import org.springframework.ai.minimax.MiniMaxEmbeddingOptions;
69 import org.springframework.ai.minimax.api.MiniMaxApi; 70 import org.springframework.ai.minimax.api.MiniMaxApi;
  71 +import org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration;
70 import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatAutoConfiguration; 72 import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatAutoConfiguration;
71 import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingAutoConfiguration; 73 import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingAutoConfiguration;
72 import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingProperties; 74 import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingProperties;
@@ -93,6 +95,8 @@ import org.springframework.ai.openai.OpenAiImageModel; @@ -93,6 +95,8 @@ import org.springframework.ai.openai.OpenAiImageModel;
93 import org.springframework.ai.openai.api.OpenAiApi; 95 import org.springframework.ai.openai.api.OpenAiApi;
94 import org.springframework.ai.openai.api.OpenAiImageApi; 96 import org.springframework.ai.openai.api.OpenAiImageApi;
95 import org.springframework.ai.openai.api.common.OpenAiApiConstants; 97 import org.springframework.ai.openai.api.common.OpenAiApiConstants;
  98 +import org.springframework.ai.anthropic.AnthropicChatModel;
  99 +import org.springframework.ai.anthropic.api.AnthropicApi;
96 import org.springframework.ai.stabilityai.StabilityAiImageModel; 100 import org.springframework.ai.stabilityai.StabilityAiImageModel;
97 import org.springframework.ai.stabilityai.api.StabilityAiApi; 101 import org.springframework.ai.stabilityai.api.StabilityAiApi;
98 import org.springframework.ai.vectorstore.SimpleVectorStore; 102 import org.springframework.ai.vectorstore.SimpleVectorStore;
@@ -168,6 +172,10 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -168,6 +172,10 @@ public class AiModelFactoryImpl implements AiModelFactory {
168 return buildOpenAiChatModel(apiKey, url); 172 return buildOpenAiChatModel(apiKey, url);
169 case AZURE_OPENAI: 173 case AZURE_OPENAI:
170 return buildAzureOpenAiChatModel(apiKey, url); 174 return buildAzureOpenAiChatModel(apiKey, url);
  175 + case ANTHROPIC:
  176 + return buildAnthropicChatModel(apiKey, url);
  177 + case GEMINI:
  178 + return buildGeminiChatModel(apiKey);
171 case OLLAMA: 179 case OLLAMA:
172 return buildOllamaChatModel(url); 180 return buildOllamaChatModel(url);
173 default: 181 default:
@@ -206,6 +214,10 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -206,6 +214,10 @@ public class AiModelFactoryImpl implements AiModelFactory {
206 return SpringUtil.getBean(OpenAiChatModel.class); 214 return SpringUtil.getBean(OpenAiChatModel.class);
207 case AZURE_OPENAI: 215 case AZURE_OPENAI:
208 return SpringUtil.getBean(AzureOpenAiChatModel.class); 216 return SpringUtil.getBean(AzureOpenAiChatModel.class);
  217 + case ANTHROPIC:
  218 + return SpringUtil.getBean(AnthropicChatModel.class);
  219 + case GEMINI:
  220 + return SpringUtil.getBean(GeminiChatModel.class);
209 case OLLAMA: 221 case OLLAMA:
210 return SpringUtil.getBean(OllamaChatModel.class); 222 return SpringUtil.getBean(OllamaChatModel.class);
211 default: 223 default:
@@ -260,7 +272,7 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -260,7 +272,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
260 String cacheKey = buildClientCacheKey(MidjourneyApi.class, AiPlatformEnum.MIDJOURNEY.getPlatform(), apiKey, 272 String cacheKey = buildClientCacheKey(MidjourneyApi.class, AiPlatformEnum.MIDJOURNEY.getPlatform(), apiKey,
261 url); 273 url);
262 return Singleton.get(cacheKey, (Func0<MidjourneyApi>) () -> { 274 return Singleton.get(cacheKey, (Func0<MidjourneyApi>) () -> {
263 - YudaoAiProperties.MidjourneyProperties properties = SpringUtil.getBean(YudaoAiProperties.class) 275 + YudaoAiProperties.Midjourney properties = SpringUtil.getBean(YudaoAiProperties.class)
264 .getMidjourney(); 276 .getMidjourney();
265 return new MidjourneyApi(url, apiKey, properties.getNotifyUrl()); 277 return new MidjourneyApi(url, apiKey, properties.getNotifyUrl());
266 }); 278 });
@@ -347,7 +359,7 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -347,7 +359,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
347 * 可参考 {@link DashScopeImageAutoConfiguration} 的 dashScopeImageModel 方法 359 * 可参考 {@link DashScopeImageAutoConfiguration} 的 dashScopeImageModel 方法
348 */ 360 */
349 private static DashScopeImageModel buildTongYiImagesModel(String key) { 361 private static DashScopeImageModel buildTongYiImagesModel(String key) {
350 - DashScopeImageApi dashScopeImageApi = new DashScopeImageApi(key); 362 + DashScopeImageApi dashScopeImageApi = DashScopeImageApi.builder().apiKey(key).build();
351 return DashScopeImageModel.builder() 363 return DashScopeImageModel.builder()
352 .dashScopeApi(dashScopeImageApi) 364 .dashScopeApi(dashScopeImageApi)
353 .build(); 365 .build();
@@ -397,7 +409,7 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -397,7 +409,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
397 * 可参考 {@link AiAutoConfiguration#douBaoChatClient(YudaoAiProperties)} 409 * 可参考 {@link AiAutoConfiguration#douBaoChatClient(YudaoAiProperties)}
398 */ 410 */
399 private ChatModel buildDouBaoChatModel(String apiKey) { 411 private ChatModel buildDouBaoChatModel(String apiKey) {
400 - YudaoAiProperties.DouBaoProperties properties = new YudaoAiProperties.DouBaoProperties() 412 + YudaoAiProperties.DouBao properties = new YudaoAiProperties.DouBao()
401 .setApiKey(apiKey); 413 .setApiKey(apiKey);
402 return new AiAutoConfiguration().buildDouBaoChatClient(properties); 414 return new AiAutoConfiguration().buildDouBaoChatClient(properties);
403 } 415 }
@@ -406,7 +418,7 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -406,7 +418,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
406 * 可参考 {@link AiAutoConfiguration#hunYuanChatClient(YudaoAiProperties)} 418 * 可参考 {@link AiAutoConfiguration#hunYuanChatClient(YudaoAiProperties)}
407 */ 419 */
408 private ChatModel buildHunYuanChatModel(String apiKey, String url) { 420 private ChatModel buildHunYuanChatModel(String apiKey, String url) {
409 - YudaoAiProperties.HunYuanProperties properties = new YudaoAiProperties.HunYuanProperties() 421 + YudaoAiProperties.HunYuan properties = new YudaoAiProperties.HunYuan()
410 .setBaseUrl(url).setApiKey(apiKey); 422 .setBaseUrl(url).setApiKey(apiKey);
411 return new AiAutoConfiguration().buildHunYuanChatClient(properties); 423 return new AiAutoConfiguration().buildHunYuanChatClient(properties);
412 } 424 }
@@ -415,7 +427,7 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -415,7 +427,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
415 * 可参考 {@link AiAutoConfiguration#siliconFlowChatClient(YudaoAiProperties)} 427 * 可参考 {@link AiAutoConfiguration#siliconFlowChatClient(YudaoAiProperties)}
416 */ 428 */
417 private ChatModel buildSiliconFlowChatModel(String apiKey) { 429 private ChatModel buildSiliconFlowChatModel(String apiKey) {
418 - YudaoAiProperties.SiliconFlowProperties properties = new YudaoAiProperties.SiliconFlowProperties() 430 + YudaoAiProperties.SiliconFlow properties = new YudaoAiProperties.SiliconFlow()
419 .setApiKey(apiKey); 431 .setApiKey(apiKey);
420 return new AiAutoConfiguration().buildSiliconFlowChatClient(properties); 432 return new AiAutoConfiguration().buildSiliconFlowChatClient(properties);
421 } 433 }
@@ -473,7 +485,7 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -473,7 +485,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
473 private static XingHuoChatModel buildXingHuoChatModel(String key) { 485 private static XingHuoChatModel buildXingHuoChatModel(String key) {
474 List<String> keys = StrUtil.split(key, '|'); 486 List<String> keys = StrUtil.split(key, '|');
475 Assert.equals(keys.size(), 2, "XingHuoChatClient 的密钥需要 (appKey|secretKey) 格式"); 487 Assert.equals(keys.size(), 2, "XingHuoChatClient 的密钥需要 (appKey|secretKey) 格式");
476 - YudaoAiProperties.XingHuoProperties properties = new YudaoAiProperties.XingHuoProperties() 488 + YudaoAiProperties.XingHuo properties = new YudaoAiProperties.XingHuo()
477 .setAppKey(keys.get(0)).setSecretKey(keys.get(1)); 489 .setAppKey(keys.get(0)).setSecretKey(keys.get(1));
478 return new AiAutoConfiguration().buildXingHuoChatClient(properties); 490 return new AiAutoConfiguration().buildXingHuoChatClient(properties);
479 } 491 }
@@ -482,7 +494,7 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -482,7 +494,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
482 * 可参考 {@link AiAutoConfiguration#baiChuanChatClient(YudaoAiProperties)} 494 * 可参考 {@link AiAutoConfiguration#baiChuanChatClient(YudaoAiProperties)}
483 */ 495 */
484 private BaiChuanChatModel buildBaiChuanChatModel(String apiKey) { 496 private BaiChuanChatModel buildBaiChuanChatModel(String apiKey) {
485 - YudaoAiProperties.BaiChuanProperties properties = new YudaoAiProperties.BaiChuanProperties() 497 + YudaoAiProperties.BaiChuan properties = new YudaoAiProperties.BaiChuan()
486 .setApiKey(apiKey); 498 .setApiKey(apiKey);
487 return new AiAutoConfiguration().buildBaiChuanChatClient(properties); 499 return new AiAutoConfiguration().buildBaiChuanChatClient(properties);
488 } 500 }
@@ -513,6 +525,30 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -513,6 +525,30 @@ public class AiModelFactoryImpl implements AiModelFactory {
513 } 525 }
514 526
515 /** 527 /**
  528 + * 可参考 {@link AnthropicChatAutoConfiguration} 的 anthropicApi 方法
  529 + */
  530 + private static AnthropicChatModel buildAnthropicChatModel(String apiKey, String url) {
  531 + AnthropicApi.Builder builder = AnthropicApi.builder().apiKey(apiKey);
  532 + if (StrUtil.isNotEmpty(url)) {
  533 + builder.baseUrl(url);
  534 + }
  535 + AnthropicApi anthropicApi = builder.build();
  536 + return AnthropicChatModel.builder()
  537 + .anthropicApi(anthropicApi)
  538 + .toolCallingManager(getToolCallingManager())
  539 + .build();
  540 + }
  541 +
  542 + /**
  543 + * 可参考 {@link AiAutoConfiguration#buildGeminiChatClient(YudaoAiProperties.Gemini)}
  544 + */
  545 + private static GeminiChatModel buildGeminiChatModel(String apiKey) {
  546 + YudaoAiProperties.Gemini properties = SpringUtil.getBean(YudaoAiProperties.class)
  547 + .getGemini().setApiKey(apiKey);
  548 + return new AiAutoConfiguration().buildGeminiChatClient(properties);
  549 + }
  550 +
  551 + /**
516 * 可参考 {@link OpenAiImageAutoConfiguration} 的 openAiImageModel 方法 552 * 可参考 {@link OpenAiImageAutoConfiguration} 的 openAiImageModel 方法
517 */ 553 */
518 private OpenAiImageModel buildOpenAiImageModel(String openAiToken, String url) { 554 private OpenAiImageModel buildOpenAiImageModel(String openAiToken, String url) {
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/doubao/DouBaoChatModel.java
@@ -6,7 +6,6 @@ import org.springframework.ai.chat.model.ChatModel; @@ -6,7 +6,6 @@ import org.springframework.ai.chat.model.ChatModel;
6 import org.springframework.ai.chat.model.ChatResponse; 6 import org.springframework.ai.chat.model.ChatResponse;
7 import org.springframework.ai.chat.prompt.ChatOptions; 7 import org.springframework.ai.chat.prompt.ChatOptions;
8 import org.springframework.ai.chat.prompt.Prompt; 8 import org.springframework.ai.chat.prompt.Prompt;
9 -import org.springframework.ai.openai.OpenAiChatModel;  
10 import reactor.core.publisher.Flux; 9 import reactor.core.publisher.Flux;
11 10
12 /** 11 /**
@@ -19,13 +18,14 @@ import reactor.core.publisher.Flux; @@ -19,13 +18,14 @@ import reactor.core.publisher.Flux;
19 public class DouBaoChatModel implements ChatModel { 18 public class DouBaoChatModel implements ChatModel {
20 19
21 public static final String BASE_URL = "https://ark.cn-beijing.volces.com/api"; 20 public static final String BASE_URL = "https://ark.cn-beijing.volces.com/api";
  21 + public static final String COMPLETE_PATH = "/v3/chat/completions";
22 22
23 public static final String MODEL_DEFAULT = "doubao-1-5-lite-32k-250115"; 23 public static final String MODEL_DEFAULT = "doubao-1-5-lite-32k-250115";
24 24
25 /** 25 /**
26 * 兼容 OpenAI 接口,进行复用 26 * 兼容 OpenAI 接口,进行复用
27 */ 27 */
28 - private final OpenAiChatModel openAiChatModel; 28 + private final ChatModel openAiChatModel;
29 29
30 @Override 30 @Override
31 public ChatResponse call(Prompt prompt) { 31 public ChatResponse call(Prompt prompt) {
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/gemini/GeminiChatModel.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini;
  2 +
  3 +import lombok.RequiredArgsConstructor;
  4 +import lombok.extern.slf4j.Slf4j;
  5 +import org.springframework.ai.chat.model.ChatModel;
  6 +import org.springframework.ai.chat.model.ChatResponse;
  7 +import org.springframework.ai.chat.prompt.ChatOptions;
  8 +import org.springframework.ai.chat.prompt.Prompt;
  9 +import org.springframework.ai.openai.OpenAiChatModel;
  10 +import reactor.core.publisher.Flux;
  11 +
  12 +/**
  13 + * 谷歌 Gemini {@link ChatModel} 实现类,基于 Google AI Studio 提供的 <a href="https://ai.google.dev/gemini-api/docs/openai">OpenAI 兼容方案</a>
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Slf4j
  18 +@RequiredArgsConstructor
  19 +public class GeminiChatModel implements ChatModel {
  20 +
  21 + public static final String BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai/";
  22 + public static final String COMPLETE_PATH = "/chat/completions";
  23 +
  24 + public static final String MODEL_DEFAULT = "gemini-2.5-flash";
  25 +
  26 + /**
  27 + * 兼容 OpenAI 接口,进行复用
  28 + */
  29 + private final OpenAiChatModel openAiChatModel;
  30 +
  31 + @Override
  32 + public ChatResponse call(Prompt prompt) {
  33 + return openAiChatModel.call(prompt);
  34 + }
  35 +
  36 + @Override
  37 + public Flux<ChatResponse> stream(Prompt prompt) {
  38 + return openAiChatModel.stream(prompt);
  39 + }
  40 +
  41 + @Override
  42 + public ChatOptions getDefaultOptions() {
  43 + return openAiChatModel.getDefaultOptions();
  44 + }
  45 +
  46 +}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/hunyuan/HunYuanChatModel.java
@@ -6,7 +6,6 @@ import org.springframework.ai.chat.model.ChatModel; @@ -6,7 +6,6 @@ import org.springframework.ai.chat.model.ChatModel;
6 import org.springframework.ai.chat.model.ChatResponse; 6 import org.springframework.ai.chat.model.ChatResponse;
7 import org.springframework.ai.chat.prompt.ChatOptions; 7 import org.springframework.ai.chat.prompt.ChatOptions;
8 import org.springframework.ai.chat.prompt.Prompt; 8 import org.springframework.ai.chat.prompt.Prompt;
9 -import org.springframework.ai.openai.OpenAiChatModel;  
10 import reactor.core.publisher.Flux; 9 import reactor.core.publisher.Flux;
11 10
12 /** 11 /**
@@ -22,6 +21,7 @@ import reactor.core.publisher.Flux; @@ -22,6 +21,7 @@ import reactor.core.publisher.Flux;
22 public class HunYuanChatModel implements ChatModel { 21 public class HunYuanChatModel implements ChatModel {
23 22
24 public static final String BASE_URL = "https://api.hunyuan.cloud.tencent.com"; 23 public static final String BASE_URL = "https://api.hunyuan.cloud.tencent.com";
  24 + public static final String COMPLETE_PATH = "/v1/chat/completions";
25 25
26 public static final String MODEL_DEFAULT = "hunyuan-turbo"; 26 public static final String MODEL_DEFAULT = "hunyuan-turbo";
27 27
@@ -32,7 +32,7 @@ public class HunYuanChatModel implements ChatModel { @@ -32,7 +32,7 @@ public class HunYuanChatModel implements ChatModel {
32 /** 32 /**
33 * 兼容 OpenAI 接口,进行复用 33 * 兼容 OpenAI 接口,进行复用
34 */ 34 */
35 - private final OpenAiChatModel openAiChatModel; 35 + private final ChatModel openAiChatModel;
36 36
37 @Override 37 @Override
38 public ChatResponse call(Prompt prompt) { 38 public ChatResponse call(Prompt prompt) {
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java
@@ -23,7 +23,7 @@ public class SiliconFlowChatModel implements ChatModel { @@ -23,7 +23,7 @@ public class SiliconFlowChatModel implements ChatModel {
23 /** 23 /**
24 * 兼容 OpenAI 接口,进行复用 24 * 兼容 OpenAI 接口,进行复用
25 */ 25 */
26 - private final OpenAiChatModel openAiChatModel; 26 + private final ChatModel openAiChatModel;
27 27
28 @Override 28 @Override
29 public ChatResponse call(Prompt prompt) { 29 public ChatResponse call(Prompt prompt) {
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/XingHuoChatModel.java
@@ -6,7 +6,6 @@ import org.springframework.ai.chat.model.ChatModel; @@ -6,7 +6,6 @@ import org.springframework.ai.chat.model.ChatModel;
6 import org.springframework.ai.chat.model.ChatResponse; 6 import org.springframework.ai.chat.model.ChatResponse;
7 import org.springframework.ai.chat.prompt.ChatOptions; 7 import org.springframework.ai.chat.prompt.ChatOptions;
8 import org.springframework.ai.chat.prompt.Prompt; 8 import org.springframework.ai.chat.prompt.Prompt;
9 -import org.springframework.ai.openai.OpenAiChatModel;  
10 import reactor.core.publisher.Flux; 9 import reactor.core.publisher.Flux;
11 10
12 /** 11 /**
@@ -18,28 +17,34 @@ import reactor.core.publisher.Flux; @@ -18,28 +17,34 @@ import reactor.core.publisher.Flux;
18 @RequiredArgsConstructor 17 @RequiredArgsConstructor
19 public class XingHuoChatModel implements ChatModel { 18 public class XingHuoChatModel implements ChatModel {
20 19
21 - public static final String BASE_URL = "https://spark-api-open.xf-yun.com"; 20 + public static final String BASE_URL_V1 = "https://spark-api-open.xf-yun.com";
22 21
23 - public static final String MODEL_DEFAULT = "generalv3.5"; 22 + public static final String BASE_URL_V2 = "https://spark-api-open.xf-yun.com";
  23 + public static final String BASE_COMPLETIONS_PATH_V2 = "/v2/chat/completions";
24 24
25 /** 25 /**
26 - * 兼容 OpenAI 接口,进行复用 26 + * 已知模型名列表:x1、4.0Ultra、generalv3.5、max-32k、generalv3、pro-128k、lite
27 */ 27 */
28 - private final OpenAiChatModel openAiChatModel; 28 + public static final String MODEL_DEFAULT = "4.0Ultra";
  29 +
  30 + /**
  31 + * v1 兼容 OpenAI 接口,进行复用
  32 + */
  33 + private final ChatModel openAiChatModelV1;
29 34
30 @Override 35 @Override
31 public ChatResponse call(Prompt prompt) { 36 public ChatResponse call(Prompt prompt) {
32 - return openAiChatModel.call(prompt); 37 + return openAiChatModelV1.call(prompt);
33 } 38 }
34 39
35 @Override 40 @Override
36 public Flux<ChatResponse> stream(Prompt prompt) { 41 public Flux<ChatResponse> stream(Prompt prompt) {
37 - return openAiChatModel.stream(prompt); 42 + return openAiChatModelV1.stream(prompt);
38 } 43 }
39 44
40 @Override 45 @Override
41 public ChatOptions getDefaultOptions() { 46 public ChatOptions getDefaultOptions() {
42 - return openAiChatModel.getDefaultOptions(); 47 + return openAiChatModelV1.getDefaultOptions();
43 } 48 }
44 49
45 } 50 }
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchClient.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.framework.ai.core.webserch;
  2 +
  3 +/**
  4 + * 网络搜索客户端接口
  5 + *
  6 + * @author 芋道源码
  7 + */
  8 +public interface AiWebSearchClient {
  9 +
  10 + /**
  11 + * 网页搜索
  12 + *
  13 + * @param request 搜索请求
  14 + * @return 搜索结果
  15 + */
  16 + AiWebSearchResponse search(AiWebSearchRequest request);
  17 +
  18 +}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchRequest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.framework.ai.core.webserch;
  2 +
  3 +import jakarta.validation.constraints.Max;
  4 +import jakarta.validation.constraints.Min;
  5 +import jakarta.validation.constraints.NotEmpty;
  6 +import jakarta.validation.constraints.NotNull;
  7 +import lombok.Data;
  8 +
  9 +@Data
  10 +public class AiWebSearchRequest {
  11 +
  12 + /**
  13 + * 用户的搜索词
  14 + */
  15 + @NotEmpty(message = "搜索词不能为空")
  16 + private String query;
  17 +
  18 + /**
  19 + * 是否显示文本摘要
  20 + *
  21 + * true - 显示
  22 + * false - 不显示(默认)
  23 + */
  24 + private Boolean summary;
  25 +
  26 + /**
  27 + * 返回结果的条数
  28 + */
  29 + @NotNull(message = "返回结果条数不能为空")
  30 + @Min(message = "返回结果条数最小为 1", value = 1)
  31 + @Max(message = "返回结果条数最大为 50", value = 50)
  32 + private Integer count;
  33 +
  34 +}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchResponse.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.framework.ai.core.webserch;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.List;
  6 +
  7 +@Data
  8 +public class AiWebSearchResponse {
  9 +
  10 + /**
  11 + * 总数(总共匹配的网页数)
  12 + */
  13 + private Long total;
  14 +
  15 + /**
  16 + * 数据列表
  17 + */
  18 + private List<WebPage> lists;
  19 +
  20 + /**
  21 + * 网页对象
  22 + */
  23 + @Data
  24 + public static class WebPage {
  25 +
  26 + /**
  27 + * 名称
  28 + *
  29 + * 例如说:搜狐网
  30 + */
  31 + private String name;
  32 + /**
  33 + * 图标
  34 + */
  35 + private String icon;
  36 +
  37 + /**
  38 + * 标题
  39 + *
  40 + * 例如说:186页|阿里巴巴:2024年环境、社会和治理(ESG)报告
  41 + */
  42 + private String title;
  43 + /**
  44 + * URL
  45 + *
  46 + * 例如说:https://m.sohu.com/a/815036254_121819701/?pvid=000115_3w_a
  47 + */
  48 + @SuppressWarnings("JavadocLinkAsPlainText")
  49 + private String url;
  50 +
  51 + /**
  52 + * 内容的简短描述
  53 + */
  54 + private String snippet;
  55 + /**
  56 + * 内容的文本摘要
  57 + */
  58 + private String summary;
  59 +
  60 + }
  61 +
  62 +}
0 \ No newline at end of file 63 \ No newline at end of file
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/bocha/AiBoChaWebSearchClient.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
  5 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient;
  7 +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchRequest;
  8 +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;
  9 +import com.fasterxml.jackson.annotation.JsonInclude;
  10 +import lombok.extern.slf4j.Slf4j;
  11 +import org.springframework.core.ParameterizedTypeReference;
  12 +import org.springframework.http.HttpStatusCode;
  13 +import org.springframework.http.MediaType;
  14 +import org.springframework.web.reactive.function.client.ClientResponse;
  15 +import org.springframework.web.reactive.function.client.WebClient;
  16 +import reactor.core.publisher.Mono;
  17 +
  18 +import java.util.List;
  19 +import java.util.function.Function;
  20 +import java.util.function.Predicate;
  21 +
  22 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  23 +
  24 +/**
  25 + * 博查 {@link AiWebSearchClient} 实现类
  26 + *
  27 + * @see <a href="https://open.bochaai.com/overview">博查 AI 开放平台</a>
  28 + *
  29 + * @author 芋道源码
  30 + */
  31 +@Slf4j
  32 +public class AiBoChaWebSearchClient implements AiWebSearchClient {
  33 +
  34 + public static final String BASE_URL = "https://api.bochaai.com";
  35 + private static final String AUTHORIZATION_HEADER = "Authorization";
  36 + private static final String BEARER_PREFIX = "Bearer ";
  37 +
  38 + private final WebClient webClient;
  39 +
  40 + private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();
  41 +
  42 + private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
  43 + reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {
  44 + log.error("[AiBoChaWebSearchClient] 调用失败!请求参数:[{}],响应数据: [{}]", reqParam, responseBody);
  45 + sink.error(new IllegalStateException("[AiBoChaWebSearchClient] 调用失败!"));
  46 + });
  47 +
  48 + public AiBoChaWebSearchClient(String apiKey) {
  49 + this.webClient = WebClient.builder()
  50 + .baseUrl(BASE_URL)
  51 + .defaultHeaders((headers) -> {
  52 + headers.setContentType(MediaType.APPLICATION_JSON);
  53 + headers.add(AUTHORIZATION_HEADER, BEARER_PREFIX + apiKey);
  54 + })
  55 + .build();
  56 + }
  57 +
  58 + @Override
  59 + public AiWebSearchResponse search(AiWebSearchRequest request) {
  60 + // 转换请求参数
  61 + WebSearchRequest webSearchRequest = new WebSearchRequest(
  62 + request.getQuery(),
  63 + request.getSummary(),
  64 + request.getCount()
  65 + );
  66 + // 调用博查 API
  67 + CommonResult<WebSearchResponse> response = this.webClient.post()
  68 + .uri("/v1/web-search")
  69 + .bodyValue(webSearchRequest)
  70 + .retrieve()
  71 + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(webSearchRequest))
  72 + .bodyToMono(new ParameterizedTypeReference<CommonResult<WebSearchResponse>>() {})
  73 + .block();
  74 + if (response == null) {
  75 + throw new IllegalStateException("[search][搜索结果为空]");
  76 + }
  77 + if (response.getData() == null) {
  78 + throw new IllegalStateException(String.format("[search][搜索失败,code = %s, msg = %s]",
  79 + response.getCode(), response.getMsg()));
  80 + }
  81 + WebSearchResponse data = response.getData();
  82 +
  83 + // 转换结果
  84 + AiWebSearchResponse result = new AiWebSearchResponse();
  85 + if (data.webPages() == null || CollUtil.isEmpty(data.webPages().value())) {
  86 + return result.setTotal(0L).setLists(List.of());
  87 + }
  88 + return result.setTotal(data.webPages().totalEstimatedMatches())
  89 + .setLists(convertList(data.webPages().value(), page -> new AiWebSearchResponse.WebPage()
  90 + .setName(page.siteName()).setIcon(page.siteIcon())
  91 + .setTitle(page.name()).setUrl(page.url())
  92 + .setSnippet(page.snippet()).setSummary(page.summary())));
  93 + }
  94 +
  95 + /**
  96 + * 网页搜索请求参数
  97 + */
  98 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  99 + public record WebSearchRequest(
  100 + String query,
  101 + Boolean summary,
  102 + Integer count
  103 + ) {
  104 + public WebSearchRequest {
  105 + Assert.notBlank(query, "query 不能为空");
  106 + }
  107 + }
  108 +
  109 + /**
  110 + * 网页搜索响应
  111 + */
  112 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  113 + public record WebSearchResponse(
  114 + WebSearchWebPages webPages
  115 + ) {
  116 + }
  117 +
  118 + /**
  119 + * 网页搜索结果
  120 + */
  121 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  122 + public record WebSearchWebPages(
  123 + String webSearchUrl,
  124 + Long totalEstimatedMatches,
  125 + List<WebPageValue> value,
  126 + Boolean someResultsRemoved
  127 + ) {
  128 +
  129 + /**
  130 + * 网页结果值
  131 + */
  132 + @JsonInclude(value = JsonInclude.Include.NON_NULL)
  133 + public record WebPageValue(
  134 + String id,
  135 + String name,
  136 + String url,
  137 + String displayUrl,
  138 + String snippet,
  139 + String summary,
  140 + String siteName,
  141 + String siteIcon,
  142 + String datePublished,
  143 + String dateLastCrawled,
  144 + String cachedPageUrl,
  145 + String language,
  146 + Boolean isFamilyFriendly,
  147 + Boolean isNavigational
  148 + ) {
  149 + }
  150 +
  151 + }
  152 +
  153 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/config/SecurityConfiguration.java renamed to yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/security/config/SecurityConfiguration.java
1 -package cn.iocoder.yudao.module.iot.framework.security.config; 1 +package cn.iocoder.yudao.module.ai.framework.security.config;
2 2
3 import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer; 3 import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;
4 -import cn.iocoder.yudao.module.iot.enums.ApiConstants; 4 +import jakarta.annotation.Resource;
  5 +import org.springframework.ai.mcp.server.autoconfigure.McpServerProperties;
5 import org.springframework.context.annotation.Bean; 6 import org.springframework.context.annotation.Bean;
6 import org.springframework.context.annotation.Configuration; 7 import org.springframework.context.annotation.Configuration;
7 import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
8 import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; 9 import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
9 10
  11 +import java.util.Optional;
  12 +
10 /** 13 /**
11 - * IoT 模块的 Security 配置 14 + * AI 模块的 Security 配置
12 */ 15 */
13 -@Configuration(proxyBeanMethods = false, value = "iotSecurityConfiguration") 16 +@Configuration(proxyBeanMethods = false, value = "aiSecurityConfiguration")
14 public class SecurityConfiguration { 17 public class SecurityConfiguration {
15 18
16 - @Bean("iotAuthorizeRequestsCustomizer") 19 + @Resource
  20 + private Optional<McpServerProperties> serverProperties;
  21 +
  22 + @Bean("aiAuthorizeRequestsCustomizer")
17 public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { 23 public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
18 return new AuthorizeRequestsCustomizer() { 24 return new AuthorizeRequestsCustomizer() {
19 25
20 @Override 26 @Override
21 public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) { 27 public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
22 - // RPC 服务的安全配置  
23 - registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); 28 + // MCP Server
  29 + serverProperties.ifPresent(properties -> {
  30 + registry.requestMatchers(properties.getSseEndpoint()).permitAll();
  31 + registry.requestMatchers(properties.getSseMessageEndpoint()).permitAll();
  32 + });
24 } 33 }
25 34
26 }; 35 };
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/core/package-info.java renamed to yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/security/core/package-info.java
1 /** 1 /**
2 * 占位 2 * 占位
3 */ 3 */
4 -package cn.iocoder.yudao.module.iot.framework.security.core; 4 +package cn.iocoder.yudao.module.ai.framework.security.core;
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
1 package cn.iocoder.yudao.module.ai.service.chat; 1 package cn.iocoder.yudao.module.ai.service.chat;
2 2
  3 +import cn.hutool.core.codec.Base64;
3 import cn.hutool.core.collection.CollUtil; 4 import cn.hutool.core.collection.CollUtil;
  5 +import cn.hutool.core.io.file.FileNameUtil;
4 import cn.hutool.core.util.ObjUtil; 6 import cn.hutool.core.util.ObjUtil;
5 import cn.hutool.core.util.StrUtil; 7 import cn.hutool.core.util.StrUtil;
6 -import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;  
7 -import cn.iocoder.yudao.module.ai.util.AiUtils; 8 +import cn.hutool.http.HttpUtil;
8 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 9 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
9 import cn.iocoder.yudao.framework.common.pojo.PageResult; 10 import cn.iocoder.yudao.framework.common.pojo.PageResult;
10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 11 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -21,6 +22,10 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; @@ -21,6 +22,10 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
21 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; 22 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
22 import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper; 23 import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper;
23 import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; 24 import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
  25 +import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
  26 +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient;
  27 +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchRequest;
  28 +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;
24 import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; 29 import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
25 import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; 30 import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
26 import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO; 31 import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
@@ -28,6 +33,10 @@ import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchR @@ -28,6 +33,10 @@ import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchR
28 import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; 33 import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
29 import cn.iocoder.yudao.module.ai.service.model.AiModelService; 34 import cn.iocoder.yudao.module.ai.service.model.AiModelService;
30 import cn.iocoder.yudao.module.ai.service.model.AiToolService; 35 import cn.iocoder.yudao.module.ai.service.model.AiToolService;
  36 +import cn.iocoder.yudao.module.ai.util.AiUtils;
  37 +import cn.iocoder.yudao.module.ai.util.FileTypeUtils;
  38 +import com.google.common.collect.Maps;
  39 +import io.modelcontextprotocol.client.McpSyncClient;
31 import jakarta.annotation.Resource; 40 import jakarta.annotation.Resource;
32 import lombok.extern.slf4j.Slf4j; 41 import lombok.extern.slf4j.Slf4j;
33 import org.springframework.ai.chat.messages.Message; 42 import org.springframework.ai.chat.messages.Message;
@@ -39,6 +48,11 @@ import org.springframework.ai.chat.model.ChatResponse; @@ -39,6 +48,11 @@ import org.springframework.ai.chat.model.ChatResponse;
39 import org.springframework.ai.chat.model.StreamingChatModel; 48 import org.springframework.ai.chat.model.StreamingChatModel;
40 import org.springframework.ai.chat.prompt.ChatOptions; 49 import org.springframework.ai.chat.prompt.ChatOptions;
41 import org.springframework.ai.chat.prompt.Prompt; 50 import org.springframework.ai.chat.prompt.Prompt;
  51 +import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
  52 +import org.springframework.ai.mcp.client.autoconfigure.properties.McpClientCommonProperties;
  53 +import org.springframework.ai.tool.ToolCallback;
  54 +import org.springframework.ai.tool.resolution.ToolCallbackResolver;
  55 +import org.springframework.beans.factory.annotation.Autowired;
42 import org.springframework.stereotype.Service; 56 import org.springframework.stereotype.Service;
43 import org.springframework.transaction.annotation.Transactional; 57 import org.springframework.transaction.annotation.Transactional;
44 import reactor.core.publisher.Flux; 58 import reactor.core.publisher.Flux;
@@ -65,12 +79,31 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_N @@ -65,12 +79,31 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_N
65 public class AiChatMessageServiceImpl implements AiChatMessageService { 79 public class AiChatMessageServiceImpl implements AiChatMessageService {
66 80
67 /** 81 /**
  82 + * 联网搜索的结束数
  83 + */
  84 + private static final Integer WEB_SEARCH_COUNT = 10;
  85 +
  86 + // TODO @芋艿:后续优化下对话的 Prompt 整体结构
  87 +
  88 + /**
68 * 知识库转 {@link UserMessage} 的内容模版 89 * 知识库转 {@link UserMessage} 的内容模版
69 */ 90 */
70 private static final String KNOWLEDGE_USER_MESSAGE_TEMPLATE = "使用 <Reference></Reference> 标记中的内容作为本次对话的参考:\n\n" + 91 private static final String KNOWLEDGE_USER_MESSAGE_TEMPLATE = "使用 <Reference></Reference> 标记中的内容作为本次对话的参考:\n\n" +
71 "%s\n\n" + // 多个 <Reference></Reference> 的拼接 92 "%s\n\n" + // 多个 <Reference></Reference> 的拼接
72 "回答要求:\n- 避免提及你是从 <Reference></Reference> 获取的知识。"; 93 "回答要求:\n- 避免提及你是从 <Reference></Reference> 获取的知识。";
73 94
  95 + private static final String WEB_SEARCH_USER_MESSAGE_TEMPLATE = "使用 <WebSearch></WebSearch> 标记中的内容作为本次对话的参考:\n\n" +
  96 + "%s\n\n" + // 多个 <WebSearch></WebSearch> 的拼接
  97 + "回答要求:\n- 避免提及你是从 <WebSearch></WebSearch> 获取的知识。";
  98 +
  99 + /**
  100 + * 附件转 ${@link UserMessage} 的内容模版
  101 + */
  102 + @SuppressWarnings("TextBlockMigration")
  103 + private static final String Attachment_USER_MESSAGE_TEMPLATE = "使用 <Attachment></Attachment> 标记用户对话上传的附件内容:\n\n" +
  104 + "%s\n\n" + // 多个 <Attachment></Attachment> 的拼接
  105 + "回答要求:\n- 避免提及 <Attachment></Attachment> 附件的编码格式。";
  106 +
74 @Resource 107 @Resource
75 private AiChatMessageMapper chatMessageMapper; 108 private AiChatMessageMapper chatMessageMapper;
76 109
@@ -87,6 +120,21 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -87,6 +120,21 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
87 @Resource 120 @Resource
88 private AiToolService toolService; 121 private AiToolService toolService;
89 122
  123 + @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
  124 + @Autowired(required = false) // 由于 yudao.ai.web-search.enable 配置项,可以关闭 AiWebSearchClient 的功能,所以这里只能不强制注入
  125 + private AiWebSearchClient webSearchClient;
  126 +
  127 + @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
  128 + @Autowired(required = false) // 由于 yudao.ai.mcp.client.enable 配置项,可以关闭 McpSyncClient 的功能,所以这里只能不强制注入
  129 + private List<McpSyncClient> mcpClients;
  130 +
  131 + @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
  132 + @Autowired(required = false) // 由于 yudao.ai.mcp.client.enable 配置项,可以关闭 McpSyncClient 的功能,所以这里只能不强制注入
  133 + private McpClientCommonProperties mcpClientCommonProperties;
  134 +
  135 + @Resource
  136 + private ToolCallbackResolver toolCallbackResolver;
  137 +
90 @Transactional(rollbackFor = Exception.class) 138 @Transactional(rollbackFor = Exception.class)
91 public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) { 139 public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) {
92 // 1.1 校验对话存在 140 // 1.1 校验对话存在
@@ -100,27 +148,35 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -100,27 +148,35 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
100 AiModelDO model = modalService.validateModel(conversation.getModelId()); 148 AiModelDO model = modalService.validateModel(conversation.getModelId());
101 ChatModel chatModel = modalService.getChatModel(model.getId()); 149 ChatModel chatModel = modalService.getChatModel(model.getId());
102 150
103 - // 2. 知识库找回  
104 - List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(), conversation); 151 + // 2.1 知识库召回
  152 + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(
  153 + sendReqVO.getContent(), conversation);
  154 +
  155 + // 2.2 联网搜索
  156 + AiWebSearchResponse webSearchResponse = Boolean.TRUE.equals(sendReqVO.getUseSearch()) && webSearchClient != null ?
  157 + webSearchClient.search(new AiWebSearchRequest().setQuery(sendReqVO.getContent())
  158 + .setSummary(true).setCount(WEB_SEARCH_COUNT)) : null;
105 159
106 // 3. 插入 user 发送消息 160 // 3. 插入 user 发送消息
107 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, 161 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
108 userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(), 162 userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(),
109 - null); 163 + null, sendReqVO.getAttachmentUrls(), null);
110 164
111 - // 3.1 插入 assistant 接收消息 165 + // 4.1 插入 assistant 接收消息
112 AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model, 166 AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
113 userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(), 167 userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(),
114 - knowledgeSegments); 168 + knowledgeSegments, null, webSearchResponse);
115 169
116 - // 3.2 创建 chat 需要的 Prompt  
117 - Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO); 170 + // 4.2 创建 chat 需要的 Prompt
  171 + Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, webSearchResponse, model, sendReqVO);
118 ChatResponse chatResponse = chatModel.call(prompt); 172 ChatResponse chatResponse = chatModel.call(prompt);
119 173
120 - // 3.3 更新响应内容  
121 - String newContent = chatResponse.getResult().getOutput().getText();  
122 - chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(newContent));  
123 - // 3.4 响应结果 174 + // 4.3 更新响应内容
  175 + String newContent = AiUtils.getChatResponseContent(chatResponse);
  176 + String newReasoningContent = AiUtils.getChatResponseReasoningContent(chatResponse);
  177 + chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId())
  178 + .setContent(newContent).setReasoningContent(newReasoningContent));
  179 + // 4.4 响应结果
124 Map<Long, AiKnowledgeDocumentDO> documentMap = knowledgeDocumentService.getKnowledgeDocumentMap( 180 Map<Long, AiKnowledgeDocumentDO> documentMap = knowledgeDocumentService.getKnowledgeDocumentMap(
125 convertSet(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getDocumentId)); 181 convertSet(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getDocumentId));
126 List<AiChatMessageRespVO.KnowledgeSegment> segments = BeanUtils.toBean(knowledgeSegments, 182 List<AiChatMessageRespVO.KnowledgeSegment> segments = BeanUtils.toBean(knowledgeSegments,
@@ -131,7 +187,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -131,7 +187,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
131 return new AiChatMessageSendRespVO() 187 return new AiChatMessageSendRespVO()
132 .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) 188 .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
133 .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class) 189 .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
134 - .setContent(newContent).setSegments(segments)); 190 + .setContent(newContent).setSegments(segments)
  191 + .setWebSearchPages(webSearchResponse != null ? webSearchResponse.getLists() : null));
135 } 192 }
136 193
137 @Override 194 @Override
@@ -148,29 +205,36 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -148,29 +205,36 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
148 AiModelDO model = modalService.validateModel(conversation.getModelId()); 205 AiModelDO model = modalService.validateModel(conversation.getModelId());
149 StreamingChatModel chatModel = modalService.getChatModel(model.getId()); 206 StreamingChatModel chatModel = modalService.getChatModel(model.getId());
150 207
151 - // 2. 知识库找回  
152 - List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(),  
153 - conversation); 208 + // 2.1 知识库找回
  209 + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(
  210 + sendReqVO.getContent(), conversation);
  211 +
  212 + // 2.2 联网搜索
  213 + AiWebSearchResponse webSearchResponse = Boolean.TRUE.equals(sendReqVO.getUseSearch()) && webSearchClient != null ?
  214 + webSearchClient.search(new AiWebSearchRequest().setQuery(sendReqVO.getContent())
  215 + .setSummary(true).setCount(WEB_SEARCH_COUNT)) : null;
154 216
155 // 3. 插入 user 发送消息 217 // 3. 插入 user 发送消息
156 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, 218 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
157 userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(), 219 userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(),
158 - null); 220 + null, sendReqVO.getAttachmentUrls(), null);
159 221
160 // 4.1 插入 assistant 接收消息 222 // 4.1 插入 assistant 接收消息
161 AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model, 223 AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
162 userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(), 224 userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(),
163 - knowledgeSegments); 225 + knowledgeSegments, null, webSearchResponse);
164 226
165 // 4.2 构建 Prompt,并进行调用 227 // 4.2 构建 Prompt,并进行调用
166 - Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO); 228 + Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, webSearchResponse, model, sendReqVO);
167 Flux<ChatResponse> streamResponse = chatModel.stream(prompt); 229 Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
168 230
169 // 4.3 流式返回 231 // 4.3 流式返回
170 StringBuffer contentBuffer = new StringBuffer(); 232 StringBuffer contentBuffer = new StringBuffer();
  233 + StringBuffer reasoningContentBuffer = new StringBuffer();
171 return streamResponse.map(chunk -> { 234 return streamResponse.map(chunk -> {
172 - // 处理知识库的返回,只有首次才有 235 + // 仅首次:返回知识库、联网搜索
173 List<AiChatMessageRespVO.KnowledgeSegment> segments = null; 236 List<AiChatMessageRespVO.KnowledgeSegment> segments = null;
  237 + List<AiWebSearchResponse.WebPage> webSearchPages = null;
174 if (StrUtil.isEmpty(contentBuffer)) { 238 if (StrUtil.isEmpty(contentBuffer)) {
175 Map<Long, AiKnowledgeDocumentDO> documentMap = TenantUtils.executeIgnore(() -> 239 Map<Long, AiKnowledgeDocumentDO> documentMap = TenantUtils.executeIgnore(() ->
176 knowledgeDocumentService.getKnowledgeDocumentMap( 240 knowledgeDocumentService.getKnowledgeDocumentMap(
@@ -179,24 +243,56 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -179,24 +243,56 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
179 AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId()); 243 AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());
180 segment.setDocumentName(document != null ? document.getName() : null); 244 segment.setDocumentName(document != null ? document.getName() : null);
181 }); 245 });
  246 + if (webSearchResponse != null) {
  247 + webSearchPages = webSearchResponse.getLists();
  248 + }
182 } 249 }
183 // 响应结果 250 // 响应结果
184 - String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;  
185 - newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况  
186 - contentBuffer.append(newContent); 251 + String newContent = AiUtils.getChatResponseContent(chunk);
  252 + String newReasoningContent = AiUtils.getChatResponseReasoningContent(chunk);
  253 + if (StrUtil.isNotEmpty(newContent)) {
  254 + contentBuffer.append(newContent);
  255 + }
  256 + if (StrUtil.isNotEmpty(newReasoningContent)) {
  257 + reasoningContentBuffer.append(newReasoningContent);
  258 + }
187 return success(new AiChatMessageSendRespVO() 259 return success(new AiChatMessageSendRespVO()
188 .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) 260 .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
189 .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class) 261 .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
190 - .setContent(newContent).setSegments(segments))); 262 + .setContent(StrUtil.nullToDefault(newContent, "")) // 避免 null 的 情况
  263 + .setReasoningContent(StrUtil.nullToDefault(newReasoningContent, "")) // 避免 null 的 情况
  264 + .setSegments(segments).setWebSearchPages(webSearchPages))); // 知识库 + 联网搜索
191 }).doOnComplete(() -> { 265 }).doOnComplete(() -> {
192 // 忽略租户,因为 Flux 异步无法透传租户 266 // 忽略租户,因为 Flux 异步无法透传租户
193 TenantUtils.executeIgnore(() -> chatMessageMapper.updateById( 267 TenantUtils.executeIgnore(() -> chatMessageMapper.updateById(
194 - new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString()))); 268 + new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString())
  269 + .setReasoningContent(reasoningContentBuffer.toString())));
195 }).doOnError(throwable -> { 270 }).doOnError(throwable -> {
196 log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable); 271 log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable);
197 // 忽略租户,因为 Flux 异步无法透传租户 272 // 忽略租户,因为 Flux 异步无法透传租户
198 - TenantUtils.executeIgnore(() -> chatMessageMapper.updateById(  
199 - new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage()))); 273 + TenantUtils.executeIgnore(() -> {
  274 + // 如果有内容,则更新内容
  275 + if (StrUtil.isNotEmpty(contentBuffer)) {
  276 + chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId())
  277 + .setContent(contentBuffer.toString()).setReasoningContent(reasoningContentBuffer.toString()));
  278 + } else {
  279 + // 否则,则进行删除
  280 + chatMessageMapper.deleteById(assistantMessage.getId());
  281 + }
  282 + });
  283 + }).doOnCancel(() -> {
  284 + log.info("[sendChatMessageStream][userId({}) sendReqVO({}) 取消请求]", userId, sendReqVO);
  285 + // 忽略租户,因为 Flux 异步无法透传租户
  286 + TenantUtils.executeIgnore(() -> {
  287 + // 如果有内容,则更新内容
  288 + if (StrUtil.isNotEmpty(contentBuffer)) {
  289 + chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId())
  290 + .setContent(contentBuffer.toString()).setReasoningContent(reasoningContentBuffer.toString()));
  291 + } else {
  292 + // 否则,则进行删除
  293 + chatMessageMapper.deleteById(assistantMessage.getId());
  294 + }
  295 + });
200 }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR))); 296 }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR)));
201 } 297 }
202 298
@@ -211,7 +307,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -211,7 +307,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
211 return Collections.emptyList(); 307 return Collections.emptyList();
212 } 308 }
213 309
214 - // 2. 遍历找回 310 + // 2. 遍历召回
215 List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = new ArrayList<>(); 311 List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = new ArrayList<>();
216 for (Long knowledgeId : role.getKnowledgeIds()) { 312 for (Long knowledgeId : role.getKnowledgeIds()) {
217 knowledgeSegments.addAll(knowledgeSegmentService.searchKnowledgeSegment(new AiKnowledgeSegmentSearchReqBO() 313 knowledgeSegments.addAll(knowledgeSegmentService.searchKnowledgeSegment(new AiKnowledgeSegmentSearchReqBO()
@@ -222,6 +318,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -222,6 +318,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
222 318
223 private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages, 319 private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,
224 List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments, 320 List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments,
  321 + AiWebSearchResponse webSearchResponse,
225 AiModelDO model, AiChatMessageSendReqVO sendReqVO) { 322 AiModelDO model, AiChatMessageSendReqVO sendReqVO) {
226 List<Message> chatMessages = new ArrayList<>(); 323 List<Message> chatMessages = new ArrayList<>();
227 // 1.1 System Context 角色设定 324 // 1.1 System Context 角色设定
@@ -231,8 +328,14 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -231,8 +328,14 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
231 328
232 // 1.2 历史 history message 历史消息 329 // 1.2 历史 history message 历史消息
233 List<AiChatMessageDO> contextMessages = filterContextMessages(messages, conversation, sendReqVO); 330 List<AiChatMessageDO> contextMessages = filterContextMessages(messages, conversation, sendReqVO);
234 - contextMessages  
235 - .forEach(message -> chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent()))); 331 + contextMessages.forEach(message -> {
  332 + chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent()));
  333 + UserMessage attachmentUserMessage = buildAttachmentUserMessage(message.getAttachmentUrls());
  334 + if (attachmentUserMessage != null) {
  335 + chatMessages.add(attachmentUserMessage);
  336 + }
  337 + // TODO @芋艿:历史的知识库;历史的搜索,要不要拼接?
  338 + });
236 339
237 // 1.3 当前 user message 新发送消息 340 // 1.3 当前 user message 新发送消息
238 chatMessages.add(new UserMessage(sendReqVO.getContent())); 341 chatMessages.add(new UserMessage(sendReqVO.getContent()));
@@ -245,23 +348,76 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -245,23 +348,76 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
245 chatMessages.add(new UserMessage(String.format(KNOWLEDGE_USER_MESSAGE_TEMPLATE, reference))); 348 chatMessages.add(new UserMessage(String.format(KNOWLEDGE_USER_MESSAGE_TEMPLATE, reference)));
246 } 349 }
247 350
248 - // 2.1 查询 tool 工具  
249 - Set<String> toolNames = null;  
250 - Map<String,Object> toolContext = Map.of();  
251 - if (conversation.getRoleId() != null) {  
252 - AiChatRoleDO chatRole = chatRoleService.getChatRole(conversation.getRoleId());  
253 - if (chatRole != null && CollUtil.isNotEmpty(chatRole.getToolIds())) {  
254 - toolNames = convertSet(toolService.getToolList(chatRole.getToolIds()), AiToolDO::getName);  
255 - toolContext = AiUtils.buildCommonToolContext(); 351 + // 1.5 联网搜索,通过 UserMessage 实现
  352 + if (webSearchResponse != null && CollUtil.isNotEmpty(webSearchResponse.getLists())) {
  353 + String webSearch = webSearchResponse.getLists().stream()
  354 + .map(page -> {
  355 + String summary = StrUtil.isNotEmpty(page.getSummary()) ?
  356 + "\nSummary: " + page.getSummary() : "";
  357 + return "<WebSearch title=\"" + page.getTitle() + "\" url=\"" + page.getUrl() + "\">"
  358 + + StrUtil.blankToDefault(page.getSummary(), page.getSnippet()) + "</WebSearch>";
  359 + })
  360 + .collect(Collectors.joining("\n\n"));
  361 + chatMessages.add(new UserMessage(String.format(WEB_SEARCH_USER_MESSAGE_TEMPLATE, webSearch)));
  362 + }
  363 +
  364 + // 1.6 附件,通过 UserMessage 实现
  365 + if (CollUtil.isNotEmpty(sendReqVO.getAttachmentUrls())) {
  366 + UserMessage attachmentUserMessage = buildAttachmentUserMessage(sendReqVO.getAttachmentUrls());
  367 + if (attachmentUserMessage != null) {
  368 + chatMessages.add(attachmentUserMessage);
256 } 369 }
257 } 370 }
  371 +
  372 + // 2.1 查询 tool 工具
  373 + List<ToolCallback> toolCallbacks = getToolCallbackListByRoleId(conversation.getRoleId());
  374 + Map<String,Object> toolContext = CollUtil.isNotEmpty(toolCallbacks) ? AiUtils.buildCommonToolContext()
  375 + : Map.of();
258 // 2.2 构建 ChatOptions 对象 376 // 2.2 构建 ChatOptions 对象
259 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); 377 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
260 ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), 378 ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(),
261 - conversation.getTemperature(), conversation.getMaxTokens(), toolNames, toolContext); 379 + conversation.getTemperature(), conversation.getMaxTokens(),
  380 + toolCallbacks, toolContext);
262 return new Prompt(chatMessages, chatOptions); 381 return new Prompt(chatMessages, chatOptions);
263 } 382 }
264 383
  384 + private List<ToolCallback> getToolCallbackListByRoleId(Long roleId) {
  385 + if (roleId == null) {
  386 + return null;
  387 + }
  388 + AiChatRoleDO chatRole = chatRoleService.getChatRole(roleId);
  389 + if (chatRole == null) {
  390 + return null;
  391 + }
  392 + List<ToolCallback> toolCallbacks = new ArrayList<>();
  393 + // 1. 通过 toolIds
  394 + if (CollUtil.isNotEmpty(chatRole.getToolIds())) {
  395 + Set<String> toolNames = convertSet(toolService.getToolList(chatRole.getToolIds()), AiToolDO::getName);
  396 + toolNames.forEach(toolName -> {
  397 + ToolCallback toolCallback = toolCallbackResolver.resolve(toolName);
  398 + if (toolCallback != null) {
  399 + toolCallbacks.add(toolCallback);
  400 + }
  401 + });
  402 + }
  403 + // 2. 通过 mcpClients
  404 + if (CollUtil.isNotEmpty(mcpClients) && CollUtil.isNotEmpty(chatRole.getMcpClientNames())) {
  405 + chatRole.getMcpClientNames().forEach(mcpClientName -> {
  406 + // 2.1 标准化名字,参考 McpClientAutoConfiguration 的 connectedClientName 方法
  407 + String finalMcpClientName = mcpClientCommonProperties.getName() + " - " + mcpClientName;
  408 + // 2.2 匹配对应的 McpSyncClient
  409 + mcpClients.forEach(mcpClient -> {
  410 + if (ObjUtil.notEqual(mcpClient.getClientInfo().name(), finalMcpClientName)) {
  411 + return;
  412 + }
  413 + ToolCallback[] mcpToolCallBacks = new SyncMcpToolCallbackProvider(mcpClient).getToolCallbacks();
  414 + CollUtil.addAll(toolCallbacks, mcpToolCallBacks);
  415 + });
  416 + });
  417 + }
  418 + return toolCallbacks;
  419 + }
  420 +
265 /** 421 /**
266 * 从历史消息中,获得倒序的 n 组消息作为消息上下文 422 * 从历史消息中,获得倒序的 n 组消息作为消息上下文
267 * <p> 423 * <p>
@@ -302,14 +458,56 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -302,14 +458,56 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
302 return contextMessages; 458 return contextMessages;
303 } 459 }
304 460
  461 + private UserMessage buildAttachmentUserMessage(List<String> attachmentUrls) {
  462 + if (CollUtil.isEmpty(attachmentUrls)) {
  463 + return null;
  464 + }
  465 + // 读取文件内容
  466 + Map<String, String> attachmentContents = Maps.newLinkedHashMapWithExpectedSize(attachmentUrls.size());
  467 + for (String attachmentUrl : attachmentUrls) {
  468 + try {
  469 + String name = FileNameUtil.getName(attachmentUrl);
  470 + String mineType = FileTypeUtils.getMineType(name);
  471 + String content;
  472 + if (FileTypeUtils.isImage(mineType)) {
  473 + // 特殊:图片则转为 Base64
  474 + byte[] bytes = HttpUtil.downloadBytes(attachmentUrl);
  475 + content = Base64.encode(bytes);
  476 + } else {
  477 + content = knowledgeDocumentService.readUrl(attachmentUrl);
  478 + }
  479 + if (StrUtil.isNotEmpty(content)) {
  480 + attachmentContents.put(name, content);
  481 + }
  482 + } catch (Exception e) {
  483 + log.error("[buildAttachmentUserMessage][读取附件({}) 发生异常]", attachmentUrl, e);
  484 + }
  485 + }
  486 + if (CollUtil.isEmpty(attachmentContents)) {
  487 + return null;
  488 + }
  489 +
  490 + // 拼接 UserMessage 消息
  491 + String attachment = attachmentContents.entrySet().stream()
  492 + .map(entry -> "<Attachment name=\"" + entry.getKey() + "\">" + entry.getValue() + "</Attachment>")
  493 + .collect(Collectors.joining("\n\n"));
  494 + return new UserMessage(String.format(Attachment_USER_MESSAGE_TEMPLATE, attachment));
  495 + }
  496 +
305 private AiChatMessageDO createChatMessage(Long conversationId, Long replyId, 497 private AiChatMessageDO createChatMessage(Long conversationId, Long replyId,
306 - AiModelDO model, Long userId, Long roleId,  
307 - MessageType messageType, String content, Boolean useContext,  
308 - List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments) { 498 + AiModelDO model, Long userId, Long roleId,
  499 + MessageType messageType, String content, Boolean useContext,
  500 + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments,
  501 + List<String> attachmentUrls,
  502 + AiWebSearchResponse webSearchResponse) {
309 AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId) 503 AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId)
310 .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId) 504 .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId)
311 .setType(messageType.getValue()).setContent(content).setUseContext(useContext) 505 .setType(messageType.getValue()).setContent(content).setUseContext(useContext)
312 - .setSegmentIds(convertList(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getId)); 506 + .setSegmentIds(convertList(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getId))
  507 + .setAttachmentUrls(attachmentUrls);
  508 + if (webSearchResponse != null) {
  509 + message.setWebSearchPages(webSearchResponse.getLists());
  510 + }
313 message.setCreateTime(LocalDateTime.now()); 511 message.setCreateTime(LocalDateTime.now());
314 chatMessageMapper.insert(message); 512 chatMessageMapper.insert(message);
315 return message; 513 return message;
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
@@ -18,6 +18,10 @@ import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; @@ -18,6 +18,10 @@ import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;
18 import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO; 18 import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
19 import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO; 19 import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
20 import cn.iocoder.yudao.module.ai.service.model.AiModelService; 20 import cn.iocoder.yudao.module.ai.service.model.AiModelService;
  21 +import com.alibaba.cloud.ai.dashscope.rerank.DashScopeRerankOptions;
  22 +import com.alibaba.cloud.ai.model.RerankModel;
  23 +import com.alibaba.cloud.ai.model.RerankRequest;
  24 +import com.alibaba.cloud.ai.model.RerankResponse;
21 import jakarta.annotation.Resource; 25 import jakarta.annotation.Resource;
22 import lombok.extern.slf4j.Slf4j; 26 import lombok.extern.slf4j.Slf4j;
23 import org.springframework.ai.document.Document; 27 import org.springframework.ai.document.Document;
@@ -27,6 +31,7 @@ import org.springframework.ai.transformer.splitter.TokenTextSplitter; @@ -27,6 +31,7 @@ import org.springframework.ai.transformer.splitter.TokenTextSplitter;
27 import org.springframework.ai.vectorstore.SearchRequest; 31 import org.springframework.ai.vectorstore.SearchRequest;
28 import org.springframework.ai.vectorstore.VectorStore; 32 import org.springframework.ai.vectorstore.VectorStore;
29 import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder; 33 import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
  34 +import org.springframework.beans.factory.annotation.Autowired;
30 import org.springframework.context.annotation.Lazy; 35 import org.springframework.context.annotation.Lazy;
31 import org.springframework.stereotype.Service; 36 import org.springframework.stereotype.Service;
32 37
@@ -36,6 +41,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU @@ -36,6 +41,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
36 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 41 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
37 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG; 42 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG;
38 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS; 43 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS;
  44 +import static org.springframework.ai.vectorstore.SearchRequest.SIMILARITY_THRESHOLD_ACCEPT_ALL;
39 45
40 /** 46 /**
41 * AI 知识库分片 Service 实现类 47 * AI 知识库分片 Service 实现类
@@ -55,6 +61,11 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService @@ -55,6 +61,11 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
55 VECTOR_STORE_METADATA_DOCUMENT_ID, String.class, 61 VECTOR_STORE_METADATA_DOCUMENT_ID, String.class,
56 VECTOR_STORE_METADATA_SEGMENT_ID, String.class); 62 VECTOR_STORE_METADATA_SEGMENT_ID, String.class);
57 63
  64 + /**
  65 + * Rerank 在向量检索时,检索数量 * 该系数,目的是为了提升 Rerank 的效果
  66 + */
  67 + private static final Integer RERANK_RETRIEVAL_FACTOR = 4;
  68 +
58 @Resource 69 @Resource
59 private AiKnowledgeSegmentMapper segmentMapper; 70 private AiKnowledgeSegmentMapper segmentMapper;
60 71
@@ -69,6 +80,9 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService @@ -69,6 +80,9 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
69 @Resource 80 @Resource
70 private TokenCountEstimator tokenCountEstimator; 81 private TokenCountEstimator tokenCountEstimator;
71 82
  83 + @Autowired(required = false) // 由于 spring.ai.model.rerank 配置项,可以关闭 RerankModel 的功能,所以这里只能不强制注入
  84 + private RerankModel rerankModel;
  85 +
72 @Override 86 @Override
73 public PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) { 87 public PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) {
74 return segmentMapper.selectPage(pageReqVO); 88 return segmentMapper.selectPage(pageReqVO);
@@ -211,28 +225,16 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService @@ -211,28 +225,16 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
211 // 1. 校验 225 // 1. 校验
212 AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqBO.getKnowledgeId()); 226 AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqBO.getKnowledgeId());
213 227
214 - // 2.1 向量检索  
215 - VectorStore vectorStore = getVectorStoreById(knowledge);  
216 - List<Document> documents = vectorStore.similaritySearch(SearchRequest.builder()  
217 - .query(reqBO.getContent())  
218 - .topK(ObjUtil.defaultIfNull(reqBO.getTopK(), knowledge.getTopK()))  
219 - .similarityThreshold(  
220 - ObjUtil.defaultIfNull(reqBO.getSimilarityThreshold(), knowledge.getSimilarityThreshold()))  
221 - .filterExpression(new FilterExpressionBuilder()  
222 - .eq(VECTOR_STORE_METADATA_KNOWLEDGE_ID, reqBO.getKnowledgeId().toString())  
223 - .build())  
224 - .build());  
225 - if (CollUtil.isEmpty(documents)) {  
226 - return ListUtil.empty();  
227 - }  
228 - // 2.2 段落召回 228 + // 2. 检索
  229 + List<Document> documents = searchDocument(knowledge, reqBO);
  230 +
  231 + // 3.1 段落召回
229 List<AiKnowledgeSegmentDO> segments = segmentMapper 232 List<AiKnowledgeSegmentDO> segments = segmentMapper
230 .selectListByVectorIds(convertList(documents, Document::getId)); 233 .selectListByVectorIds(convertList(documents, Document::getId));
231 if (CollUtil.isEmpty(segments)) { 234 if (CollUtil.isEmpty(segments)) {
232 return ListUtil.empty(); 235 return ListUtil.empty();
233 } 236 }
234 -  
235 - // 3. 增加召回次数 237 + // 3.2 增加召回次数
236 segmentMapper.updateRetrievalCountIncrByIds(convertList(segments, AiKnowledgeSegmentDO::getId)); 238 segmentMapper.updateRetrievalCountIncrByIds(convertList(segments, AiKnowledgeSegmentDO::getId));
237 239
238 // 4. 构建结果 240 // 4. 构建结果
@@ -249,6 +251,42 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService @@ -249,6 +251,42 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
249 return result; 251 return result;
250 } 252 }
251 253
  254 + /**
  255 + * 基于 Embedding + Rerank Model,检索知识库中的文档
  256 + *
  257 + * @param knowledge 知识库
  258 + * @param reqBO 检索请求
  259 + * @return 文档列表
  260 + */
  261 + private List<Document> searchDocument(AiKnowledgeDO knowledge, AiKnowledgeSegmentSearchReqBO reqBO) {
  262 + VectorStore vectorStore = getVectorStoreById(knowledge);
  263 + Integer topK = ObjUtil.defaultIfNull(reqBO.getTopK(), knowledge.getTopK());
  264 + Double similarityThreshold = ObjUtil.defaultIfNull(reqBO.getSimilarityThreshold(), knowledge.getSimilarityThreshold());
  265 +
  266 + // 1. 向量检索
  267 + int searchTopK = rerankModel != null ? topK * RERANK_RETRIEVAL_FACTOR : topK;
  268 + double searchSimilarityThreshold = rerankModel != null ? SIMILARITY_THRESHOLD_ACCEPT_ALL : similarityThreshold;
  269 + SearchRequest.Builder searchRequestBuilder = SearchRequest.builder()
  270 + .query(reqBO.getContent())
  271 + .topK(searchTopK).similarityThreshold(searchSimilarityThreshold)
  272 + .filterExpression(new FilterExpressionBuilder()
  273 + .eq(VECTOR_STORE_METADATA_KNOWLEDGE_ID, reqBO.getKnowledgeId().toString()).build());
  274 + List<Document> documents = vectorStore.similaritySearch(searchRequestBuilder.build());
  275 + if (CollUtil.isEmpty(documents)) {
  276 + return documents;
  277 + }
  278 +
  279 + // 2. Rerank 重排序
  280 + if (rerankModel != null) {
  281 + RerankResponse rerankResponse = rerankModel.call(new RerankRequest(reqBO.getContent(), documents,
  282 + DashScopeRerankOptions.builder().withTopN(topK).build()));
  283 + documents = convertList(rerankResponse.getResults(),
  284 + documentWithScore -> documentWithScore.getScore() >= similarityThreshold
  285 + ? documentWithScore.getOutput() : null);
  286 + }
  287 + return documents;
  288 + }
  289 +
252 @Override 290 @Override
253 public List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens) { 291 public List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens) {
254 // 1. 读取 URL 内容 292 // 1. 读取 URL 内容
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java
1 package cn.iocoder.yudao.module.ai.service.model; 1 package cn.iocoder.yudao.module.ai.service.model;
2 2
3 import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; 3 import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
4 -import cn.iocoder.yudao.module.ai.framework.ai.core.AiModelFactory; 4 +import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactory;
5 import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; 5 import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
6 import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi; 6 import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolServiceImpl.java
1 package cn.iocoder.yudao.module.ai.service.model; 1 package cn.iocoder.yudao.module.ai.service.model;
2 2
3 -import cn.hutool.extra.spring.SpringUtil;  
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO; 5 import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
@@ -8,7 +7,8 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO @@ -8,7 +7,8 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO
8 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; 7 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
9 import cn.iocoder.yudao.module.ai.dal.mysql.model.AiToolMapper; 8 import cn.iocoder.yudao.module.ai.dal.mysql.model.AiToolMapper;
10 import jakarta.annotation.Resource; 9 import jakarta.annotation.Resource;
11 -import org.springframework.beans.factory.NoSuchBeanDefinitionException; 10 +import org.springframework.ai.tool.ToolCallback;
  11 +import org.springframework.ai.tool.resolution.ToolCallbackResolver;
12 import org.springframework.stereotype.Service; 12 import org.springframework.stereotype.Service;
13 import org.springframework.validation.annotation.Validated; 13 import org.springframework.validation.annotation.Validated;
14 14
@@ -31,6 +31,9 @@ public class AiToolServiceImpl implements AiToolService { @@ -31,6 +31,9 @@ public class AiToolServiceImpl implements AiToolService {
31 @Resource 31 @Resource
32 private AiToolMapper toolMapper; 32 private AiToolMapper toolMapper;
33 33
  34 + @Resource
  35 + private ToolCallbackResolver toolCallbackResolver;
  36 +
34 @Override 37 @Override
35 public Long createTool(AiToolSaveReqVO createReqVO) { 38 public Long createTool(AiToolSaveReqVO createReqVO) {
36 // 校验名称是否存在 39 // 校验名称是否存在
@@ -70,9 +73,8 @@ public class AiToolServiceImpl implements AiToolService { @@ -70,9 +73,8 @@ public class AiToolServiceImpl implements AiToolService {
70 } 73 }
71 74
72 private void validateToolNameExists(String name) { 75 private void validateToolNameExists(String name) {
73 - try {  
74 - SpringUtil.getBean(name);  
75 - } catch (NoSuchBeanDefinitionException e) { 76 + ToolCallback toolCallback = toolCallbackResolver.resolve(name);
  77 + if (toolCallback == null) {
76 throw exception(TOOL_NAME_NOT_EXISTS, name); 78 throw exception(TOOL_NAME_NOT_EXISTS, name);
77 } 79 }
78 } 80 }
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/DirectoryListToolFunction.java renamed to yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/DirectoryListToolFunction.java
1 -package cn.iocoder.yudao.module.ai.service.model.tool; 1 +package cn.iocoder.yudao.module.ai.tool.function;
2 2
3 import cn.hutool.core.date.LocalDateTimeUtil; 3 import cn.hutool.core.date.LocalDateTimeUtil;
4 import cn.hutool.core.io.FileUtil; 4 import cn.hutool.core.io.FileUtil;
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/UserProfileQueryToolFunction.java renamed to yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/UserProfileQueryToolFunction.java
1 -package cn.iocoder.yudao.module.ai.service.model.tool; 1 +package cn.iocoder.yudao.module.ai.tool.function;
2 2
3 import cn.iocoder.yudao.module.ai.util.AiUtils; 3 import cn.iocoder.yudao.module.ai.util.AiUtils;
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/WeatherQueryToolFunction.java renamed to yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/WeatherQueryToolFunction.java
1 -package cn.iocoder.yudao.module.ai.service.model.tool; 1 +package cn.iocoder.yudao.module.ai.tool.function;
2 2
3 import cn.hutool.core.date.LocalDateTimeUtil; 3 import cn.hutool.core.date.LocalDateTimeUtil;
4 import cn.hutool.core.util.RandomUtil; 4 import cn.hutool.core.util.RandomUtil;
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/package-info.java 0 → 100644
  1 +/**
  2 + * 参考 <a href="https://docs.spring.io/spring-ai/reference/api/tools.html#_methods_as_tools">Tool Calling —— Methods as Tools</a>
  3 + */
  4 +package cn.iocoder.yudao.module.ai.tool.function;
0 \ No newline at end of file 5 \ No newline at end of file
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/Person.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.tool.method;
  2 +
  3 +/**
  4 + * 来自 Spring AI 官方文档
  5 + *
  6 + * Represents a person with basic information.
  7 + * This is an immutable record.
  8 + */
  9 +public record Person(
  10 + int id,
  11 + String firstName,
  12 + String lastName,
  13 + String email,
  14 + String sex,
  15 + String ipAddress,
  16 + String jobTitle,
  17 + int age
  18 +) {
  19 +}
0 \ No newline at end of file 20 \ No newline at end of file
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/PersonService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.tool.method;
  2 +
  3 +import java.util.List;
  4 +import java.util.Optional;
  5 +
  6 +/**
  7 + * 来自 Spring AI 官方文档
  8 + *
  9 + * Service interface for managing Person data.
  10 + * Defines the contract for CRUD operations and search/filter functionalities.
  11 + */
  12 +public interface PersonService {
  13 +
  14 + /**
  15 + * Creates a new Person record.
  16 + * Assigns a unique ID to the person and stores it.
  17 + *
  18 + * @param personData The data for the new person (ID field is ignored). Must not be null.
  19 + * @return The created Person record, including the generated ID.
  20 + */
  21 + Person createPerson(Person personData);
  22 +
  23 + /**
  24 + * Retrieves a Person by their unique ID.
  25 + *
  26 + * @param id The ID of the person to retrieve.
  27 + * @return An Optional containing the found Person, or an empty Optional if not found.
  28 + */
  29 + Optional<Person> getPersonById(int id);
  30 +
  31 + /**
  32 + * Retrieves all Person records currently stored.
  33 + *
  34 + * @return An unmodifiable List containing all Persons. Returns an empty list if none exist.
  35 + */
  36 + List<Person> getAllPersons();
  37 +
  38 + /**
  39 + * Updates an existing Person record identified by ID.
  40 + * Replaces the existing data with the provided data, keeping the original ID.
  41 + *
  42 + * @param id The ID of the person to update.
  43 + * @param updatedPersonData The new data for the person (ID field is ignored). Must not be null.
  44 + * @return true if the person was found and updated, false otherwise.
  45 + */
  46 + boolean updatePerson(int id, Person updatedPersonData);
  47 +
  48 + /**
  49 + * Deletes a Person record identified by ID.
  50 + *
  51 + * @param id The ID of the person to delete.
  52 + * @return true if the person was found and deleted, false otherwise.
  53 + */
  54 + boolean deletePerson(int id);
  55 +
  56 + /**
  57 + * Searches for Persons whose job title contains the given query string (case-insensitive).
  58 + *
  59 + * @param jobTitleQuery The string to search for within job titles. Can be null or blank.
  60 + * @return An unmodifiable List of matching Persons. Returns an empty list if no matches or query is invalid.
  61 + */
  62 + List<Person> searchByJobTitle(String jobTitleQuery);
  63 +
  64 + /**
  65 + * Filters Persons by their exact sex (case-insensitive).
  66 + *
  67 + * @param sex The sex to filter by (e.g., "Male", "Female"). Can be null or blank.
  68 + * @return An unmodifiable List of matching Persons. Returns an empty list if no matches or filter is invalid.
  69 + */
  70 + List<Person> filterBySex(String sex);
  71 +
  72 + /**
  73 + * Filters Persons by their exact age.
  74 + *
  75 + * @param age The age to filter by.
  76 + * @return An unmodifiable List of matching Persons. Returns an empty list if no matches.
  77 + */
  78 + List<Person> filterByAge(int age);
  79 +
  80 +}
0 \ No newline at end of file 81 \ No newline at end of file
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/PersonServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.tool.method;
  2 +
  3 +import jakarta.annotation.PostConstruct;
  4 +import lombok.extern.slf4j.Slf4j;
  5 +import org.springframework.ai.tool.annotation.Tool;
  6 +import org.springframework.stereotype.Service;
  7 +
  8 +import java.util.Collections;
  9 +import java.util.List;
  10 +import java.util.Map;
  11 +import java.util.Optional;
  12 +import java.util.concurrent.ConcurrentHashMap;
  13 +import java.util.concurrent.atomic.AtomicInteger;
  14 +import java.util.stream.Collectors;
  15 +import java.util.stream.Stream;
  16 +
  17 +/**
  18 + * 来自 Spring AI 官方文档
  19 + *
  20 + * Implementation of the PersonService interface using an in-memory data store.
  21 + * Manages a collection of Person objects loaded from embedded CSV data.
  22 + * This class is thread-safe due to the use of ConcurrentHashMap and AtomicInteger.
  23 + */
  24 +@Service
  25 +@Slf4j
  26 +public class PersonServiceImpl implements PersonService {
  27 +
  28 + private final Map<Integer, Person> personStore = new ConcurrentHashMap<>();
  29 +
  30 + private AtomicInteger idGenerator;
  31 +
  32 + /**
  33 + * Embedded CSV data for initial population
  34 + */
  35 + private static final String CSV_DATA = """
  36 + Id,FirstName,LastName,Email,Sex,IpAddress,JobTitle,Age
  37 + 1,Fons,Tollfree,ftollfree0@senate.gov,Male,55.1 Tollfree Lane,Research Associate,31
  38 + 2,Emlynne,Tabourier,etabourier1@networksolutions.com,Female,18 Tabourier Way,Associate Professor,38
  39 + 3,Shae,Johncey,sjohncey2@yellowpages.com,Male,1 Johncey Circle,Structural Analysis Engineer,30
  40 + 4,Sebastien,Bradly,sbradly3@mapquest.com,Male,2 Bradly Hill,Chief Executive Officer,40
  41 + 5,Harriott,Kitteringham,hkitteringham4@typepad.com,Female,3 Kitteringham Drive,VP Sales,47
  42 + 6,Anallise,Parradine,aparradine5@miibeian.gov.cn,Female,4 Parradine Street,Analog Circuit Design manager,44
  43 + 7,Gorden,Kirkbright,gkirkbright6@reuters.com,Male,5 Kirkbright Plaza,Senior Editor,40
  44 + 8,Veradis,Ledwitch,vledwitch7@google.com.au,Female,6 Ledwitch Avenue,Computer Systems Analyst IV,44
  45 + 9,Agnesse,Penhalurick,apenhalurick8@google.it,Female,7 Penhalurick Terrace,Automation Specialist IV,41
  46 + 10,Bibby,Hutable,bhutable9@craigslist.org,Female,8 Hutable Place,Account Representative I,43
  47 + 11,Karoly,Lightoller,klightollera@rakuten.co.jp,Female,9 Lightoller Parkway,Senior Developer,46
  48 + 12,Cristine,Durrad,cdurradb@aol.com,Female,10 Durrad Center,Senior Developer,48
  49 + 13,Aggy,Napier,anapierc@hostgator.com,Female,11 Napier Court,VP Product Management,44
  50 + 14,Prisca,Caddens,pcaddensd@vinaora.com,Female,12 Caddens Alley,Business Systems Development Analyst,41
  51 + 15,Khalil,McKernan,kmckernane@google.fr,Male,13 McKernan Pass,Engineer IV,44
  52 + 16,Lorry,MacTrusty,lmactrustyf@eventbrite.com,Male,14 MacTrusty Junction,Design Engineer,42
  53 + 17,Casandra,Worsell,cworsellg@goo.gl,Female,15 Worsell Point,Systems Administrator IV,45
  54 + 18,Ulrikaumeko,Haveline,uhavelineh@usgs.gov,Female,16 Haveline Trail,Financial Advisor,42
  55 + 19,Shurlocke,Albany,salbanyi@artisteer.com,Male,17 Albany Plaza,Software Test Engineer III,46
  56 + 20,Myrilla,Brimilcombe,mbrimilcombej@accuweather.com,Female,18 Brimilcombe Road,Programmer Analyst I,48
  57 + 21,Carlina,Scimonelli,cscimonellik@va.gov,Female,19 Scimonelli Pass,Help Desk Technician,45
  58 + 22,Tina,Goullee,tgoulleel@miibeian.gov.cn,Female,20 Goullee Crossing,Accountant IV,43
  59 + 23,Adriaens,Storek,astorekm@devhub.com,Female,21 Storek Avenue,Recruiting Manager,40
  60 + 24,Tedra,Giraudot,tgiraudotn@wiley.com,Female,22 Giraudot Terrace,Speech Pathologist,47
  61 + 25,Josiah,Soares,jsoareso@google.nl,Male,23 Soares Street,Tax Accountant,45
  62 + 26,Kayle,Gaukrodge,kgaukrodgep@wikispaces.com,Female,24 Gaukrodge Parkway,Accountant II,43
  63 + 27,Ardys,Chuter,achuterq@ustream.tv,Female,25 Chuter Drive,Engineer IV,41
  64 + 28,Francyne,Baudinet,fbaudinetr@newyorker.com,Female,26 Baudinet Center,VP Accounting,48
  65 + 29,Gerick,Bullan,gbullans@seesaa.net,Male,27 Bullan Way,Senior Financial Analyst,43
  66 + 30,Northrup,Grivori,ngrivorit@unc.edu,Male,28 Grivori Plaza,Systems Administrator I,45
  67 + 31,Town,Duguid,tduguidu@squarespace.com,Male,29 Duguid Pass,Safety Technician IV,46
  68 + 32,Pierette,Kopisch,pkopischv@google.com.br,Female,30 Kopisch Lane,Director of Sales,41
  69 + 33,Jacquenetta,Le Prevost,jleprevostw@netlog.com,Female,31 Le Prevost Trail,Senior Developer,47
  70 + 34,Garvy,Rusted,grustedx@aboutads.info,Male,32 Rusted Junction,Senior Developer,42
  71 + 35,Clarice,Aysh,cayshy@merriam-webster.com,Female,33 Aysh Avenue,VP Quality Control,40
  72 + 36,Tracie,Fedorski,tfedorskiz@bloglines.com,Male,34 Fedorski Terrace,Design Engineer,44
  73 + 37,Noelyn,Matushenko,nmatushenko10@globo.com,Female,35 Matushenko Place,VP Sales,48
  74 + 38,Rudiger,Klaesson,rklaesson11@usnews.com,Male,36 Klaesson Road,Database Administrator IV,43
  75 + 39,Mirella,Syddie,msyddie12@geocities.jp,Female,37 Syddie Circle,Geological Engineer,46
  76 + 40,Donalt,O'Lunny,dolunny13@elpais.com,Male,38 O'Lunny Center,Analog Circuit Design manager,41
  77 + 41,Guntar,Deniskevich,gdeniskevich14@google.com.hk,Male,39 Deniskevich Way,Structural Engineer,47
  78 + 42,Hort,Shufflebotham,hshufflebotham15@about.me,Male,40 Shufflebotham Court,Structural Analysis Engineer,45
  79 + 43,Dominique,Thickett,dthickett16@slashdot.org,Male,41 Thickett Crossing,Safety Technician I,42
  80 + 44,Zebulen,Piscopello,zpiscopello17@umich.edu,Male,42 Piscopello Parkway,Web Developer II,40
  81 + 45,Mellicent,Mac Giany,mmacgiany18@state.tx.us,Female,43 Mac Giany Pass,Assistant Manager,44
  82 + 46,Merle,Bounds,mbounds19@amazon.co.jp,Female,44 Bounds Alley,Systems Administrator III,41
  83 + 47,Madelle,Farbrace,mfarbrace1a@xinhuanet.com,Female,45 Farbrace Terrace,Quality Engineer,48
  84 + 48,Galvin,O'Sheeryne,gosheeryne1b@addtoany.com,Male,46 O'Sheeryne Way,Environmental Specialist,43
  85 + 49,Guillemette,Bootherstone,gbootherstone1c@nationalgeographic.com,Female,47 Bootherstone Plaza,Professor,46
  86 + 50,Letti,Aylmore,laylmore1d@vinaora.com,Female,48 Aylmore Circle,Automation Specialist I,40
  87 + 51,Nonie,Rivalland,nrivalland1e@weather.com,Female,49 Rivalland Avenue,Software Test Engineer IV,45
  88 + 52,Jacquelynn,Halfacre,jhalfacre1f@surveymonkey.com,Female,50 Halfacre Pass,Geologist II,42
  89 + 53,Anderea,MacKibbon,amackibbon1g@weibo.com,Female,51 MacKibbon Parkway,Automation Specialist II,47
  90 + 54,Wash,Klimko,wklimko1h@slashdot.org,Male,52 Klimko Alley,Database Administrator I,40
  91 + 55,Flori,Kynett,fkynett1i@auda.org.au,Female,53 Kynett Trail,Quality Control Specialist,46
  92 + 56,Libbey,Penswick,lpenswick1j@google.co.uk,Female,54 Penswick Point,VP Accounting,43
  93 + 57,Silvanus,Skellorne,sskellorne1k@booking.com,Male,55 Skellorne Drive,Account Executive,48
  94 + 58,Carmine,Mateos,cmateos1l@plala.or.jp,Male,56 Mateos Terrace,Systems Administrator I,41
  95 + 59,Sheffie,Blazewicz,sblazewicz1m@google.com.au,Male,57 Blazewicz Center,VP Sales,44
  96 + 60,Leanor,Worsnop,lworsnop1n@uol.com.br,Female,58 Worsnop Plaza,Systems Administrator III,45
  97 + 61,Caspar,Pamment,cpamment1o@google.co.jp,Male,59 Pamment Court,Senior Financial Analyst,42
  98 + 62,Justinian,Pentycost,jpentycost1p@sciencedaily.com,Male,60 Pentycost Way,Senior Quality Engineer,47
  99 + 63,Gerianne,Jarnell,gjarnell1q@bing.com,Female,61 Jarnell Avenue,Help Desk Operator,40
  100 + 64,Boycie,Zanetto,bzanetto1r@about.com,Male,62 Zanetto Place,Quality Engineer,46
  101 + 65,Camilla,Mac Giany,cmacgiany1s@state.gov,Female,63 Mac Giany Parkway,Senior Cost Accountant,43
  102 + 66,Hadlee,Piscopiello,hpiscopiello1t@artisteer.com,Male,64 Piscopiello Street,Account Representative III,48
  103 + 67,Bobbie,Penvarden,bpenvarden1u@google.cn,Male,65 Penvarden Lane,Help Desk Operator,41
  104 + 68,Ali,Gowlett,agowlett1v@parallels.com,Male,66 Gowlett Pass,VP Marketing,44
  105 + 69,Olivette,Acome,oacome1w@qq.com,Female,67 Acome Hill,VP Product Management,45
  106 + 70,Jehanna,Brotherheed,jbrotherheed1x@google.nl,Female,68 Brotherheed Junction,Database Administrator III,42
  107 + 71,Morgan,Berthomieu,mberthomieu1y@artisteer.com,Male,69 Berthomieu Alley,Systems Administrator II,47
  108 + 72,Linzy,Shilladay,lshilladay1z@icq.com,Female,70 Shilladay Trail,Research Assistant IV,40
  109 + 73,Faydra,Brimner,fbrimner20@mozilla.org,Female,71 Brimner Road,Senior Editor,46
  110 + 74,Gwenore,Oxlee,goxlee21@devhub.com,Female,72 Oxlee Terrace,Systems Administrator II,43
  111 + 75,Evangelin,Beinke,ebeinke22@mozilla.com,Female,73 Beinke Circle,Accountant I,48
  112 + 76,Missy,Cockling,mcockling23@si.edu,Female,74 Cockling Way,Software Engineer I,41
  113 + 77,Suzanne,Klimschak,sklimschak24@etsy.com,Female,75 Klimschak Plaza,Tax Accountant,44
  114 + 78,Candide,Goricke,cgoricke25@weebly.com,Female,76 Goricke Pass,Sales Associate,45
  115 + 79,Gerome,Pinsent,gpinsent26@google.com.au,Male,77 Pinsent Junction,Software Consultant,42
  116 + 80,Lezley,Mac Giany,lmacgiany27@scribd.com,Male,78 Mac Giany Alley,Operator,47
  117 + 81,Tobiah,Durn,tdurn28@state.tx.us,Male,79 Durn Court,VP Sales,40
  118 + 82,Sherlocke,Cockshoot,scockshoot29@yelp.com,Male,80 Cockshoot Street,Senior Financial Analyst,46
  119 + 83,Myrle,Speenden,mspeenden2a@utexas.edu,Female,81 Speenden Center,Senior Developer,43
  120 + 84,Isidore,Gorries,igorries2b@flavors.me,Male,82 Gorries Parkway,Sales Representative,48
  121 + 85,Isac,Kitchingman,ikitchingman2c@businessinsider.com,Male,83 Kitchingman Drive,VP Accounting,41
  122 + 86,Benedetta,Purrier,bpurrier2d@admin.ch,Female,84 Purrier Trail,VP Accounting,44
  123 + 87,Tera,Fitchell,tfitchell2e@fotki.com,Female,85 Fitchell Place,Software Engineer IV,45
  124 + 88,Abbe,Pamment,apamment2f@about.com,Male,86 Pamment Avenue,VP Sales,42
  125 + 89,Jandy,Gommowe,jgommowe2g@angelfire.com,Female,87 Gommowe Road,Financial Analyst,47
  126 + 90,Karena,Fussey,kfussey2h@google.com.au,Female,88 Fussey Point,Assistant Professor,40
  127 + 91,Gaspar,Pammenter,gpammenter2i@google.com.br,Male,89 Pammenter Hill,Help Desk Operator,46
  128 + 92,Stanwood,Mac Giany,smacgiany2j@prlog.org,Male,90 Mac Giany Terrace,Research Associate,43
  129 + 93,Byrom,Beedell,bbeedell2k@google.co.jp,Male,91 Beedell Way,VP Sales,48
  130 + 94,Annabella,Rowbottom,arowbottom2l@google.com.au,Female,92 Rowbottom Plaza,Help Desk Operator,41
  131 + 95,Rodolphe,Debell,rdebell2m@imageshack.us,Male,93 Debell Pass,Design Engineer,44
  132 + 96,Tyne,Gommey,tgommey2n@joomla.org,Female,94 Gommey Junction,VP Marketing,45
  133 + 97,Christoper,Pincked,cpincked2o@icq.com,Male,95 Pincked Alley,Human Resources Manager,42
  134 + 98,Kore,Le Prevost,kleprevost2p@tripadvisor.com,Female,96 Le Prevost Street,VP Quality Control,47
  135 + 99,Ceciley,Petrolli,cpetrolli2q@oaic.gov.au,Female,97 Petrolli Court,Senior Developer,40
  136 + 100,Elspeth,Mac Giany,emacgiany2r@icio.us,Female,98 Mac Giany Parkway,Internal Auditor,46
  137 + """;
  138 +
  139 + /**
  140 + * Initializes the service after dependency injection by loading data from the CSV string.
  141 + * Uses @PostConstruct to ensure this runs after the bean is created.
  142 + */
  143 + @PostConstruct
  144 + private void initializeData() {
  145 + log.info("Initializing PersonService data store...");
  146 + int maxId = loadDataFromCsv();
  147 + idGenerator = new AtomicInteger(maxId);
  148 + log.info("PersonService initialized with {} records. Next ID: {}", personStore.size(), idGenerator.get() + 1);
  149 + }
  150 +
  151 + /**
  152 + * Parses the embedded CSV data and populates the in-memory store.
  153 + * Calculates the maximum ID found in the data to initialize the ID generator.
  154 + *
  155 + * @return The maximum ID found in the loaded CSV data.
  156 + */
  157 + private int loadDataFromCsv() {
  158 + final AtomicInteger currentMaxId = new AtomicInteger(0);
  159 + // Clear existing data before loading (important for tests or re-initialization scenarios)
  160 + personStore.clear();
  161 + try (Stream<String> lines = CSV_DATA.lines().skip(1)) { // Skip header row
  162 + lines.forEach(line -> {
  163 + try {
  164 + // Split carefully, handling potential commas within quoted fields if necessary (simple split here)
  165 + String[] fields = line.split(",", 8); // Limit split to handle potential commas in job title
  166 + if (fields.length == 8) {
  167 + int id = Integer.parseInt(fields[0].trim());
  168 + String firstName = fields[1].trim();
  169 + String lastName = fields[2].trim();
  170 + String email = fields[3].trim();
  171 + String sex = fields[4].trim();
  172 + String ipAddress = fields[5].trim();
  173 + String jobTitle = fields[6].trim();
  174 + int age = Integer.parseInt(fields[7].trim());
  175 +
  176 + Person person = new Person(id, firstName, lastName, email, sex, ipAddress, jobTitle, age);
  177 + personStore.put(id, person);
  178 + currentMaxId.updateAndGet(max -> Math.max(max, id));
  179 + } else {
  180 + log.warn("Skipping malformed CSV line (expected 8 fields, found {}): {}", fields.length, line);
  181 + }
  182 + } catch (NumberFormatException e) {
  183 + log.warn("Skipping line due to parsing error (ID or Age): {} - Error: {}", line, e.getMessage());
  184 + } catch (Exception e) {
  185 + log.error("Skipping line due to unexpected error: {} - Error: {}", line, e.getMessage(), e);
  186 + }
  187 + });
  188 + } catch (Exception e) {
  189 + log.error("Fatal error reading embedded CSV data: {}", e.getMessage(), e);
  190 + // In a real application, might throw a specific initialization exception
  191 + }
  192 + return currentMaxId.get();
  193 + }
  194 +
  195 + @Override
  196 + @Tool(
  197 + name = "ps_create_person",
  198 + description = "Create a new person record in the in-memory store."
  199 + )
  200 + public Person createPerson(Person personData) {
  201 + if (personData == null) {
  202 + throw new IllegalArgumentException("Person data cannot be null");
  203 + }
  204 + int newId = idGenerator.incrementAndGet();
  205 + // Create a new Person record using data from the input, but with the generated ID
  206 + Person newPerson = new Person(
  207 + newId,
  208 + personData.firstName(),
  209 + personData.lastName(),
  210 + personData.email(),
  211 + personData.sex(),
  212 + personData.ipAddress(),
  213 + personData.jobTitle(),
  214 + personData.age()
  215 + );
  216 + personStore.put(newId, newPerson);
  217 + log.debug("Created person: {}", newPerson);
  218 + return newPerson;
  219 + }
  220 +
  221 + @Override
  222 + @Tool(
  223 + name = "ps_get_person_by_id",
  224 + description = "Retrieve a person record by ID from the in-memory store."
  225 + )
  226 + public Optional<Person> getPersonById(int id) {
  227 + Person person = personStore.get(id);
  228 + log.debug("Retrieved person by ID {}: {}", id, person);
  229 + return Optional.ofNullable(person);
  230 + }
  231 +
  232 + @Override
  233 + @Tool(
  234 + name = "ps_get_all_persons",
  235 + description = "Retrieve all person records from the in-memory store."
  236 + )
  237 + public List<Person> getAllPersons() {
  238 + // Return an unmodifiable view of the values
  239 + List<Person> allPersons = personStore.values().stream().toList();
  240 + log.debug("Retrieved all persons (count: {})", allPersons.size());
  241 + return allPersons;
  242 + }
  243 +
  244 + @Override
  245 + @Tool(
  246 + name = "ps_update_person",
  247 + description = "Update an existing person record by ID in the in-memory store."
  248 + )
  249 + public boolean updatePerson(int id, Person updatedPersonData) {
  250 + if (updatedPersonData == null) {
  251 + throw new IllegalArgumentException("Updated person data cannot be null");
  252 + }
  253 + // Use computeIfPresent for atomic update if the key exists
  254 + Person result = personStore.computeIfPresent(id, (key, existingPerson) ->
  255 + // Create a new Person record with the original ID but updated data
  256 + new Person(
  257 + id, // Keep original ID
  258 + updatedPersonData.firstName(),
  259 + updatedPersonData.lastName(),
  260 + updatedPersonData.email(),
  261 + updatedPersonData.sex(),
  262 + updatedPersonData.ipAddress(),
  263 + updatedPersonData.jobTitle(),
  264 + updatedPersonData.age()
  265 + )
  266 + );
  267 + boolean updated = result != null;
  268 + log.debug("Update attempt for ID {}: {}", id, updated ? "Successful" : "Failed (Not Found)");
  269 + if(updated) log.trace("Updated person data for ID {}: {}", id, result);
  270 + return updated;
  271 + }
  272 +
  273 + @Override
  274 + @Tool(
  275 + name = "ps_delete_person",
  276 + description = "Delete a person record by ID from the in-memory store."
  277 + )
  278 + public boolean deletePerson(int id) {
  279 + boolean removed = personStore.remove(id) != null;
  280 + log.debug("Delete attempt for ID {}: {}", id, removed ? "Successful" : "Failed (Not Found)");
  281 + return removed;
  282 + }
  283 +
  284 + @Override
  285 + @Tool(
  286 + name = "ps_search_by_job_title",
  287 + description = "Search for persons by job title in the in-memory store."
  288 + )
  289 + public List<Person> searchByJobTitle(String jobTitleQuery) {
  290 + if (jobTitleQuery == null || jobTitleQuery.isBlank()) {
  291 + log.debug("Search by job title skipped due to blank query.");
  292 + return Collections.emptyList();
  293 + }
  294 + String lowerCaseQuery = jobTitleQuery.toLowerCase();
  295 + List<Person> results = personStore.values().stream()
  296 + .filter(person -> person.jobTitle() != null && person.jobTitle().toLowerCase().contains(lowerCaseQuery))
  297 + .collect(Collectors.toList());
  298 + log.debug("Search by job title '{}' found {} results.", jobTitleQuery, results.size());
  299 + return Collections.unmodifiableList(results);
  300 + }
  301 +
  302 + @Override
  303 + @Tool(
  304 + name = "ps_filter_by_sex",
  305 + description = "Filters Persons by sex (case-insensitive)."
  306 + )
  307 + public List<Person> filterBySex(String sex) {
  308 + if (sex == null || sex.isBlank()) {
  309 + log.debug("Filter by sex skipped due to blank filter.");
  310 + return Collections.emptyList();
  311 + }
  312 + List<Person> results = personStore.values().stream()
  313 + .filter(person -> person.sex() != null && person.sex().equalsIgnoreCase(sex))
  314 + .collect(Collectors.toList());
  315 + log.debug("Filter by sex '{}' found {} results.", sex, results.size());
  316 + return Collections.unmodifiableList(results);
  317 + }
  318 +
  319 + @Override
  320 + @Tool(
  321 + name = "ps_filter_by_age",
  322 + description = "Filters Persons by age."
  323 + )
  324 + public List<Person> filterByAge(int age) {
  325 + if (age < 0) {
  326 + log.debug("Filter by age skipped due to negative age: {}", age);
  327 + return Collections.emptyList(); // Or throw IllegalArgumentException based on requirements
  328 + }
  329 + List<Person> results = personStore.values().stream()
  330 + .filter(person -> person.age() == age)
  331 + .collect(Collectors.toList());
  332 + log.debug("Filter by age {} found {} results.", age, results.size());
  333 + return Collections.unmodifiableList(results);
  334 + }
  335 +
  336 +}
0 \ No newline at end of file 337 \ No newline at end of file
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/package-info.java 0 → 100644
  1 +/**
  2 + * 参考 <a href="https://docs.spring.io/spring-ai/reference/api/tools.html#_methods_as_tools">Tool Calling —— Methods as Tools</a>
  3 + */
  4 +package cn.iocoder.yudao.module.ai.tool.method;
0 \ No newline at end of file 5 \ No newline at end of file
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java
@@ -8,18 +8,20 @@ import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; @@ -8,18 +8,20 @@ import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
8 import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; 8 import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
9 import org.springaicommunity.moonshot.MoonshotChatOptions; 9 import org.springaicommunity.moonshot.MoonshotChatOptions;
10 import org.springaicommunity.qianfan.QianFanChatOptions; 10 import org.springaicommunity.qianfan.QianFanChatOptions;
  11 +import org.springframework.ai.anthropic.AnthropicChatOptions;
11 import org.springframework.ai.azure.openai.AzureOpenAiChatOptions; 12 import org.springframework.ai.azure.openai.AzureOpenAiChatOptions;
12 import org.springframework.ai.chat.messages.*; 13 import org.springframework.ai.chat.messages.*;
  14 +import org.springframework.ai.chat.model.ChatResponse;
13 import org.springframework.ai.chat.prompt.ChatOptions; 15 import org.springframework.ai.chat.prompt.ChatOptions;
  16 +import org.springframework.ai.deepseek.DeepSeekAssistantMessage;
  17 +import org.springframework.ai.deepseek.DeepSeekChatOptions;
14 import org.springframework.ai.minimax.MiniMaxChatOptions; 18 import org.springframework.ai.minimax.MiniMaxChatOptions;
15 import org.springframework.ai.ollama.api.OllamaOptions; 19 import org.springframework.ai.ollama.api.OllamaOptions;
16 import org.springframework.ai.openai.OpenAiChatOptions; 20 import org.springframework.ai.openai.OpenAiChatOptions;
  21 +import org.springframework.ai.tool.ToolCallback;
17 import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; 22 import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
18 23
19 -import java.util.Collections;  
20 -import java.util.HashMap;  
21 -import java.util.Map;  
22 -import java.util.Set; 24 +import java.util.*;
23 25
24 /** 26 /**
25 * Spring AI 工具类 27 * Spring AI 工具类
@@ -36,40 +38,47 @@ public class AiUtils { @@ -36,40 +38,47 @@ public class AiUtils {
36 } 38 }
37 39
38 public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens, 40 public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens,
39 - Set<String> toolNames, Map<String, Object> toolContext) {  
40 - toolNames = ObjUtil.defaultIfNull(toolNames, Collections.emptySet()); 41 + List<ToolCallback> toolCallbacks, Map<String, Object> toolContext) {
  42 + toolCallbacks = ObjUtil.defaultIfNull(toolCallbacks, Collections.emptyList());
41 toolContext = ObjUtil.defaultIfNull(toolContext, Collections.emptyMap()); 43 toolContext = ObjUtil.defaultIfNull(toolContext, Collections.emptyMap());
42 // noinspection EnhancedSwitchMigration 44 // noinspection EnhancedSwitchMigration
43 switch (platform) { 45 switch (platform) {
44 case TONG_YI: 46 case TONG_YI:
45 return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens) 47 return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens)
46 - .withToolNames(toolNames).withToolContext(toolContext).build(); 48 + .withEnableThinking(true) // TODO 芋艿:默认都开启 thinking 模式,后续可以让用户配置
  49 + .withToolCallbacks(toolCallbacks).withToolContext(toolContext).build();
47 case YI_YAN: 50 case YI_YAN:
48 return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); 51 return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build();
  52 + case DEEP_SEEK:
  53 + case DOU_BAO: // 复用 DeepSeek 客户端
  54 + case HUN_YUAN: // 复用 DeepSeek 客户端
  55 + case SILICON_FLOW: // 复用 DeepSeek 客户端
  56 + case XING_HUO: // 复用 DeepSeek 客户端
  57 + return DeepSeekChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
  58 + .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
49 case ZHI_PU: 59 case ZHI_PU:
50 return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) 60 return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
51 - .toolNames(toolNames).toolContext(toolContext).build(); 61 + .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
52 case MINI_MAX: 62 case MINI_MAX:
53 return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) 63 return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
54 - .toolNames(toolNames).toolContext(toolContext).build(); 64 + .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
55 case MOONSHOT: 65 case MOONSHOT:
56 return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) 66 return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
57 - .toolNames(toolNames).toolContext(toolContext).build(); 67 + .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
58 case OPENAI: 68 case OPENAI:
59 - case DEEP_SEEK: // 复用 OpenAI 客户端  
60 - case DOU_BAO: // 复用 OpenAI 客户端  
61 - case HUN_YUAN: // 复用 OpenAI 客户端  
62 - case XING_HUO: // 复用 OpenAI 客户端  
63 - case SILICON_FLOW: // 复用 OpenAI 客户端 69 + case GEMINI: // 复用 OpenAI 客户端
64 case BAI_CHUAN: // 复用 OpenAI 客户端 70 case BAI_CHUAN: // 复用 OpenAI 客户端
65 return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) 71 return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
66 - .toolNames(toolNames).toolContext(toolContext).build(); 72 + .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
67 case AZURE_OPENAI: 73 case AZURE_OPENAI:
68 return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens) 74 return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens)
69 - .toolNames(toolNames).toolContext(toolContext).build(); 75 + .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
  76 + case ANTHROPIC:
  77 + return AnthropicChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
  78 + .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
70 case OLLAMA: 79 case OLLAMA:
71 return OllamaOptions.builder().model(model).temperature(temperature).numPredict(maxTokens) 80 return OllamaOptions.builder().model(model).temperature(temperature).numPredict(maxTokens)
72 - .toolNames(toolNames).toolContext(toolContext).build(); 81 + .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
73 default: 82 default:
74 throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); 83 throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
75 } 84 }
@@ -98,4 +107,27 @@ public class AiUtils { @@ -98,4 +107,27 @@ public class AiUtils {
98 return context; 107 return context;
99 } 108 }
100 109
  110 + @SuppressWarnings("ConstantValue")
  111 + public static String getChatResponseContent(ChatResponse response) {
  112 + if (response == null
  113 + || response.getResult() == null
  114 + || response.getResult().getOutput() == null) {
  115 + return null;
  116 + }
  117 + return response.getResult().getOutput().getText();
  118 + }
  119 +
  120 + @SuppressWarnings("ConstantValue")
  121 + public static String getChatResponseReasoningContent(ChatResponse response) {
  122 + if (response == null
  123 + || response.getResult() == null
  124 + || response.getResult().getOutput() == null) {
  125 + return null;
  126 + }
  127 + if (response.getResult().getOutput() instanceof DeepSeekAssistantMessage) {
  128 + return ((DeepSeekAssistantMessage) (response.getResult().getOutput())).getReasoningContent();
  129 + }
  130 + return null;
  131 + }
  132 +
101 } 133 }
102 \ No newline at end of file 134 \ No newline at end of file
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/FileTypeUtils.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.util;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import lombok.extern.slf4j.Slf4j;
  5 +import org.apache.tika.Tika;
  6 +
  7 +/**
  8 + * 文件类型 Utils
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@Slf4j
  13 +public class FileTypeUtils {
  14 +
  15 + private static final Tika TIKA = new Tika();
  16 +
  17 + /**
  18 + * 已知文件名,获取文件类型,在某些情况下比通过字节数组准确,例如使用 jar 文件时,通过名字更为准确
  19 + *
  20 + * @param name 文件名
  21 + * @return mineType 无法识别时会返回“application/octet-stream”
  22 + */
  23 + public static String getMineType(String name) {
  24 + return TIKA.detect(name);
  25 + }
  26 +
  27 + /**
  28 + * 判断是否是图片
  29 + *
  30 + * @param mineType 类型
  31 + * @return 是否是图片
  32 + */
  33 + public static boolean isImage(String mineType) {
  34 + return StrUtil.startWith(mineType, "image/");
  35 + }
  36 +
  37 +}
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AnthropicChatModelTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
  2 +
  3 +import org.junit.jupiter.api.Disabled;
  4 +import org.junit.jupiter.api.Test;
  5 +import org.springframework.ai.anthropic.AnthropicChatModel;
  6 +import org.springframework.ai.anthropic.AnthropicChatOptions;
  7 +import org.springframework.ai.anthropic.api.AnthropicApi;
  8 +import org.springframework.ai.chat.messages.Message;
  9 +import org.springframework.ai.chat.messages.SystemMessage;
  10 +import org.springframework.ai.chat.messages.UserMessage;
  11 +import org.springframework.ai.chat.model.ChatResponse;
  12 +import org.springframework.ai.chat.prompt.Prompt;
  13 +import reactor.core.publisher.Flux;
  14 +
  15 +import java.util.ArrayList;
  16 +import java.util.List;
  17 +
  18 +/**
  19 + * {@link AnthropicChatModel} 集成测试类
  20 + *
  21 + * @author 芋道源码
  22 + */
  23 +public class AnthropicChatModelTest {
  24 +
  25 + private final AnthropicChatModel chatModel = AnthropicChatModel.builder()
  26 + .anthropicApi(AnthropicApi.builder()
  27 + .apiKey("sk-muubv7cXeLw0Etgs743f365cD5Ea44429946Fa7e672d8942")
  28 + .baseUrl("https://aihubmix.com")
  29 + .build())
  30 + .defaultOptions(AnthropicChatOptions.builder()
  31 + .model(AnthropicApi.ChatModel.CLAUDE_SONNET_4)
  32 + .temperature(0.7)
  33 + .maxTokens(4096)
  34 + .build())
  35 + .build();
  36 +
  37 + @Test
  38 + @Disabled
  39 + public void testCall() {
  40 + // 准备参数
  41 + List<Message> messages = new ArrayList<>();
  42 + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
  43 + messages.add(new UserMessage("1 + 1 = ?"));
  44 +
  45 + // 调用
  46 + ChatResponse response = chatModel.call(new Prompt(messages));
  47 + // 打印结果
  48 + System.out.println(response);
  49 + }
  50 +
  51 + @Test
  52 + @Disabled
  53 + public void testStream() {
  54 + // 准备参数
  55 + List<Message> messages = new ArrayList<>();
  56 + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
  57 + messages.add(new UserMessage("1 + 1 = ?"));
  58 +
  59 + // 调用
  60 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
  61 + // 打印结果
  62 + flux.doOnNext(System.out::println).then().block();
  63 + }
  64 +
  65 + // TODO @芋艿:需要等 spring ai 升级:https://github.com/spring-projects/spring-ai/pull/2800
  66 + @Test
  67 + @Disabled
  68 + public void testStream_thinking() {
  69 + // 准备参数
  70 + List<Message> messages = new ArrayList<>();
  71 + messages.add(new UserMessage("thkinking 下,1+1 为什么等于 2 "));
  72 + AnthropicChatOptions options = AnthropicChatOptions.builder()
  73 + .model(AnthropicApi.ChatModel.CLAUDE_SONNET_4)
  74 + .thinking(AnthropicApi.ThinkingType.ENABLED, 3096)
  75 + .temperature(1D)
  76 + .build();
  77 +
  78 + // 调用
  79 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
  80 + // 打印结果
  81 + flux.doOnNext(response -> {
  82 +// System.out.println(response);
  83 + System.out.println(response.getResult());
  84 + }).then().block();
  85 + }
  86 +
  87 +}
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java
@@ -60,4 +60,23 @@ public class DeepSeekChatModelTests { @@ -60,4 +60,23 @@ public class DeepSeekChatModelTests {
60 flux.doOnNext(System.out::println).then().block(); 60 flux.doOnNext(System.out::println).then().block();
61 } 61 }
62 62
  63 + @Test
  64 + @Disabled
  65 + public void testStream_thinking() {
  66 + // 准备参数
  67 + List<Message> messages = new ArrayList<>();
  68 + messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
  69 + DeepSeekChatOptions options = DeepSeekChatOptions.builder()
  70 + .model("deepseek-reasoner")
  71 + .build();
  72 +
  73 + // 调用
  74 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
  75 + // 打印结果
  76 + flux.doOnNext(response -> {
  77 +// System.out.println(response);
  78 + System.out.println(response.getResult().getOutput());
  79 + }).then().block();
  80 + }
  81 +
63 } 82 }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DouBaoChatModelTests.java
@@ -8,9 +8,9 @@ import org.springframework.ai.chat.messages.SystemMessage; @@ -8,9 +8,9 @@ import org.springframework.ai.chat.messages.SystemMessage;
8 import org.springframework.ai.chat.messages.UserMessage; 8 import org.springframework.ai.chat.messages.UserMessage;
9 import org.springframework.ai.chat.model.ChatResponse; 9 import org.springframework.ai.chat.model.ChatResponse;
10 import org.springframework.ai.chat.prompt.Prompt; 10 import org.springframework.ai.chat.prompt.Prompt;
11 -import org.springframework.ai.openai.OpenAiChatModel;  
12 -import org.springframework.ai.openai.OpenAiChatOptions;  
13 -import org.springframework.ai.openai.api.OpenAiApi; 11 +import org.springframework.ai.deepseek.DeepSeekChatModel;
  12 +import org.springframework.ai.deepseek.DeepSeekChatOptions;
  13 +import org.springframework.ai.deepseek.api.DeepSeekApi;
14 import reactor.core.publisher.Flux; 14 import reactor.core.publisher.Flux;
15 15
16 import java.util.ArrayList; 16 import java.util.ArrayList;
@@ -23,13 +23,18 @@ import java.util.List; @@ -23,13 +23,18 @@ import java.util.List;
23 */ 23 */
24 public class DouBaoChatModelTests { 24 public class DouBaoChatModelTests {
25 25
26 - private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()  
27 - .openAiApi(OpenAiApi.builder() 26 + /**
  27 + * 相比 OpenAIChatModel 来说,DeepSeekChatModel 可以兼容豆包的 thinking 能力!
  28 + */
  29 + private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
  30 + .deepSeekApi(DeepSeekApi.builder()
28 .baseUrl(DouBaoChatModel.BASE_URL) 31 .baseUrl(DouBaoChatModel.BASE_URL)
  32 + .completionsPath(DouBaoChatModel.COMPLETE_PATH)
29 .apiKey("5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272") // apiKey 33 .apiKey("5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272") // apiKey
30 .build()) 34 .build())
31 - .defaultOptions(OpenAiChatOptions.builder() 35 + .defaultOptions(DeepSeekChatOptions.builder()
32 .model("doubao-1-5-lite-32k-250115") // 模型(doubao) 36 .model("doubao-1-5-lite-32k-250115") // 模型(doubao)
  37 +// .model("doubao-seed-1-6-thinking-250715") // 模型(doubao)
33 // .model("deepseek-r1-250120") // 模型(deepseek) 38 // .model("deepseek-r1-250120") // 模型(deepseek)
34 .temperature(0.7) 39 .temperature(0.7)
35 .build()) 40 .build())
@@ -51,14 +56,13 @@ public class DouBaoChatModelTests { @@ -51,14 +56,13 @@ public class DouBaoChatModelTests {
51 System.out.println(response); 56 System.out.println(response);
52 } 57 }
53 58
54 - // TODO @芋艿:因为使用的是 v1 api,导致 deepseek-r1-250120 不返回 think 过程,后续需要优化  
55 @Test 59 @Test
56 @Disabled 60 @Disabled
57 public void testStream() { 61 public void testStream() {
58 // 准备参数 62 // 准备参数
59 List<Message> messages = new ArrayList<>(); 63 List<Message> messages = new ArrayList<>();
60 messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); 64 messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
61 - messages.add(new UserMessage("1 + 1 = ?")); 65 + messages.add(new UserMessage("详细推理下,帮我设计一个用户中心!"));
62 66
63 // 调用 67 // 调用
64 Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages)); 68 Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
@@ -66,4 +70,23 @@ public class DouBaoChatModelTests { @@ -66,4 +70,23 @@ public class DouBaoChatModelTests {
66 flux.doOnNext(System.out::println).then().block(); 70 flux.doOnNext(System.out::println).then().block();
67 } 71 }
68 72
  73 + @Test
  74 + @Disabled
  75 + public void testStream_thinking() {
  76 + // 准备参数
  77 + List<Message> messages = new ArrayList<>();
  78 + messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
  79 + DeepSeekChatOptions options = DeepSeekChatOptions.builder()
  80 + .model("doubao-seed-1-6-thinking-250715")
  81 + .build();
  82 +
  83 + // 调用
  84 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
  85 + // 打印结果
  86 + flux.doOnNext(response -> {
  87 +// System.out.println(response);
  88 + System.out.println(response.getResult().getOutput());
  89 + }).then().block();
  90 + }
  91 +
69 } 92 }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GeminiChatModelTests.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
  2 +
  3 +import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel;
  4 +import org.junit.jupiter.api.Disabled;
  5 +import org.junit.jupiter.api.Test;
  6 +import org.springframework.ai.chat.messages.Message;
  7 +import org.springframework.ai.chat.messages.SystemMessage;
  8 +import org.springframework.ai.chat.messages.UserMessage;
  9 +import org.springframework.ai.chat.model.ChatResponse;
  10 +import org.springframework.ai.chat.prompt.Prompt;
  11 +import org.springframework.ai.openai.OpenAiChatModel;
  12 +import org.springframework.ai.openai.OpenAiChatOptions;
  13 +import org.springframework.ai.openai.api.OpenAiApi;
  14 +import reactor.core.publisher.Flux;
  15 +
  16 +import java.util.ArrayList;
  17 +import java.util.List;
  18 +
  19 +/**
  20 + * {@link GeminiChatModel} 集成测试
  21 + *
  22 + * @author 芋道源码
  23 + */
  24 +public class GeminiChatModelTests {
  25 +
  26 + private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
  27 + .openAiApi(OpenAiApi.builder()
  28 + .baseUrl(GeminiChatModel.BASE_URL)
  29 + .completionsPath(GeminiChatModel.COMPLETE_PATH)
  30 + .apiKey("AIzaSyAVoBxgoFvvte820vEQMma2LKBnC98bqMQ")
  31 + .build())
  32 + .defaultOptions(OpenAiChatOptions.builder()
  33 + .model(GeminiChatModel.MODEL_DEFAULT) // 模型
  34 + .temperature(0.7)
  35 + .build())
  36 + .build();
  37 +
  38 + private final GeminiChatModel chatModel = new GeminiChatModel(openAiChatModel);
  39 +
  40 + @Test
  41 + @Disabled
  42 + public void testCall() {
  43 + // 准备参数
  44 + List<Message> messages = new ArrayList<>();
  45 + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
  46 + messages.add(new UserMessage("1 + 1 = ?"));
  47 +
  48 + // 调用
  49 + ChatResponse response = chatModel.call(new Prompt(messages));
  50 + // 打印结果
  51 + System.out.println(response);
  52 + }
  53 +
  54 + @Test
  55 + @Disabled
  56 + public void testStream() {
  57 + // 准备参数
  58 + List<Message> messages = new ArrayList<>();
  59 + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
  60 + messages.add(new UserMessage("1 + 1 = ?"));
  61 +
  62 + // 调用
  63 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
  64 + // 打印结果
  65 + flux.doOnNext(System.out::println).then().block();
  66 + }
  67 +
  68 +}
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/HunYuanChatModelTests.java
@@ -8,9 +8,9 @@ import org.springframework.ai.chat.messages.SystemMessage; @@ -8,9 +8,9 @@ import org.springframework.ai.chat.messages.SystemMessage;
8 import org.springframework.ai.chat.messages.UserMessage; 8 import org.springframework.ai.chat.messages.UserMessage;
9 import org.springframework.ai.chat.model.ChatResponse; 9 import org.springframework.ai.chat.model.ChatResponse;
10 import org.springframework.ai.chat.prompt.Prompt; 10 import org.springframework.ai.chat.prompt.Prompt;
11 -import org.springframework.ai.openai.OpenAiChatModel;  
12 -import org.springframework.ai.openai.OpenAiChatOptions;  
13 -import org.springframework.ai.openai.api.OpenAiApi; 11 +import org.springframework.ai.deepseek.DeepSeekChatModel;
  12 +import org.springframework.ai.deepseek.DeepSeekChatOptions;
  13 +import org.springframework.ai.deepseek.api.DeepSeekApi;
14 import reactor.core.publisher.Flux; 14 import reactor.core.publisher.Flux;
15 15
16 import java.util.ArrayList; 16 import java.util.ArrayList;
@@ -23,12 +23,13 @@ import java.util.List; @@ -23,12 +23,13 @@ import java.util.List;
23 */ 23 */
24 public class HunYuanChatModelTests { 24 public class HunYuanChatModelTests {
25 25
26 - private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()  
27 - .openAiApi(OpenAiApi.builder() 26 + private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
  27 + .deepSeekApi(DeepSeekApi.builder()
28 .baseUrl(HunYuanChatModel.BASE_URL) 28 .baseUrl(HunYuanChatModel.BASE_URL)
29 - .apiKey("sk-bcd") // apiKey 29 + .completionsPath(HunYuanChatModel.COMPLETE_PATH)
  30 + .apiKey("sk-abc") // apiKey
30 .build()) 31 .build())
31 - .defaultOptions(OpenAiChatOptions.builder() 32 + .defaultOptions(DeepSeekChatOptions.builder()
32 .model(HunYuanChatModel.MODEL_DEFAULT) // 模型 33 .model(HunYuanChatModel.MODEL_DEFAULT) // 模型
33 .temperature(0.7) 34 .temperature(0.7)
34 .build()) 35 .build())
@@ -64,12 +65,33 @@ public class HunYuanChatModelTests { @@ -64,12 +65,33 @@ public class HunYuanChatModelTests {
64 flux.doOnNext(System.out::println).then().block(); 65 flux.doOnNext(System.out::println).then().block();
65 } 66 }
66 67
67 - private final OpenAiChatModel deepSeekOpenAiChatModel = OpenAiChatModel.builder()  
68 - .openAiApi(OpenAiApi.builder() 68 + @Test
  69 + @Disabled
  70 + public void testStream_thinking() {
  71 + // 准备参数
  72 + List<Message> messages = new ArrayList<>();
  73 + messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
  74 + DeepSeekChatOptions options = DeepSeekChatOptions.builder()
  75 + .model("hunyuan-a13b")
  76 +// .model("hunyuan-turbos-latest")
  77 + .build();
  78 +
  79 + // 调用
  80 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
  81 + // 打印结果
  82 + flux.doOnNext(response -> {
  83 +// System.out.println(response);
  84 + System.out.println(response.getResult().getOutput());
  85 + }).then().block();
  86 + }
  87 +
  88 + private final DeepSeekChatModel deepSeekOpenAiChatModel = DeepSeekChatModel.builder()
  89 + .deepSeekApi(DeepSeekApi.builder()
69 .baseUrl(HunYuanChatModel.DEEP_SEEK_BASE_URL) 90 .baseUrl(HunYuanChatModel.DEEP_SEEK_BASE_URL)
  91 + .completionsPath(HunYuanChatModel.COMPLETE_PATH)
70 .apiKey("sk-abc") // apiKey 92 .apiKey("sk-abc") // apiKey
71 .build()) 93 .build())
72 - .defaultOptions(OpenAiChatOptions.builder() 94 + .defaultOptions(DeepSeekChatOptions.builder()
73 // .model(HunYuanChatModel.DEEP_SEEK_MODEL_DEFAULT) // 模型("deepseek-v3") 95 // .model(HunYuanChatModel.DEEP_SEEK_MODEL_DEFAULT) // 模型("deepseek-v3")
74 .model("deepseek-r1") // 模型("deepseek-r1") 96 .model("deepseek-r1") // 模型("deepseek-r1")
75 .temperature(0.7) 97 .temperature(0.7)
@@ -94,7 +116,7 @@ public class HunYuanChatModelTests { @@ -94,7 +116,7 @@ public class HunYuanChatModelTests {
94 116
95 @Test 117 @Test
96 @Disabled 118 @Disabled
97 - public void testStream_deekseek() { 119 + public void testStream_deepseek() {
98 // 准备参数 120 // 准备参数
99 List<Message> messages = new ArrayList<>(); 121 List<Message> messages = new ArrayList<>();
100 messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); 122 messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -106,5 +128,23 @@ public class HunYuanChatModelTests { @@ -106,5 +128,23 @@ public class HunYuanChatModelTests {
106 flux.doOnNext(System.out::println).then().block(); 128 flux.doOnNext(System.out::println).then().block();
107 } 129 }
108 130
  131 + @Test
  132 + @Disabled
  133 + public void testStream_deepseek_thinking() {
  134 + // 准备参数
  135 + List<Message> messages = new ArrayList<>();
  136 + messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
  137 + DeepSeekChatOptions options = DeepSeekChatOptions.builder()
  138 + .model("deepseek-r1")
  139 + .build();
  140 +
  141 + // 调用
  142 + Flux<ChatResponse> flux = deepSeekChatModel.stream(new Prompt(messages, options));
  143 + // 打印结果
  144 + flux.doOnNext(response -> {
  145 +// System.out.println(response);
  146 + System.out.println(response.getResult().getOutput());
  147 + }).then().block();
  148 + }
109 149
110 } 150 }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java
1 package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; 1 package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
2 2
  3 +import org.junit.jupiter.api.Disabled;
  4 +import org.junit.jupiter.api.Test;
  5 +import org.springframework.ai.chat.messages.Message;
  6 +import org.springframework.ai.chat.messages.SystemMessage;
  7 +import org.springframework.ai.chat.messages.UserMessage;
  8 +import org.springframework.ai.chat.model.ChatResponse;
  9 +import org.springframework.ai.chat.prompt.Prompt;
3 import org.springframework.ai.ollama.OllamaChatModel; 10 import org.springframework.ai.ollama.OllamaChatModel;
  11 +import org.springframework.ai.ollama.api.OllamaApi;
  12 +import org.springframework.ai.ollama.api.OllamaModel;
  13 +import org.springframework.ai.ollama.api.OllamaOptions;
  14 +import reactor.core.publisher.Flux;
  15 +
  16 +import java.util.ArrayList;
  17 +import java.util.List;
4 18
5 /** 19 /**
6 * {@link OllamaChatModel} 集成测试 20 * {@link OllamaChatModel} 集成测试
@@ -9,43 +23,65 @@ import org.springframework.ai.ollama.OllamaChatModel; @@ -9,43 +23,65 @@ import org.springframework.ai.ollama.OllamaChatModel;
9 */ 23 */
10 public class LlamaChatModelTests { 24 public class LlamaChatModelTests {
11 25
12 -// private final OllamaChatModel chatModel = OllamaChatModel.builder()  
13 -// .ollamaApi(new OllamaApi("http://127.0.0.1:11434")) // Ollama 服务地址  
14 -// .defaultOptions(OllamaOptions.builder()  
15 -// .model(OllamaModel.LLAMA3.getName()) // 模型  
16 -// .build())  
17 -// .build();  
18 -//  
19 -// @Test  
20 -// @Disabled  
21 -// public void testCall() {  
22 -// // 准备参数  
23 -// List<Message> messages = new ArrayList<>();  
24 -// messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));  
25 -// messages.add(new UserMessage("1 + 1 = ?"));  
26 -//  
27 -// // 调用  
28 -// ChatResponse response = chatModel.call(new Prompt(messages));  
29 -// // 打印结果  
30 -// System.out.println(response);  
31 -// System.out.println(response.getResult().getOutput());  
32 -// }  
33 -//  
34 -// @Test  
35 -// @Disabled  
36 -// public void testStream() {  
37 -// // 准备参数  
38 -// List<Message> messages = new ArrayList<>();  
39 -// messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));  
40 -// messages.add(new UserMessage("1 + 1 = ?"));  
41 -//  
42 -// // 调用  
43 -// Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));  
44 -// // 打印结果  
45 -// flux.doOnNext(response -> {  
46 -//// System.out.println(response);  
47 -// System.out.println(response.getResult().getOutput());  
48 -// }).then().block();  
49 -// } 26 + private final OllamaChatModel chatModel = OllamaChatModel.builder()
  27 + .ollamaApi(OllamaApi.builder()
  28 + .baseUrl("http://127.0.0.1:11434") // Ollama 服务地址
  29 + .build())
  30 + .defaultOptions(OllamaOptions.builder()
  31 + .model(OllamaModel.LLAMA3.getName()) // 模型
  32 + .build())
  33 + .build();
  34 +
  35 + @Test
  36 + @Disabled
  37 + public void testCall() {
  38 + // 准备参数
  39 + List<Message> messages = new ArrayList<>();
  40 + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
  41 + messages.add(new UserMessage("1 + 1 = ?"));
  42 +
  43 + // 调用
  44 + ChatResponse response = chatModel.call(new Prompt(messages));
  45 + // 打印结果
  46 + System.out.println(response);
  47 + System.out.println(response.getResult().getOutput());
  48 + }
  49 +
  50 + @Test
  51 + @Disabled
  52 + public void testStream() {
  53 + // 准备参数
  54 + List<Message> messages = new ArrayList<>();
  55 + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
  56 + messages.add(new UserMessage("1 + 1 = ?"));
  57 +
  58 + // 调用
  59 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
  60 + // 打印结果
  61 + flux.doOnNext(response -> {
  62 +// System.out.println(response);
  63 + System.out.println(response.getResult().getOutput());
  64 + }).then().block();
  65 + }
  66 +
  67 + @Test
  68 + @Disabled
  69 + public void testStream_thinking() {
  70 + // 准备参数
  71 + List<Message> messages = new ArrayList<>();
  72 + messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
  73 + OllamaOptions options = OllamaOptions.builder()
  74 + .model("qwen3")
  75 + .build();
  76 +
  77 + // 调用
  78 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
  79 + // 打印结果
  80 + flux.doOnNext(response -> {
  81 +// System.out.println(response);
  82 + System.out.println(response.getResult().getOutput());
  83 + }).then().block();
  84 + }
  85 +
50 86
51 } 87 }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MiniMaxChatModelTests.java
@@ -59,4 +59,24 @@ public class MiniMaxChatModelTests { @@ -59,4 +59,24 @@ public class MiniMaxChatModelTests {
59 }).then().block(); 59 }).then().block();
60 } 60 }
61 61
  62 + // TODO @芋艿:暂时没解析 reasoning_content 结果,需要等官方修复
  63 + @Test
  64 + @Disabled
  65 + public void testStream_thinking() {
  66 + // 准备参数
  67 + List<Message> messages = new ArrayList<>();
  68 + messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
  69 + MiniMaxChatOptions options = MiniMaxChatOptions.builder()
  70 + .model("MiniMax-M1")
  71 + .build();
  72 +
  73 + // 调用
  74 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
  75 + // 打印结果
  76 + flux.doOnNext(response -> {
  77 +// System.out.println(response);
  78 + System.out.println(response.getResult().getOutput());
  79 + }).then().block();
  80 + }
  81 +
62 } 82 }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java
@@ -63,4 +63,25 @@ public class MoonshotChatModelTests { @@ -63,4 +63,25 @@ public class MoonshotChatModelTests {
63 }).then().block(); 63 }).then().block();
64 } 64 }
65 65
  66 + // TODO @芋艿:暂时没解析 reasoning_content 结果,需要等官方修复
  67 + @Test
  68 + @Disabled
  69 + public void testStream_thinking() {
  70 + // 准备参数
  71 + List<Message> messages = new ArrayList<>();
  72 + messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
  73 + MoonshotChatOptions options = MoonshotChatOptions.builder()
  74 +// .model("kimi-k2-0711-preview")
  75 + .model("kimi-thinking-preview")
  76 + .build();
  77 +
  78 + // 调用
  79 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
  80 + // 打印结果
  81 + flux.doOnNext(response -> {
  82 +// System.out.println(response);
  83 + System.out.println(response.getResult().getOutput());
  84 + }).then().block();
  85 + }
  86 +
66 } 87 }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java
1 package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; 1 package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
2 2
  3 +import com.azure.ai.openai.models.ReasoningEffortValue;
3 import org.junit.jupiter.api.Disabled; 4 import org.junit.jupiter.api.Disabled;
4 import org.junit.jupiter.api.Test; 5 import org.junit.jupiter.api.Test;
5 import org.springframework.ai.chat.messages.Message; 6 import org.springframework.ai.chat.messages.Message;
@@ -25,10 +26,11 @@ public class OpenAIChatModelTests { @@ -25,10 +26,11 @@ public class OpenAIChatModelTests {
25 private final OpenAiChatModel chatModel = OpenAiChatModel.builder() 26 private final OpenAiChatModel chatModel = OpenAiChatModel.builder()
26 .openAiApi(OpenAiApi.builder() 27 .openAiApi(OpenAiApi.builder()
27 .baseUrl("https://api.holdai.top") 28 .baseUrl("https://api.holdai.top")
28 - .apiKey("sk-PytRecQlmjEteoa2RRN6cGnwslo72UUPLQVNEMS6K9yjbmpD") // apiKey 29 + .apiKey("sk-z5joyRoV1iFEnh2SAi8QPNrIZTXyQSyxTmD5CoNDQbFixK2l") // apiKey
29 .build()) 30 .build())
30 .defaultOptions(OpenAiChatOptions.builder() 31 .defaultOptions(OpenAiChatOptions.builder()
31 - .model(OpenAiApi.ChatModel.GPT_4_1_NANO) // 模型 32 + .model("gpt-5-nano-2025-08-07") // 模型
  33 +// .model(OpenAiApi.ChatModel.O1) // 模型
32 .temperature(0.7) 34 .temperature(0.7)
33 .build()) 35 .build())
34 .build(); 36 .build();
@@ -54,7 +56,7 @@ public class OpenAIChatModelTests { @@ -54,7 +56,7 @@ public class OpenAIChatModelTests {
54 // 准备参数 56 // 准备参数
55 List<Message> messages = new ArrayList<>(); 57 List<Message> messages = new ArrayList<>();
56 messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); 58 messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
57 - messages.add(new UserMessage("1 + 1 = ?")); 59 + messages.add(new UserMessage("帮我推理下,怎么实现一个用户中心!"));
58 60
59 // 调用 61 // 调用
60 Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages)); 62 Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
@@ -65,4 +67,29 @@ public class OpenAIChatModelTests { @@ -65,4 +67,29 @@ public class OpenAIChatModelTests {
65 }).then().block(); 67 }).then().block();
66 } 68 }
67 69
  70 + // TODO @芋艿:无法触发思考的字段返回,需要 response api:https://github.com/spring-projects/spring-ai/issues/2962
  71 + @Test
  72 + @Disabled
  73 + public void testStream_thinking() {
  74 + // 准备参数
  75 + List<Message> messages = new ArrayList<>();
  76 + messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
  77 + OpenAiChatOptions options = OpenAiChatOptions.builder()
  78 + .model("gpt-5")
  79 +// .model(OpenAiApi.ChatModel.O4_MINI)
  80 +// .model("o3-pro")
  81 + .reasoningEffort(ReasoningEffortValue.LOW.getValue())
  82 + .build();
  83 +
  84 + // 调用
  85 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
  86 + // 打印结果
  87 + flux.doOnNext(response -> {
  88 +// System.out.println(response);
  89 + System.out.println(response.getResult().getOutput());
  90 + }).then().block();
  91 + }
  92 +
  93 +
  94 +
68 } 95 }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/SiliconFlowChatModelTests.java
@@ -9,9 +9,9 @@ import org.springframework.ai.chat.messages.SystemMessage; @@ -9,9 +9,9 @@ import org.springframework.ai.chat.messages.SystemMessage;
9 import org.springframework.ai.chat.messages.UserMessage; 9 import org.springframework.ai.chat.messages.UserMessage;
10 import org.springframework.ai.chat.model.ChatResponse; 10 import org.springframework.ai.chat.model.ChatResponse;
11 import org.springframework.ai.chat.prompt.Prompt; 11 import org.springframework.ai.chat.prompt.Prompt;
12 -import org.springframework.ai.openai.OpenAiChatModel;  
13 -import org.springframework.ai.openai.OpenAiChatOptions;  
14 -import org.springframework.ai.openai.api.OpenAiApi; 12 +import org.springframework.ai.deepseek.DeepSeekChatModel;
  13 +import org.springframework.ai.deepseek.DeepSeekChatOptions;
  14 +import org.springframework.ai.deepseek.api.DeepSeekApi;
15 import reactor.core.publisher.Flux; 15 import reactor.core.publisher.Flux;
16 16
17 import java.util.ArrayList; 17 import java.util.ArrayList;
@@ -24,12 +24,12 @@ import java.util.List; @@ -24,12 +24,12 @@ import java.util.List;
24 */ 24 */
25 public class SiliconFlowChatModelTests { 25 public class SiliconFlowChatModelTests {
26 26
27 - private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()  
28 - .openAiApi(OpenAiApi.builder() 27 + private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
  28 + .deepSeekApi(DeepSeekApi.builder()
29 .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL) 29 .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL)
30 .apiKey("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // apiKey 30 .apiKey("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // apiKey
31 .build()) 31 .build())
32 - .defaultOptions(OpenAiChatOptions.builder() 32 + .defaultOptions(DeepSeekChatOptions.builder()
33 .model(SiliconFlowApiConstants.MODEL_DEFAULT) // 模型 33 .model(SiliconFlowApiConstants.MODEL_DEFAULT) // 模型
34 // .model("deepseek-ai/DeepSeek-R1") // 模型(deepseek-ai/DeepSeek-R1)可用赠费 34 // .model("deepseek-ai/DeepSeek-R1") // 模型(deepseek-ai/DeepSeek-R1)可用赠费
35 // .model("Pro/deepseek-ai/DeepSeek-R1") // 模型(Pro/deepseek-ai/DeepSeek-R1)需要付费 35 // .model("Pro/deepseek-ai/DeepSeek-R1") // 模型(Pro/deepseek-ai/DeepSeek-R1)需要付费
@@ -67,4 +67,23 @@ public class SiliconFlowChatModelTests { @@ -67,4 +67,23 @@ public class SiliconFlowChatModelTests {
67 flux.doOnNext(System.out::println).then().block(); 67 flux.doOnNext(System.out::println).then().block();
68 } 68 }
69 69
  70 + @Test
  71 + @Disabled
  72 + public void testStream_thinking() {
  73 + // 准备参数
  74 + List<Message> messages = new ArrayList<>();
  75 + messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
  76 + DeepSeekChatOptions options = DeepSeekChatOptions.builder()
  77 + .model("deepseek-ai/DeepSeek-R1")
  78 + .build();
  79 +
  80 + // 调用
  81 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
  82 + // 打印结果
  83 + flux.doOnNext(response -> {
  84 +// System.out.println(response);
  85 + System.out.println(response.getResult().getOutput());
  86 + }).then().block();
  87 + }
  88 +
70 } 89 }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java
1 package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; 1 package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
2 2
  3 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
3 import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; 4 import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
4 import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; 5 import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
5 import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; 6 import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
  7 +import com.alibaba.cloud.ai.dashscope.rerank.DashScopeRerankModel;
  8 +import com.alibaba.cloud.ai.dashscope.rerank.DashScopeRerankOptions;
  9 +import com.alibaba.cloud.ai.model.RerankModel;
  10 +import com.alibaba.cloud.ai.model.RerankOptions;
  11 +import com.alibaba.cloud.ai.model.RerankRequest;
  12 +import com.alibaba.cloud.ai.model.RerankResponse;
6 import org.junit.jupiter.api.Disabled; 13 import org.junit.jupiter.api.Disabled;
7 import org.junit.jupiter.api.Test; 14 import org.junit.jupiter.api.Test;
8 import org.springframework.ai.chat.messages.Message; 15 import org.springframework.ai.chat.messages.Message;
@@ -10,11 +17,14 @@ import org.springframework.ai.chat.messages.SystemMessage; @@ -10,11 +17,14 @@ import org.springframework.ai.chat.messages.SystemMessage;
10 import org.springframework.ai.chat.messages.UserMessage; 17 import org.springframework.ai.chat.messages.UserMessage;
11 import org.springframework.ai.chat.model.ChatResponse; 18 import org.springframework.ai.chat.model.ChatResponse;
12 import org.springframework.ai.chat.prompt.Prompt; 19 import org.springframework.ai.chat.prompt.Prompt;
  20 +import org.springframework.ai.document.Document;
13 import reactor.core.publisher.Flux; 21 import reactor.core.publisher.Flux;
14 22
15 import java.util.ArrayList; 23 import java.util.ArrayList;
16 import java.util.List; 24 import java.util.List;
17 25
  26 +import static java.util.Arrays.asList;
  27 +
18 /** 28 /**
19 * {@link DashScopeChatModel} 集成测试类 29 * {@link DashScopeChatModel} 集成测试类
20 * 30 *
@@ -26,11 +36,13 @@ public class TongYiChatModelTests { @@ -26,11 +36,13 @@ public class TongYiChatModelTests {
26 .dashScopeApi(DashScopeApi.builder() 36 .dashScopeApi(DashScopeApi.builder()
27 .apiKey("sk-47aa124781be4bfb95244cc62f63f7d0") 37 .apiKey("sk-47aa124781be4bfb95244cc62f63f7d0")
28 .build()) 38 .build())
29 - .defaultOptions( DashScopeChatOptions.builder()  
30 - .withModel("qwen1.5-72b-chat") // 模型 39 + .defaultOptions(DashScopeChatOptions.builder()
  40 +// .withModel("qwen1.5-72b-chat") // 模型
  41 + .withModel("qwen3-235b-a22b-thinking-2507") // 模型
31 // .withModel("deepseek-r1") // 模型(deepseek-r1) 42 // .withModel("deepseek-r1") // 模型(deepseek-r1)
32 // .withModel("deepseek-v3") // 模型(deepseek-v3) 43 // .withModel("deepseek-v3") // 模型(deepseek-v3)
33 // .withModel("deepseek-r1-distill-qwen-1.5b") // 模型(deepseek-r1-distill-qwen-1.5b) 44 // .withModel("deepseek-r1-distill-qwen-1.5b") // 模型(deepseek-r1-distill-qwen-1.5b)
  45 +// .withEnableThinking(true)
34 .build()) 46 .build())
35 .build(); 47 .build();
36 48
@@ -54,8 +66,8 @@ public class TongYiChatModelTests { @@ -54,8 +66,8 @@ public class TongYiChatModelTests {
54 public void testStream() { 66 public void testStream() {
55 // 准备参数 67 // 准备参数
56 List<Message> messages = new ArrayList<>(); 68 List<Message> messages = new ArrayList<>();
57 - messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));  
58 - messages.add(new UserMessage("1 + 1 = ?")); 69 +// messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
  70 + messages.add(new UserMessage("帮我推理下,怎么实现一个用户中心!"));
59 71
60 // 调用 72 // 调用
61 Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages)); 73 Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
@@ -66,4 +78,52 @@ public class TongYiChatModelTests { @@ -66,4 +78,52 @@ public class TongYiChatModelTests {
66 }).then().block(); 78 }).then().block();
67 } 79 }
68 80
  81 + @Test
  82 + @Disabled
  83 + public void testStream_thinking() {
  84 + // 准备参数
  85 + List<Message> messages = new ArrayList<>();
  86 + messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
  87 + DashScopeChatOptions options = DashScopeChatOptions.builder()
  88 + .withModel("qwen3-235b-a22b-thinking-2507")
  89 +// .withModel("qwen-max-2025-01-25")
  90 + .withEnableThinking(true) // 必须设置,否则会报错
  91 + .build();
  92 +
  93 + // 调用
  94 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
  95 + // 打印结果
  96 + flux.doOnNext(response -> {
  97 +// System.out.println(response);
  98 + System.out.println(response.getResult().getOutput());
  99 + }).then().block();
  100 + }
  101 +
  102 + @Test
  103 + @Disabled
  104 + public void testRerank() {
  105 + // 准备环境
  106 + RerankModel rerankModel = new DashScopeRerankModel(
  107 + DashScopeApi.builder()
  108 + .apiKey("sk-47aa124781be4bfb95244cc62f63f7d0")
  109 + .build());
  110 + // 准备参数
  111 + String query = "spring";
  112 + Document document01 = new Document("abc");
  113 + Document document02 = new Document("sapring");
  114 + RerankOptions options = DashScopeRerankOptions.builder()
  115 + .withTopN(1)
  116 + .withModel("gte-rerank-v2")
  117 + .build();
  118 + RerankRequest rerankRequest = new RerankRequest(
  119 + query,
  120 + asList(document01, document02),
  121 + options);
  122 +
  123 + // 调用
  124 + RerankResponse call = rerankModel.call(rerankRequest);
  125 + // 打印结果
  126 + System.out.println(JsonUtils.toJsonPrettyString(call));
  127 + }
  128 +
69 } 129 }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/XingHuoChatModelTests.java
@@ -8,9 +8,9 @@ import org.springframework.ai.chat.messages.SystemMessage; @@ -8,9 +8,9 @@ import org.springframework.ai.chat.messages.SystemMessage;
8 import org.springframework.ai.chat.messages.UserMessage; 8 import org.springframework.ai.chat.messages.UserMessage;
9 import org.springframework.ai.chat.model.ChatResponse; 9 import org.springframework.ai.chat.model.ChatResponse;
10 import org.springframework.ai.chat.prompt.Prompt; 10 import org.springframework.ai.chat.prompt.Prompt;
11 -import org.springframework.ai.openai.OpenAiChatModel;  
12 -import org.springframework.ai.openai.OpenAiChatOptions;  
13 -import org.springframework.ai.openai.api.OpenAiApi; 11 +import org.springframework.ai.deepseek.DeepSeekChatModel;
  12 +import org.springframework.ai.deepseek.DeepSeekChatOptions;
  13 +import org.springframework.ai.deepseek.api.DeepSeekApi;
14 import reactor.core.publisher.Flux; 14 import reactor.core.publisher.Flux;
15 15
16 import java.util.ArrayList; 16 import java.util.ArrayList;
@@ -23,13 +23,15 @@ import java.util.List; @@ -23,13 +23,15 @@ import java.util.List;
23 */ 23 */
24 public class XingHuoChatModelTests { 24 public class XingHuoChatModelTests {
25 25
26 - private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()  
27 - .openAiApi(OpenAiApi.builder()  
28 - .baseUrl(XingHuoChatModel.BASE_URL) 26 + private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
  27 + .deepSeekApi(DeepSeekApi.builder()
  28 + .baseUrl(XingHuoChatModel.BASE_URL_V2)
  29 + .completionsPath(XingHuoChatModel.BASE_COMPLETIONS_PATH_V2)
29 .apiKey("75b161ed2aef4719b275d6e7f2a4d4cd:YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz") // appKey:secretKey 30 .apiKey("75b161ed2aef4719b275d6e7f2a4d4cd:YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz") // appKey:secretKey
30 .build()) 31 .build())
31 - .defaultOptions(OpenAiChatOptions.builder()  
32 - .model("generalv3.5") // 模型 32 + .defaultOptions(DeepSeekChatOptions.builder()
  33 +// .model("generalv3.5") // 模型
  34 + .model("x1") // 模型
33 .temperature(0.7) 35 .temperature(0.7)
34 .build()) 36 .build())
35 .build(); 37 .build();
@@ -64,4 +66,23 @@ public class XingHuoChatModelTests { @@ -64,4 +66,23 @@ public class XingHuoChatModelTests {
64 flux.doOnNext(System.out::println).then().block(); 66 flux.doOnNext(System.out::println).then().block();
65 } 67 }
66 68
  69 + @Test
  70 + @Disabled
  71 + public void testStream_thinking() {
  72 + // 准备参数
  73 + List<Message> messages = new ArrayList<>();
  74 + messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
  75 + DeepSeekChatOptions options = DeepSeekChatOptions.builder()
  76 + .model("x1")
  77 + .build();
  78 +
  79 + // 调用
  80 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
  81 + // 打印结果
  82 + flux.doOnNext(response -> {
  83 +// System.out.println(response);
  84 + System.out.println(response.getResult().getOutput());
  85 + }).then().block();
  86 + }
  87 +
67 } 88 }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/ZhiPuAiChatModelTests.java
@@ -23,7 +23,7 @@ import java.util.List; @@ -23,7 +23,7 @@ import java.util.List;
23 public class ZhiPuAiChatModelTests { 23 public class ZhiPuAiChatModelTests {
24 24
25 private final ZhiPuAiChatModel chatModel = new ZhiPuAiChatModel( 25 private final ZhiPuAiChatModel chatModel = new ZhiPuAiChatModel(
26 - new ZhiPuAiApi("32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs"), // 密钥 26 + new ZhiPuAiApi("2f35fb6ca4ea41fab898729b7fac086c.6ESSfPcCkxaKEUlR"), // 密钥
27 ZhiPuAiChatOptions.builder() 27 ZhiPuAiChatOptions.builder()
28 .model(ZhiPuAiApi.ChatModel.GLM_4.getName()) // 模型 28 .model(ZhiPuAiApi.ChatModel.GLM_4.getName()) // 模型
29 .build() 29 .build()
@@ -61,4 +61,24 @@ public class ZhiPuAiChatModelTests { @@ -61,4 +61,24 @@ public class ZhiPuAiChatModelTests {
61 }).then().block(); 61 }).then().block();
62 } 62 }
63 63
  64 + // TODO @芋艿:暂时没解析 reasoning_content 结果,需要等官方修复
  65 + @Test
  66 + @Disabled
  67 + public void testStream_thinking() {
  68 + // 准备参数
  69 + List<Message> messages = new ArrayList<>();
  70 + messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
  71 + ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder()
  72 + .model("GLM-4.5")
  73 + .build();
  74 +
  75 + // 调用
  76 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
  77 + // 打印结果
  78 + flux.doOnNext(response -> {
  79 +// System.out.println(response);
  80 + System.out.println(response.getResult().getOutput());
  81 + }).then().block();
  82 + }
  83 +
64 } 84 }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/TongYiImagesModelTest.java
@@ -16,8 +16,11 @@ import org.springframework.ai.image.ImageResponse; @@ -16,8 +16,11 @@ import org.springframework.ai.image.ImageResponse;
16 */ 16 */
17 public class TongYiImagesModelTest { 17 public class TongYiImagesModelTest {
18 18
19 - private final DashScopeImageModel imageModel = new DashScopeImageModel(  
20 - new DashScopeImageApi("sk-7d903764249848cfa912733146da12d1")); 19 + private final DashScopeImageModel imageModel = DashScopeImageModel.builder()
  20 + .dashScopeApi(DashScopeImageApi.builder()
  21 + .apiKey("sk-47aa124781be4bfb95244cc62f63f7d0")
  22 + .build())
  23 + .build();
21 24
22 @Test 25 @Test
23 @Disabled 26 @Disabled
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/mcp/DouBaoMcpTests.java
@@ -36,44 +36,47 @@ public class DouBaoMcpTests { @@ -36,44 +36,47 @@ public class DouBaoMcpTests {
36 36
37 @Test 37 @Test
38 public void testMcpGetUserInfo() { 38 public void testMcpGetUserInfo() {
39 -  
40 // 打印结果 39 // 打印结果
41 System.out.println(chatClient.prompt() 40 System.out.println(chatClient.prompt()
42 .user("目前有哪些工具可以使用") 41 .user("目前有哪些工具可以使用")
43 .call() 42 .call()
44 .content()); 43 .content());
45 System.out.println("===================================="); 44 System.out.println("====================================");
  45 +
46 // 打印结果 46 // 打印结果
47 System.out.println(chatClient.prompt() 47 System.out.println(chatClient.prompt()
48 .user("小新的年龄是多少") 48 .user("小新的年龄是多少")
49 .call() 49 .call()
50 .content()); 50 .content());
51 System.out.println("===================================="); 51 System.out.println("====================================");
  52 +
52 // 打印结果 53 // 打印结果
53 System.out.println(chatClient.prompt() 54 System.out.println(chatClient.prompt()
54 .user("获取小新的基本信息") 55 .user("获取小新的基本信息")
55 .call() 56 .call()
56 .content()); 57 .content());
57 System.out.println("===================================="); 58 System.out.println("====================================");
  59 +
58 // 打印结果 60 // 打印结果
59 System.out.println(chatClient.prompt() 61 System.out.println(chatClient.prompt()
60 .user("小新是什么职业的") 62 .user("小新是什么职业的")
61 .call() 63 .call()
62 .content()); 64 .content());
63 System.out.println("===================================="); 65 System.out.println("====================================");
  66 +
64 // 打印结果 67 // 打印结果
65 System.out.println(chatClient.prompt() 68 System.out.println(chatClient.prompt()
66 .user("小新的教育背景") 69 .user("小新的教育背景")
67 .call() 70 .call()
68 .content()); 71 .content());
69 System.out.println("===================================="); 72 System.out.println("====================================");
  73 +
70 // 打印结果 74 // 打印结果
71 System.out.println(chatClient.prompt() 75 System.out.println(chatClient.prompt()
72 .user("小新的兴趣爱好是什么") 76 .user("小新的兴趣爱好是什么")
73 .call() 77 .call()
74 .content()); 78 .content());
75 System.out.println("===================================="); 79 System.out.println("====================================");
76 -  
77 } 80 }
78 81
79 82
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/websearch/AiBoChaWebSearchClientTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.framework.ai.core.websearch;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  4 +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchRequest;
  5 +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;
  6 +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha.AiBoChaWebSearchClient;
  7 +import org.junit.jupiter.api.Test;
  8 +
  9 +/**
  10 + * {@link AiBoChaWebSearchClient} 集成测试类
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public class AiBoChaWebSearchClientTest {
  15 +
  16 + private final AiBoChaWebSearchClient webSearchClient = new AiBoChaWebSearchClient(
  17 + "sk-40500e52840f4d24b956d0b1d80d9abe");
  18 +
  19 + @Test
  20 + public void testSearch() {
  21 + AiWebSearchRequest request = new AiWebSearchRequest()
  22 + .setQuery("阿里巴巴")
  23 + .setCount(3);
  24 + AiWebSearchResponse response = webSearchClient.search(request);
  25 + System.out.println(JsonUtils.toJsonPrettyString(response));
  26 + }
  27 +
  28 +}
0 \ No newline at end of file 29 \ No newline at end of file
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/event/BpmProcessInstanceStatusEvent.java
@@ -29,6 +29,11 @@ public class BpmProcessInstanceStatusEvent extends ApplicationEvent { @@ -29,6 +29,11 @@ public class BpmProcessInstanceStatusEvent extends ApplicationEvent {
29 @NotNull(message = "流程实例的状态不能为空") 29 @NotNull(message = "流程实例的状态不能为空")
30 private Integer status; 30 private Integer status;
31 /** 31 /**
  32 + * 流程实例结束的原因
  33 + */
  34 + private String reason;
  35 +
  36 + /**
32 * 流程实例对应的业务标识 37 * 流程实例对应的业务标识
33 * 例如说,请假 38 * 例如说,请假
34 */ 39 */
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
@@ -122,8 +122,9 @@ public interface BpmProcessInstanceConvert { @@ -122,8 +122,9 @@ public interface BpmProcessInstanceConvert {
122 @Mapping(source = "from.id", target = "to.id", ignore = true) 122 @Mapping(source = "from.id", target = "to.id", ignore = true)
123 void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to); 123 void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to);
124 124
125 - default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) {  
126 - return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status) 125 + default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance,
  126 + Integer status, String reason) {
  127 + return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status).setReason(reason)
127 .setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey()); 128 .setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey());
128 } 129 }
129 130
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/redis/BpmProcessIdRedisDAO.java
@@ -51,8 +51,8 @@ public class BpmProcessIdRedisDAO { @@ -51,8 +51,8 @@ public class BpmProcessIdRedisDAO {
51 String noPrefix = processIdRule.getPrefix() + infix + processIdRule.getPostfix(); 51 String noPrefix = processIdRule.getPrefix() + infix + processIdRule.getPostfix();
52 String key = RedisKeyConstants.BPM_PROCESS_ID + noPrefix; 52 String key = RedisKeyConstants.BPM_PROCESS_ID + noPrefix;
53 Long no = stringRedisTemplate.opsForValue().increment(key); 53 Long no = stringRedisTemplate.opsForValue().increment(key);
54 - if (StrUtil.isNotEmpty(infix)) {  
55 - // 特殊:没有前缀,则不能过期,不能每次都是从 0 开始 54 + if (StrUtil.isEmpty(infix)) {
  55 + // 特殊:没有前缀,则不能过期,不能每次都是从 0 开始。可见 https://t.zsxq.com/MU1E2 讨论
56 stringRedisTemplate.expire(key, Duration.ofDays(1L)); 56 stringRedisTemplate.expire(key, Duration.ofDays(1L));
57 } 57 }
58 return noPrefix + String.format("%0" + processIdRule.getLength() + "d", no); 58 return noPrefix + String.format("%0" + processIdRule.getLength() + "d", no);
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
@@ -737,10 +737,10 @@ public class SimpleModelUtils { @@ -737,10 +737,10 @@ public class SimpleModelUtils {
737 BoundaryEvent boundaryEvent = null; 737 BoundaryEvent boundaryEvent = null;
738 if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { 738 if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) {
739 boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), 739 boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(),
740 - node.getDelaySetting().getDelayTime(), null, null); 740 + null, null, node.getDelaySetting().getDelayTime());
741 } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { 741 } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) {
742 boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), 742 boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(),
743 - null, null, node.getDelaySetting().getDelayTime()); 743 + node.getDelaySetting().getDelayTime(), null, null);
744 } else { 744 } else {
745 throw new UnsupportedOperationException("不支持的延迟类型:" + node.getDelaySetting()); 745 throw new UnsupportedOperationException("不支持的延迟类型:" + node.getDelaySetting());
746 } 746 }
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
@@ -960,7 +960,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -960,7 +960,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
960 // 1.3.1 获取父流程实例 并标记为不通过 960 // 1.3.1 获取父流程实例 并标记为不通过
961 Execution execution = runtimeService.createExecutionQuery().executionId(instance.getSuperExecutionId()).singleResult(); 961 Execution execution = runtimeService.createExecutionQuery().executionId(instance.getSuperExecutionId()).singleResult();
962 ProcessInstance parentProcessInstance = getProcessInstance(execution.getProcessInstanceId()); 962 ProcessInstance parentProcessInstance = getProcessInstance(execution.getProcessInstanceId());
963 - updateProcessInstanceReject(parentProcessInstance, REJECT_CHILD_PROCESS.getReason()); 963 + updateProcessInstanceReject(parentProcessInstance, BpmReasonEnum.REJECT_CHILD_PROCESS.getReason());
964 964
965 // 1.3.2 结束父流程。需要在子流程结束事务提交后执行 965 // 1.3.2 结束父流程。需要在子流程结束事务提交后执行
966 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { 966 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@@ -971,7 +971,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -971,7 +971,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
971 if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) { 971 if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) {
972 return; 972 return;
973 } 973 }
974 - taskService.moveTaskToEnd(parentProcessInstance.getId(),REJECT_CHILD_PROCESS.getReason()); 974 + taskService.moveTaskToEnd(parentProcessInstance.getId(), BpmReasonEnum.REJECT_CHILD_PROCESS.getReason());
975 } 975 }
976 }); 976 });
977 } 977 }
@@ -987,7 +987,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -987,7 +987,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
987 987
988 // 3. 发送流程实例的状态事件 988 // 3. 发送流程实例的状态事件
989 processInstanceEventPublisher.sendProcessInstanceResultEvent( 989 processInstanceEventPublisher.sendProcessInstanceResultEvent(
990 - BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status)); 990 + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status, reason));
991 991
992 // 4. 流程后置通知 992 // 4. 流程后置通知
993 if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { 993 if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java
@@ -7,6 +7,11 @@ import io.swagger.v3.oas.annotations.media.Schema; @@ -7,6 +7,11 @@ import io.swagger.v3.oas.annotations.media.Schema;
7 import lombok.Data; 7 import lombok.Data;
8 import lombok.EqualsAndHashCode; 8 import lombok.EqualsAndHashCode;
9 import lombok.ToString; 9 import lombok.ToString;
  10 +import org.springframework.format.annotation.DateTimeFormat;
  11 +
  12 +import java.time.LocalDateTime;
  13 +
  14 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
10 15
11 @Schema(description = "管理后台 - 线索分页 Request VO") 16 @Schema(description = "管理后台 - 线索分页 Request VO")
12 @Data 17 @Data
@@ -42,4 +47,8 @@ public class CrmCluePageReqVO extends PageParam { @@ -42,4 +47,8 @@ public class CrmCluePageReqVO extends PageParam {
42 @Schema(description = "跟进状态", example = "true") 47 @Schema(description = "跟进状态", example = "true")
43 private Boolean followUpStatus; 48 private Boolean followUpStatus;
44 49
  50 + @Schema(description = "创建时间", example = "[2023-01-01 00:00:00, 2023-01-31 23:59:59]")
  51 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  52 + private LocalDateTime[] createTime;
  53 +
45 } 54 }
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java
@@ -10,7 +10,6 @@ import lombok.AllArgsConstructor; @@ -10,7 +10,6 @@ import lombok.AllArgsConstructor;
10 import lombok.Builder; 10 import lombok.Builder;
11 import lombok.Data; 11 import lombok.Data;
12 import lombok.NoArgsConstructor; 12 import lombok.NoArgsConstructor;
13 -import lombok.experimental.Accessors;  
14 13
15 import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; 14 import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*;
16 15
@@ -21,7 +20,6 @@ import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; @@ -21,7 +20,6 @@ import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*;
21 @Builder 20 @Builder
22 @AllArgsConstructor 21 @AllArgsConstructor
23 @NoArgsConstructor 22 @NoArgsConstructor
24 -@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题  
25 public class CrmCustomerImportExcelVO { 23 public class CrmCustomerImportExcelVO {
26 24
27 @ExcelProperty("客户名称") 25 @ExcelProperty("客户名称")
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java
@@ -33,6 +33,7 @@ public interface CrmClueMapper extends BaseMapperX&lt;CrmClueDO&gt; { @@ -33,6 +33,7 @@ public interface CrmClueMapper extends BaseMapperX&lt;CrmClueDO&gt; {
33 .eqIfPresent(CrmClueDO::getLevel, pageReqVO.getLevel()) 33 .eqIfPresent(CrmClueDO::getLevel, pageReqVO.getLevel())
34 .eqIfPresent(CrmClueDO::getSource, pageReqVO.getSource()) 34 .eqIfPresent(CrmClueDO::getSource, pageReqVO.getSource())
35 .eqIfPresent(CrmClueDO::getFollowUpStatus, pageReqVO.getFollowUpStatus()) 35 .eqIfPresent(CrmClueDO::getFollowUpStatus, pageReqVO.getFollowUpStatus())
  36 + .betweenIfPresent(CrmClueDO::getCreateTime, pageReqVO.getCreateTime())
36 .orderByDesc(CrmClueDO::getId); 37 .orderByDesc(CrmClueDO::getId);
37 return selectJoinPage(pageReqVO, CrmClueDO.class, query); 38 return selectJoinPage(pageReqVO, CrmClueDO.class, query);
38 } 39 }
yudao-module-infra/pom.xml
@@ -101,7 +101,7 @@ @@ -101,7 +101,7 @@
101 <artifactId>commons-net</artifactId> <!-- 文件客户端:解决 ftp 连接 --> 101 <artifactId>commons-net</artifactId> <!-- 文件客户端:解决 ftp 连接 -->
102 </dependency> 102 </dependency>
103 <dependency> 103 <dependency>
104 - <groupId>com.jcraft</groupId> 104 + <groupId>com.github.mwiede</groupId>
105 <artifactId>jsch</artifactId> <!-- 文件客户端:解决 sftp 连接 --> 105 <artifactId>jsch</artifactId> <!-- 文件客户端:解决 sftp 连接 -->
106 </dependency> 106 </dependency>
107 <!-- 文件客户端:解决阿里云、腾讯云、minio 等 S3 连接 --> 107 <!-- 文件客户端:解决阿里云、腾讯云、minio 等 S3 连接 -->
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java
@@ -26,6 +26,7 @@ import org.springframework.validation.annotation.Validated; @@ -26,6 +26,7 @@ import org.springframework.validation.annotation.Validated;
26 import org.springframework.web.bind.annotation.*; 26 import org.springframework.web.bind.annotation.*;
27 import org.springframework.web.multipart.MultipartFile; 27 import org.springframework.web.multipart.MultipartFile;
28 28
  29 +import java.nio.charset.StandardCharsets;
29 import java.util.List; 30 import java.util.List;
30 31
31 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 32 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -99,8 +100,10 @@ public class FileController { @@ -99,8 +100,10 @@ public class FileController {
99 if (StrUtil.isEmpty(path)) { 100 if (StrUtil.isEmpty(path)) {
100 throw new IllegalArgumentException("结尾的 path 路径必须传递"); 101 throw new IllegalArgumentException("结尾的 path 路径必须传递");
101 } 102 }
102 - // 解码,解决中文路径的问题 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/807/  
103 - path = URLUtil.decode(path); 103 + // 解码,解决中文路径的问题
  104 + // https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/807/
  105 + // https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1432/
  106 + path = URLUtil.decode(path, StandardCharsets.UTF_8, false);
104 107
105 // 读取内容 108 // 读取内容
106 byte[] content = fileService.getFileContent(configId, path); 109 byte[] content = fileService.getFileContent(configId, path);