Commit a4e230828f34a2dc84b0210068f361b8f856dcdb
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
README.md
| ... | ... | @@ -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 | 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 | 315 | | [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 4.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) | |
| 316 | 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 | 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 | 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 | ... | ... |
pom.xml
| ... | ... | @@ -32,17 +32,17 @@ |
| 32 | 32 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 33 | 33 | |
| 34 | 34 | <properties> |
| 35 | - <revision>2025.08-SNAPSHOT</revision> | |
| 35 | + <revision>2025.09-SNAPSHOT</revision> | |
| 36 | 36 | <!-- Maven 相关 --> |
| 37 | 37 | <java.version>17</java.version> |
| 38 | 38 | <maven.compiler.source>${java.version}</maven.compiler.source> |
| 39 | 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 | 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 | 43 | <!-- maven-surefire-plugin 暂时无法通过 bom 的依赖读取(兼容老版本 IDEA 2024 及以前版本) --> |
| 44 | 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 | 46 | <mapstruct.version>1.6.3</mapstruct.version> |
| 47 | 47 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| 48 | 48 | </properties> | ... | ... |
sql/mysql/ruoyi-vue-pro.sql
| ... | ... | @@ -11,7 +11,7 @@ |
| 11 | 11 | Target Server Version : 80200 (8.2.0) |
| 12 | 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 | 17 | SET NAMES utf8mb4; |
| ... | ... | @@ -49,7 +49,7 @@ CREATE TABLE `infra_api_access_log` ( |
| 49 | 49 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 50 | 50 | PRIMARY KEY (`id`) USING BTREE, |
| 51 | 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 | 55 | -- Records of infra_api_access_log |
| ... | ... | @@ -91,7 +91,7 @@ CREATE TABLE `infra_api_error_log` ( |
| 91 | 91 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 92 | 92 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 93 | 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 | 97 | -- Records of infra_api_error_log |
| ... | ... | @@ -128,7 +128,7 @@ CREATE TABLE `infra_codegen_column` ( |
| 128 | 128 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 129 | 129 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 130 | 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 | 134 | -- Records of infra_codegen_column |
| ... | ... | @@ -166,7 +166,7 @@ CREATE TABLE `infra_codegen_table` ( |
| 166 | 166 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 167 | 167 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 168 | 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 | 172 | -- Records of infra_codegen_table |
| ... | ... | @@ -251,7 +251,7 @@ CREATE TABLE `infra_file` ( |
| 251 | 251 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 252 | 252 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 253 | 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 | 257 | -- Records of infra_file |
| ... | ... | @@ -276,21 +276,22 @@ CREATE TABLE `infra_file_config` ( |
| 276 | 276 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 277 | 277 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 278 | 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 | 282 | -- Records of infra_file_config |
| 283 | 283 | -- ---------------------------- |
| 284 | 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 | 295 | COMMIT; |
| 295 | 296 | |
| 296 | 297 | -- ---------------------------- |
| ... | ... | @@ -336,7 +337,7 @@ CREATE TABLE `infra_job` ( |
| 336 | 337 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 337 | 338 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 338 | 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 | 343 | -- Records of infra_job |
| ... | ... | @@ -355,6 +356,8 @@ INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param` |
| 355 | 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 | 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 | 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 | 361 | COMMIT; |
| 359 | 362 | |
| 360 | 363 | -- ---------------------------- |
| ... | ... | @@ -378,7 +381,7 @@ CREATE TABLE `infra_job_log` ( |
| 378 | 381 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 379 | 382 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 380 | 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 | 387 | -- Records of infra_job_log |
| ... | ... | @@ -448,7 +451,7 @@ CREATE TABLE `system_dict_data` ( |
| 448 | 451 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 449 | 452 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 450 | 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 | 457 | -- Records of system_dict_data |
| ... | ... | @@ -639,7 +642,7 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st |
| 639 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 1087 | COMMIT; |
| 1061 | 1088 | |
| 1062 | 1089 | -- ---------------------------- |
| ... | ... | @@ -1076,7 +1103,7 @@ CREATE TABLE `system_dict_type` ( |
| 1076 | 1103 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 1077 | 1104 | `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间', |
| 1078 | 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 | 1109 | -- Records of system_dict_type |
| ... | ... | @@ -1175,20 +1202,24 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat |
| 1175 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 1223 | COMMIT; |
| 1193 | 1224 | |
| 1194 | 1225 | -- ---------------------------- |
| ... | ... | @@ -1212,7 +1243,7 @@ CREATE TABLE `system_login_log` ( |
| 1212 | 1243 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 1213 | 1244 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 1214 | 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 | 1249 | -- Records of system_login_log |
| ... | ... | @@ -1246,7 +1277,7 @@ CREATE TABLE `system_mail_account` ( |
| 1246 | 1277 | -- ---------------------------- |
| 1247 | 1278 | BEGIN; |
| 1248 | 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 | 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 | 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 | 1283 | COMMIT; |
| ... | ... | @@ -1260,8 +1291,8 @@ CREATE TABLE `system_mail_log` ( |
| 1260 | 1291 | `user_id` bigint NULL DEFAULT NULL COMMENT '用户编号', |
| 1261 | 1292 | `user_type` tinyint NULL DEFAULT NULL COMMENT '用户类型', |
| 1262 | 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 | 1296 | `account_id` bigint NOT NULL COMMENT '邮箱账号编号', |
| 1266 | 1297 | `from_mail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '发送邮箱地址', |
| 1267 | 1298 | `template_id` bigint NOT NULL COMMENT '模板编号', |
| ... | ... | @@ -1280,7 +1311,7 @@ CREATE TABLE `system_mail_log` ( |
| 1280 | 1311 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 1281 | 1312 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 1282 | 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 | 1317 | -- Records of system_mail_log |
| ... | ... | @@ -1316,8 +1347,8 @@ CREATE TABLE `system_mail_template` ( |
| 1316 | 1347 | -- ---------------------------- |
| 1317 | 1348 | BEGIN; |
| 1318 | 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 | 1352 | COMMIT; |
| 1322 | 1353 | |
| 1323 | 1354 | -- ---------------------------- |
| ... | ... | @@ -1345,7 +1376,7 @@ CREATE TABLE `system_menu` ( |
| 1345 | 1376 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 1346 | 1377 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 1347 | 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 | 1382 | -- Records of system_menu |
| ... | ... | @@ -2203,55 +2234,47 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i |
| 2203 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 2316 | COMMIT; |
| 2266 | 2317 | |
| 2267 | 2318 | -- ---------------------------- |
| ... | ... | @@ -2287,7 +2338,7 @@ CREATE TABLE `system_notice` ( |
| 2287 | 2338 | -- Records of system_notice |
| 2288 | 2339 | -- ---------------------------- |
| 2289 | 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 | 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 | 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 | 2344 | COMMIT; |
| ... | ... | @@ -2383,7 +2434,7 @@ CREATE TABLE `system_oauth2_access_token` ( |
| 2383 | 2434 | PRIMARY KEY (`id`) USING BTREE, |
| 2384 | 2435 | INDEX `idx_access_token`(`access_token` ASC) USING BTREE, |
| 2385 | 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 | 2440 | -- Records of system_oauth2_access_token |
| ... | ... | @@ -2451,7 +2502,7 @@ CREATE TABLE `system_oauth2_client` ( |
| 2451 | 2502 | -- Records of system_oauth2_client |
| 2452 | 2503 | -- ---------------------------- |
| 2453 | 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 | 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 | 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 | 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 | 2556 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 2506 | 2557 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 2507 | 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 | 2562 | -- Records of system_oauth2_refresh_token |
| ... | ... | @@ -2539,7 +2590,7 @@ CREATE TABLE `system_operate_log` ( |
| 2539 | 2590 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 2540 | 2591 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 2541 | 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 | 2596 | -- Records of system_operate_log |
| ... | ... | @@ -2607,11 +2658,9 @@ BEGIN; |
| 2607 | 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 | 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 | 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 | 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 | 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 | 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 | 2664 | COMMIT; |
| 2616 | 2665 | |
| 2617 | 2666 | -- ---------------------------- |
| ... | ... | @@ -2670,87 +2719,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t |
| 2670 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 3347 | COMMIT; |
| 3502 | 3348 | |
| 3503 | 3349 | -- ---------------------------- |
| ... | ... | @@ -3552,7 +3398,7 @@ CREATE TABLE `system_sms_code` ( |
| 3552 | 3398 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 3553 | 3399 | PRIMARY KEY (`id`) USING BTREE, |
| 3554 | 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 | 3404 | -- Records of system_sms_code |
| ... | ... | @@ -3593,7 +3439,7 @@ CREATE TABLE `system_sms_log` ( |
| 3593 | 3439 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 3594 | 3440 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 3595 | 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 | 3445 | -- Records of system_sms_log |
| ... | ... | @@ -3726,7 +3572,7 @@ CREATE TABLE `system_social_user_bind` ( |
| 3726 | 3572 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 3727 | 3573 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 3728 | 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 | 3578 | -- Records of system_social_user_bind |
| ... | ... | @@ -3744,8 +3590,8 @@ CREATE TABLE `system_tenant` ( |
| 3744 | 3590 | `contact_user_id` bigint NULL DEFAULT NULL COMMENT '联系人的用户编号', |
| 3745 | 3591 | `contact_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '联系人', |
| 3746 | 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 | 3595 | `package_id` bigint NOT NULL COMMENT '租户套餐编号', |
| 3750 | 3596 | `expire_time` datetime NOT NULL COMMENT '过期时间', |
| 3751 | 3597 | `account_count` int NOT NULL COMMENT '账号数量', |
| ... | ... | @@ -3761,9 +3607,9 @@ CREATE TABLE `system_tenant` ( |
| 3761 | 3607 | -- Records of system_tenant |
| 3762 | 3608 | -- ---------------------------- |
| 3763 | 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 | 3613 | COMMIT; |
| 3768 | 3614 | |
| 3769 | 3615 | -- ---------------------------- |
| ... | ... | @@ -3788,8 +3634,7 @@ CREATE TABLE `system_tenant_package` ( |
| 3788 | 3634 | -- Records of system_tenant_package |
| 3789 | 3635 | -- ---------------------------- |
| 3790 | 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 | 3638 | COMMIT; |
| 3794 | 3639 | |
| 3795 | 3640 | -- ---------------------------- |
| ... | ... | @@ -3839,7 +3684,7 @@ CREATE TABLE `system_user_role` ( |
| 3839 | 3684 | `deleted` bit(1) NULL DEFAULT b'0' COMMENT '是否删除', |
| 3840 | 3685 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 3841 | 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 | 3690 | -- Records of system_user_role |
| ... | ... | @@ -3847,7 +3692,6 @@ CREATE TABLE `system_user_role` ( |
| 3847 | 3692 | BEGIN; |
| 3848 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 3709 | COMMIT; |
| 3866 | 3710 | |
| 3867 | 3711 | -- ---------------------------- |
| ... | ... | @@ -3890,15 +3734,15 @@ CREATE TABLE `system_users` ( |
| 3890 | 3734 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 3891 | 3735 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 3892 | 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 | 3740 | -- Records of system_users |
| 3897 | 3741 | -- ---------------------------- |
| 3898 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 3761 | COMMIT; |
| 3918 | 3762 | |
| 3919 | 3763 | -- ---------------------------- | ... | ... |
yudao-dependencies/pom.xml
| ... | ... | @@ -14,57 +14,58 @@ |
| 14 | 14 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 15 | 15 | |
| 16 | 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 | 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 | 24 | <!-- DB 相关 --> |
| 25 | - <druid.version>1.2.24</druid.version> | |
| 25 | + <druid.version>1.2.27</druid.version> | |
| 26 | 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 | 28 | <mybatis-plus-join.version>1.5.4</mybatis-plus-join.version> |
| 29 | 29 | <dynamic-datasource.version>4.3.1</dynamic-datasource.version> |
| 30 | 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 | 32 | <dm8.jdbc.version>8.1.3.140</dm8.jdbc.version> |
| 33 | 33 | <kingbase.jdbc.version>8.6.0</kingbase.jdbc.version> |
| 34 | 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 | 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 | 43 | <opentracing.version>0.33.0</opentracing.version> |
| 44 | 44 | <!-- Test 测试相关 --> |
| 45 | 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 | 47 | <mockito-inline.version>5.2.0</mockito-inline.version> |
| 48 | 48 | <!-- Bpm 工作流相关 --> |
| 49 | 49 | <flowable.version>7.0.1</flowable.version> |
| 50 | 50 | <!-- 工具类相关 --> |
| 51 | 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 | 53 | <lombok.version>1.18.38</lombok.version> |
| 54 | 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 | 58 | <velocity.version>2.4.1</velocity.version> |
| 59 | 59 | <fastjson.version>1.2.83</fastjson.version> |
| 60 | 60 | <guava.version>33.4.8-jre</guava.version> |
| 61 | 61 | <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version> |
| 62 | 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 | 66 | <ip2region.version>2.7.0</ip2region.version> |
| 66 | 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 | 69 | <mqtt.version>1.2.5</mqtt.version> |
| 69 | 70 | <pf4j-spring.version>0.9.0</pf4j-spring.version> |
| 70 | 71 | <vertx.version>4.5.13</vertx.version> |
| ... | ... | @@ -72,9 +73,9 @@ |
| 72 | 73 | <awssdk.version>2.30.14</awssdk.version> |
| 73 | 74 | <justauth.version>1.16.7</justauth.version> |
| 74 | 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 | 79 | </properties> |
| 79 | 80 | |
| 80 | 81 | <dependencyManagement> |
| ... | ... | @@ -151,13 +152,19 @@ |
| 151 | 152 | </dependency> |
| 152 | 153 | |
| 153 | 154 | <dependency> |
| 154 | - <groupId>com.github.xingfudeshi</groupId> <!-- TODO 芋艿:https://github.com/xiaoymin/knife4j/issues/874 --> | |
| 155 | + <groupId>com.github.xiaoymin</groupId> | |
| 155 | 156 | <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> |
| 156 | 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 | 164 | </dependency> |
| 158 | 165 | <dependency> |
| 159 | 166 | <groupId>org.springdoc</groupId> |
| 160 | - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> | |
| 167 | + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> | |
| 161 | 168 | <version>${springdoc.version}</version> |
| 162 | 169 | </dependency> |
| 163 | 170 | |
| ... | ... | @@ -518,14 +525,19 @@ |
| 518 | 525 | <artifactId>commons-net</artifactId> <!-- 解决 ftp 连接 --> |
| 519 | 526 | <version>${commons-net.version}</version> |
| 520 | 527 | </dependency> |
| 521 | - | |
| 522 | 528 | <dependency> |
| 523 | - <groupId>com.jcraft</groupId> | |
| 529 | + <groupId>com.github.mwiede</groupId> | |
| 524 | 530 | <artifactId>jsch</artifactId> <!-- 解决 sftp 连接 --> |
| 525 | 531 | <version>${jsch.version}</version> |
| 526 | 532 | </dependency> |
| 527 | 533 | |
| 528 | 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 | 541 | <groupId>com.anji-plus</groupId> |
| 530 | 542 | <artifactId>captcha-spring-boot-starter</artifactId> <!-- 验证码,一般用于登录使用 --> |
| 531 | 543 | <version>${anji-plus-captcha.version}</version> |
| ... | ... | @@ -599,6 +611,10 @@ |
| 599 | 611 | <groupId>com.github.jsqlparser</groupId> |
| 600 | 612 | <artifactId>jsqlparser</artifactId> |
| 601 | 613 | </exclusion> |
| 614 | + <exclusion> | |
| 615 | + <groupId>cn.hutool</groupId> | |
| 616 | + <artifactId>hutool-core</artifactId> | |
| 617 | + </exclusion> | |
| 602 | 618 | </exclusions> |
| 603 | 619 | </dependency> |
| 604 | 620 | ... | ... |
yudao-framework/yudao-common/pom.xml
| ... | ... | @@ -60,7 +60,7 @@ |
| 60 | 60 | |
| 61 | 61 | <dependency> |
| 62 | 62 | <groupId>org.springdoc</groupId> |
| 63 | - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> | |
| 63 | + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> | |
| 64 | 64 | <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 --> |
| 65 | 65 | </dependency> |
| 66 | 66 | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenRespDTO.java
| 1 | 1 | package cn.iocoder.yudao.framework.common.biz.system.oauth2.dto; |
| 2 | 2 | |
| 3 | 3 | import lombok.Data; |
| 4 | -import lombok.experimental.Accessors; | |
| 5 | 4 | |
| 6 | 5 | import java.io.Serializable; |
| 7 | 6 | import java.time.LocalDateTime; |
| ... | ... | @@ -12,7 +11,6 @@ import java.time.LocalDateTime; |
| 12 | 11 | * @author 芋道源码 |
| 13 | 12 | */ |
| 14 | 13 | @Data |
| 15 | -@Accessors(chain = true) | |
| 16 | 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
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 | 8 | import cn.hutool.core.util.StrUtil; |
| 9 | 9 | import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; |
| 10 | 10 | |
| 11 | +import java.sql.Timestamp; | |
| 11 | 12 | import java.time.*; |
| 12 | 13 | import java.time.format.DateTimeFormatter; |
| 13 | 14 | import java.time.format.DateTimeParseException; |
| ... | ... | @@ -16,8 +17,7 @@ import java.time.temporal.TemporalAdjusters; |
| 16 | 17 | import java.util.ArrayList; |
| 17 | 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 | 23 | * 时间工具类,用于 {@link LocalDateTime} |
| ... | ... | @@ -90,6 +90,21 @@ public class LocalDateTimeUtils { |
| 90 | 90 | * @param time 指定时间 |
| 91 | 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 | 108 | public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) { |
| 94 | 109 | if (startTime == null || endTime == null || time == null) { |
| 95 | 110 | return false; |
| ... | ... | @@ -234,6 +249,11 @@ public class LocalDateTimeUtils { |
| 234 | 249 | // 2. 循环,生成时间范围 |
| 235 | 250 | List<LocalDateTime[]> timeRanges = new ArrayList<>(); |
| 236 | 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 | 257 | case DAY: |
| 238 | 258 | while (startTime.isBefore(endTime)) { |
| 239 | 259 | timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)}); |
| ... | ... | @@ -297,6 +317,8 @@ public class LocalDateTimeUtils { |
| 297 | 317 | |
| 298 | 318 | // 2. 循环,生成时间范围 |
| 299 | 319 | switch (intervalEnum) { |
| 320 | + case HOUR: | |
| 321 | + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATETIME_MINUTE_PATTERN); | |
| 300 | 322 | case DAY: |
| 301 | 323 | return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN); |
| 302 | 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 | 3 | import cn.hutool.core.util.ArrayUtil; |
| 4 | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | 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 | 8 | import com.fasterxml.jackson.annotation.JsonInclude; |
| 7 | 9 | import com.fasterxml.jackson.core.type.TypeReference; |
| 8 | 10 | import com.fasterxml.jackson.databind.DeserializationFeature; |
| 9 | 11 | import com.fasterxml.jackson.databind.JsonNode; |
| 10 | 12 | import com.fasterxml.jackson.databind.ObjectMapper; |
| 11 | 13 | import com.fasterxml.jackson.databind.SerializationFeature; |
| 14 | +import com.fasterxml.jackson.databind.module.SimpleModule; | |
| 12 | 15 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; |
| 16 | +import lombok.Getter; | |
| 13 | 17 | import lombok.SneakyThrows; |
| 14 | 18 | import lombok.extern.slf4j.Slf4j; |
| 15 | 19 | |
| 16 | 20 | import java.io.IOException; |
| 17 | 21 | import java.lang.reflect.Type; |
| 22 | +import java.time.LocalDateTime; | |
| 18 | 23 | import java.util.ArrayList; |
| 19 | 24 | import java.util.List; |
| 20 | 25 | |
| ... | ... | @@ -26,13 +31,18 @@ import java.util.List; |
| 26 | 31 | @Slf4j |
| 27 | 32 | public class JsonUtils { |
| 28 | 33 | |
| 34 | + @Getter | |
| 29 | 35 | private static ObjectMapper objectMapper = new ObjectMapper(); |
| 30 | 36 | |
| 31 | 37 | static { |
| 32 | 38 | objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); |
| 33 | 39 | objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
| 34 | 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 | 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 | 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
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 | 44 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; |
| 45 | 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 | 49 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 51 | 50 | |
| ... | ... | @@ -84,41 +83,13 @@ public class YudaoTenantAutoConfiguration { |
| 84 | 83 | // ========== WEB ========== |
| 85 | 84 | |
| 86 | 85 | @Bean |
| 87 | - public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter(TenantProperties tenantProperties) { | |
| 86 | + public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter() { | |
| 88 | 87 | FilterRegistrationBean<TenantContextWebFilter> registrationBean = new FilterRegistrationBean<>(); |
| 89 | 88 | registrationBean.setFilter(new TenantContextWebFilter()); |
| 90 | 89 | registrationBean.setOrder(WebFilterOrderEnum.TENANT_CONTEXT_FILTER); |
| 91 | - addIgnoreUrls(tenantProperties); | |
| 92 | 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 | 93 | @Bean |
| 123 | 94 | public TenantVisitContextInterceptor tenantVisitContextInterceptor(TenantProperties tenantProperties, |
| 124 | 95 | SecurityFrameworkService securityFrameworkService) { |
| ... | ... | @@ -146,12 +117,42 @@ public class YudaoTenantAutoConfiguration { |
| 146 | 117 | GlobalExceptionHandler globalExceptionHandler, |
| 147 | 118 | TenantFrameworkService tenantFrameworkService) { |
| 148 | 119 | FilterRegistrationBean<TenantSecurityWebFilter> registrationBean = new FilterRegistrationBean<>(); |
| 149 | - registrationBean.setFilter(new TenantSecurityWebFilter(tenantProperties, webProperties, | |
| 120 | + registrationBean.setFilter(new TenantSecurityWebFilter(webProperties, tenantProperties, getTenantIgnoreUrls(), | |
| 150 | 121 | globalExceptionHandler, tenantFrameworkService)); |
| 151 | 122 | registrationBean.setOrder(WebFilterOrderEnum.TENANT_SECURITY_FILTER); |
| 152 | 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 | 156 | // ========== MQ ========== |
| 156 | 157 | |
| 157 | 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 | 75 | if (TenantBaseDO.class.isAssignableFrom(tableInfo.getEntityType())) { |
| 76 | 76 | return false; |
| 77 | 77 | } |
| 78 | - // 如果添加了 @TenantIgnore 注解,显然也不忽略租户 | |
| 78 | + // 如果添加了 @TenantIgnore 注解,则忽略租户 | |
| 79 | 79 | TenantIgnore tenantIgnore = tableInfo.getEntityType().getAnnotation(TenantIgnore.class); |
| 80 | 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 | 12 | public class TenantRabbitMQInitializer implements BeanPostProcessor { |
| 13 | 13 | |
| 14 | 14 | @Override |
| 15 | + @SuppressWarnings("PatternVariableCanBeUsed") | |
| 15 | 16 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { |
| 16 | 17 | if (bean instanceof RabbitTemplate) { |
| 17 | 18 | RabbitTemplate rabbitTemplate = (RabbitTemplate) bean; |
| ... | ... | @@ -20,4 +21,4 @@ public class TenantRabbitMQInitializer implements BeanPostProcessor { |
| 20 | 21 | return bean; |
| 21 | 22 | } |
| 22 | 23 | |
| 23 | -} | |
| 24 | 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 | 17 | public class TenantRocketMQInitializer implements BeanPostProcessor { |
| 18 | 18 | |
| 19 | 19 | @Override |
| 20 | + @SuppressWarnings("PatternVariableCanBeUsed") | |
| 20 | 21 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { |
| 21 | 22 | if (bean instanceof DefaultRocketMQListenerContainer) { |
| 22 | 23 | DefaultRocketMQListenerContainer container = (DefaultRocketMQListenerContainer) bean; |
| ... | ... | @@ -50,4 +51,4 @@ public class TenantRocketMQInitializer implements BeanPostProcessor { |
| 50 | 51 | consumerImpl.registerConsumeMessageHook(new TenantRocketMQConsumeMessageHook()); |
| 51 | 52 | } |
| 52 | 53 | |
| 53 | -} | |
| 54 | 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 | 12 | import cn.iocoder.yudao.framework.web.config.WebProperties; |
| 13 | 13 | import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter; |
| 14 | 14 | import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; |
| 15 | -import lombok.extern.slf4j.Slf4j; | |
| 16 | -import org.springframework.util.AntPathMatcher; | |
| 17 | - | |
| 18 | 15 | import jakarta.servlet.FilterChain; |
| 19 | 16 | import jakarta.servlet.ServletException; |
| 20 | 17 | import jakarta.servlet.http.HttpServletRequest; |
| 21 | 18 | import jakarta.servlet.http.HttpServletResponse; |
| 19 | +import lombok.extern.slf4j.Slf4j; | |
| 20 | +import org.springframework.util.AntPathMatcher; | |
| 21 | + | |
| 22 | 22 | import java.io.IOException; |
| 23 | 23 | import java.util.Objects; |
| 24 | +import java.util.Set; | |
| 24 | 25 | |
| 25 | 26 | /** |
| 26 | 27 | * 多租户 Security Web 过滤器 |
| ... | ... | @@ -35,17 +36,26 @@ public class TenantSecurityWebFilter extends ApiRequestFilter { |
| 35 | 36 | |
| 36 | 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 | 46 | private final AntPathMatcher pathMatcher; |
| 39 | 47 | |
| 40 | 48 | private final GlobalExceptionHandler globalExceptionHandler; |
| 41 | 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 | 54 | GlobalExceptionHandler globalExceptionHandler, |
| 46 | 55 | TenantFrameworkService tenantFrameworkService) { |
| 47 | 56 | super(webProperties); |
| 48 | 57 | this.tenantProperties = tenantProperties; |
| 58 | + this.ignoreUrls = ignoreUrls; | |
| 49 | 59 | this.pathMatcher = new AntPathMatcher(); |
| 50 | 60 | this.globalExceptionHandler = globalExceptionHandler; |
| 51 | 61 | this.tenantFrameworkService = tenantFrameworkService; |
| ... | ... | @@ -101,13 +111,20 @@ public class TenantSecurityWebFilter extends ApiRequestFilter { |
| 101 | 111 | } |
| 102 | 112 | |
| 103 | 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 | 118 | return true; |
| 107 | 119 | } |
| 108 | 120 | // 逐个 Ant 路径匹配 |
| 109 | 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 | 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 | 21 | return new BeanPostProcessor() { |
| 22 | 22 | |
| 23 | 23 | @Override |
| 24 | + @SuppressWarnings("PatternVariableCanBeUsed") | |
| 24 | 25 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { |
| 25 | 26 | // 处理 ThreadPoolTaskExecutor |
| 26 | 27 | if (bean instanceof ThreadPoolTaskExecutor) { | ... | ... |
yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoTracerAutoConfiguration.java
| 1 | 1 | package cn.iocoder.yudao.framework.tracer.config; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; |
| 4 | -import cn.iocoder.yudao.framework.tracer.core.aop.BizTraceAspect; | |
| 5 | 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 | 5 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 10 | 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
| 11 | 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| ... | ... | @@ -20,32 +16,28 @@ import org.springframework.context.annotation.Bean; |
| 20 | 16 | */ |
| 21 | 17 | @AutoConfiguration |
| 22 | 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 | 21 | "jakarta.servlet.Filter" |
| 26 | 22 | }) |
| 27 | 23 | @EnableConfigurationProperties(TracerProperties.class) |
| 28 | 24 | @ConditionalOnProperty(prefix = "yudao.tracer", value = "enable", matchIfMissing = true) |
| 29 | 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 | 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 | 69 | @ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 |
| 70 | 70 | public RedisPendingMessageResendJob redisPendingMessageResendJob(List<AbstractRedisStreamMessageListener<?>> listeners, |
| 71 | 71 | RedisMQTemplate redisTemplate, |
| 72 | - @Value("${spring.application.name}") String groupName, | |
| 73 | 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 | 140 | * |
| 142 | 141 | * @return 消费者名字 |
| 143 | 142 | */ |
| 144 | - private static String buildConsumerName() { | |
| 143 | + public static String buildConsumerName() { | |
| 145 | 144 | return String.format("%s@%d", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID()); |
| 146 | 145 | } |
| 147 | 146 | |
| 148 | 147 | /** |
| 149 | 148 | * 校验 Redis 版本号,是否满足最低的版本号要求! |
| 150 | 149 | */ |
| 151 | - private static void checkRedisVersion(RedisTemplate<String, ?> redisTemplate) { | |
| 150 | + public static void checkRedisVersion(RedisTemplate<String, ?> redisTemplate) { | |
| 152 | 151 | // 获得 Redis 版本 |
| 153 | 152 | Properties info = redisTemplate.execute((RedisCallback<Properties>) RedisServerCommands::info); |
| 154 | 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 | 35 | |
| 36 | 36 | private final List<AbstractRedisStreamMessageListener<?>> listeners; |
| 37 | 37 | private final RedisMQTemplate redisTemplate; |
| 38 | - private final String groupName; | |
| 39 | 38 | private final RedissonClient redissonClient; |
| 40 | 39 | |
| 41 | 40 | /** |
| ... | ... | @@ -64,13 +63,13 @@ public class RedisPendingMessageResendJob { |
| 64 | 63 | private void execute() { |
| 65 | 64 | StreamOperations<String, Object, Object> ops = redisTemplate.getRedisTemplate().opsForStream(); |
| 66 | 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 | 67 | // 每个消费者的 pending 队列消息数量 |
| 69 | 68 | Map<String, Long> pendingMessagesPerConsumer = pendingMessagesSummary.getPendingMessagesPerConsumer(); |
| 70 | 69 | pendingMessagesPerConsumer.forEach((consumerName, pendingMessageCount) -> { |
| 71 | 70 | log.info("[processPendingMessage][消费者({}) 消息数量({})]", consumerName, pendingMessageCount); |
| 72 | 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 | 73 | if (pendingMessages.isEmpty()) { |
| 75 | 74 | return; |
| 76 | 75 | } |
| ... | ... | @@ -91,7 +90,7 @@ public class RedisPendingMessageResendJob { |
| 91 | 90 | .ofObject(records.get(0).getValue()) // 设置内容 |
| 92 | 91 | .withStreamKey(listener.getStreamKey())); |
| 93 | 92 | // ack 消息消费完成 |
| 94 | - redisTemplate.getRedisTemplate().opsForStream().acknowledge(groupName, records.get(0)); | |
| 93 | + redisTemplate.getRedisTemplate().opsForStream().acknowledge(listener.getGroup(), records.get(0)); | |
| 95 | 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<T extends AbstractRedis |
| 53 | 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 | 62 | @Override |
| 57 | 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 | 1 | package cn.iocoder.yudao.framework.mybatis.config; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 3 | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | |
| 4 | 6 | import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler; |
| 5 | 7 | import com.baomidou.mybatisplus.annotation.DbType; |
| 6 | 8 | import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; |
| 9 | +import com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler; | |
| 7 | 10 | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; |
| 8 | 11 | import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator; |
| 12 | +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; | |
| 9 | 13 | import com.baomidou.mybatisplus.extension.incrementer.*; |
| 10 | 14 | import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal; |
| 11 | 15 | import com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlParseCache; |
| 12 | 16 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
| 13 | 17 | import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; |
| 18 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
| 14 | 19 | import org.apache.ibatis.annotations.Mapper; |
| 15 | 20 | import org.mybatis.spring.annotation.MapperScan; |
| 16 | 21 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| ... | ... | @@ -18,6 +23,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 18 | 23 | import org.springframework.context.annotation.Bean; |
| 19 | 24 | import org.springframework.core.env.ConfigurableEnvironment; |
| 20 | 25 | |
| 26 | +import java.util.List; | |
| 21 | 27 | import java.util.concurrent.TimeUnit; |
| 22 | 28 | |
| 23 | 29 | /** |
| ... | ... | @@ -42,6 +48,8 @@ public class YudaoMybatisAutoConfiguration { |
| 42 | 48 | public MybatisPlusInterceptor mybatisPlusInterceptor() { |
| 43 | 49 | MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); |
| 44 | 50 | mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); // 分页插件 |
| 51 | + // ↓↓↓ 按需开启,可能会影响到 updateBatch 的地方:例如说文件配置管理 ↓↓↓ | |
| 52 | + // mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); // 拦截没有指定条件的 update 和 delete 语句 | |
| 45 | 53 | return mybatisPlusInterceptor; |
| 46 | 54 | } |
| 47 | 55 | |
| ... | ... | @@ -73,4 +81,15 @@ public class YudaoMybatisAutoConfiguration { |
| 73 | 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 | 18 | public class DefaultDBFieldHandler implements MetaObjectHandler { |
| 19 | 19 | |
| 20 | 20 | @Override |
| 21 | + @SuppressWarnings("PatternVariableCanBeUsed") | |
| 21 | 22 | public void insertFill(MetaObject metaObject) { |
| 22 | 23 | if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) { |
| 23 | 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 | 39 | |
| 40 | 40 | @Before("@annotation(rateLimiter)") |
| 41 | 41 | public void beforePointCut(JoinPoint joinPoint, RateLimiter rateLimiter) { |
| 42 | - // 获得 IdempotentKeyResolver 对象 | |
| 42 | + // 获得 RateLimiterKeyResolver 对象 | |
| 43 | 43 | RateLimiterKeyResolver keyResolver = keyResolvers.get(rateLimiter.keyResolver()); |
| 44 | 44 | Assert.notNull(keyResolver, "找不到对应的 RateLimiterKeyResolver"); |
| 45 | 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 | 165 | // 获得有 @PermitAll 注解的接口 |
| 166 | 166 | for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) { |
| 167 | 167 | HandlerMethod handlerMethod = entry.getValue(); |
| 168 | - if (!handlerMethod.hasMethodAnnotation(PermitAll.class)) { | |
| 168 | + if (!handlerMethod.hasMethodAnnotation(PermitAll.class) // 方法级 | |
| 169 | + && !handlerMethod.getBeanType().isAnnotationPresent(PermitAll.class)) { // 接口级 | |
| 169 | 170 | continue; |
| 170 | 171 | } |
| 171 | 172 | Set<String> urls = new HashSet<>(); | ... | ... |
yudao-framework/yudao-spring-boot-starter-web/pom.xml
| ... | ... | @@ -39,12 +39,12 @@ |
| 39 | 39 | </dependency> |
| 40 | 40 | |
| 41 | 41 | <dependency> |
| 42 | - <groupId>com.github.xingfudeshi</groupId> | |
| 42 | + <groupId>com.github.xiaoymin</groupId> | |
| 43 | 43 | <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> |
| 44 | 44 | </dependency> |
| 45 | 45 | <dependency> |
| 46 | 46 | <groupId>org.springdoc</groupId> |
| 47 | - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> | |
| 47 | + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> | |
| 48 | 48 | </dependency> |
| 49 | 49 | |
| 50 | 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 | 23 | import org.springframework.web.method.HandlerMethod; |
| 24 | 24 | import org.springframework.web.servlet.HandlerExecutionChain; |
| 25 | 25 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; |
| 26 | +import org.springframework.web.util.ServletRequestPathUtils; | |
| 26 | 27 | |
| 27 | 28 | import java.io.IOException; |
| 28 | 29 | |
| ... | ... | @@ -131,6 +132,12 @@ public class ApiEncryptFilter extends ApiRequestFilter { |
| 131 | 132 | @SuppressWarnings("PatternVariableCanBeUsed") |
| 132 | 133 | private ApiEncrypt getApiEncrypt(HttpServletRequest request) { |
| 133 | 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 | 141 | HandlerExecutionChain mappingHandler = requestMappingHandlerMapping.getHandler(request); |
| 135 | 142 | if (mappingHandler == null) { |
| 136 | 143 | return null; | ... | ... |
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java
| 1 | 1 | package cn.iocoder.yudao.framework.jackson.config; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.collection.CollUtil; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 5 | 4 | import cn.iocoder.yudao.framework.common.util.json.databind.NumberSerializer; |
| 6 | 5 | import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer; |
| 7 | 6 | import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer; |
| 7 | +import com.fasterxml.jackson.databind.Module; | |
| 8 | 8 | import com.fasterxml.jackson.databind.ObjectMapper; |
| 9 | 9 | import com.fasterxml.jackson.databind.module.SimpleModule; |
| 10 | 10 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; |
| ... | ... | @@ -13,39 +13,65 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; |
| 13 | 13 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; |
| 14 | 14 | import lombok.extern.slf4j.Slf4j; |
| 15 | 15 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 16 | +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; | |
| 17 | +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; | |
| 16 | 18 | import org.springframework.context.annotation.Bean; |
| 17 | 19 | |
| 18 | 20 | import java.time.LocalDate; |
| 19 | 21 | import java.time.LocalDateTime; |
| 20 | 22 | import java.time.LocalTime; |
| 21 | -import java.util.List; | |
| 22 | 23 | |
| 23 | -@AutoConfiguration | |
| 24 | +@AutoConfiguration(after = JacksonAutoConfiguration.class) | |
| 24 | 25 | @Slf4j |
| 25 | 26 | public class YudaoJacksonAutoConfiguration { |
| 26 | 27 | |
| 28 | + /** | |
| 29 | + * 从 Builder 源头定制(关键:使用 *ByType,避免 handledType 要求) | |
| 30 | + */ | |
| 27 | 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 | 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 | 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 | 1 | package cn.iocoder.yudao.framework.swagger.config; |
| 2 | 2 | |
| 3 | +import com.github.xiaoymin.knife4j.spring.configuration.Knife4jAutoConfiguration; | |
| 3 | 4 | import io.swagger.v3.oas.models.Components; |
| 4 | 5 | import io.swagger.v3.oas.models.OpenAPI; |
| 5 | 6 | import io.swagger.v3.oas.models.info.Contact; |
| ... | ... | @@ -11,6 +12,7 @@ import io.swagger.v3.oas.models.parameters.Parameter; |
| 11 | 12 | import io.swagger.v3.oas.models.security.SecurityRequirement; |
| 12 | 13 | import io.swagger.v3.oas.models.security.SecurityScheme; |
| 13 | 14 | import org.springdoc.core.customizers.OpenApiBuilderCustomizer; |
| 15 | +import org.springdoc.core.customizers.OperationCustomizer; | |
| 14 | 16 | import org.springdoc.core.customizers.ServerBaseUrlCustomizer; |
| 15 | 17 | import org.springdoc.core.models.GroupedOpenApi; |
| 16 | 18 | import org.springdoc.core.properties.SpringDocConfigProperties; |
| ... | ... | @@ -23,6 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
| 23 | 25 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 24 | 26 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 25 | 27 | import org.springframework.context.annotation.Bean; |
| 28 | +import org.springframework.context.annotation.Import; | |
| 26 | 29 | import org.springframework.context.annotation.Primary; |
| 27 | 30 | import org.springframework.http.HttpHeaders; |
| 28 | 31 | |
| ... | ... | @@ -42,10 +45,11 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_ |
| 42 | 45 | * |
| 43 | 46 | * @author 芋道源码 |
| 44 | 47 | */ |
| 45 | -@AutoConfiguration | |
| 48 | +@AutoConfiguration(before = Knife4jAutoConfiguration.class) // before 原因,保证覆写的 Knife4jOpenApiCustomizer 先生效!相关 https://github.com/YunaiV/ruoyi-vue-pro/issues/954 讨论 | |
| 46 | 49 | @ConditionalOnClass({OpenAPI.class}) |
| 47 | 50 | @EnableConfigurationProperties(SwaggerProperties.class) |
| 48 | 51 | @ConditionalOnProperty(prefix = "springdoc.api-docs", name = "enabled", havingValue = "true", matchIfMissing = true) // 设置为 false 时,禁用 |
| 52 | +@Import(Knife4jOpenApiCustomizer.class) | |
| 49 | 53 | public class YudaoSwaggerAutoConfiguration { |
| 50 | 54 | |
| 51 | 55 | // ========== 全局 OpenAPI 配置 ========== |
| ... | ... | @@ -125,6 +129,7 @@ public class YudaoSwaggerAutoConfiguration { |
| 125 | 129 | .addOperationCustomizer((operation, handlerMethod) -> operation |
| 126 | 130 | .addParametersItem(buildTenantHeaderParameter()) |
| 127 | 131 | .addParametersItem(buildSecurityHeaderParameter())) |
| 132 | + .addOperationCustomizer(buildOperationIdCustomizer()) | |
| 128 | 133 | .build(); |
| 129 | 134 | } |
| 130 | 135 | |
| ... | ... | @@ -156,5 +161,26 @@ public class YudaoSwaggerAutoConfiguration { |
| 156 | 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 | 1 | package cn.iocoder.yudao.framework.web.config; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.util.StrUtil; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.biz.infra.logger.ApiErrorLogCommonApi; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; |
| 5 | 6 | import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyFilter; |
| ... | ... | @@ -7,6 +8,7 @@ import cn.iocoder.yudao.framework.web.core.filter.DemoFilter; |
| 7 | 8 | import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; |
| 8 | 9 | import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler; |
| 9 | 10 | import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; |
| 11 | +import com.google.common.collect.Maps; | |
| 10 | 12 | import jakarta.annotation.Resource; |
| 11 | 13 | import jakarta.servlet.Filter; |
| 12 | 14 | import org.springframework.beans.factory.annotation.Value; |
| ... | ... | @@ -14,6 +16,7 @@ import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 14 | 16 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
| 15 | 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 16 | 18 | import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration; |
| 19 | +import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations; | |
| 17 | 20 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 18 | 21 | import org.springframework.boot.web.client.RestTemplateBuilder; |
| 19 | 22 | import org.springframework.boot.web.servlet.FilterRegistrationBean; |
| ... | ... | @@ -26,35 +29,57 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; |
| 26 | 29 | import org.springframework.web.filter.CorsFilter; |
| 27 | 30 | import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; |
| 28 | 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 | 37 | @AutoConfiguration |
| 31 | 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 | 44 | @Value("${spring.application.name}") |
| 40 | 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 | 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 | 182 | * 例如说,接口上设置了 @RequestBody 实体中 xx 属性类型为 Integer,结果传递 xx 参数类型为 String |
| 183 | 183 | */ |
| 184 | 184 | @ExceptionHandler(HttpMessageNotReadableException.class) |
| 185 | + @SuppressWarnings("PatternVariableCanBeUsed") | |
| 185 | 186 | public CommonResult<?> methodArgumentTypeInvalidFormatExceptionHandler(HttpMessageNotReadableException ex) { |
| 186 | 187 | log.warn("[methodArgumentTypeInvalidFormatExceptionHandler]", ex); |
| 187 | 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 | 144 | return (CommonResult<?>) request.getAttribute(REQUEST_ATTRIBUTE_COMMON_RESULT); |
| 145 | 145 | } |
| 146 | 146 | |
| 147 | + @SuppressWarnings("PatternVariableCanBeUsed") | |
| 147 | 148 | public static HttpServletRequest getRequest() { |
| 148 | 149 | RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); |
| 149 | 150 | if (!(requestAttributes instanceof ServletRequestAttributes)) { | ... | ... |
yudao-module-ai/pom.xml
| ... | ... | @@ -19,8 +19,8 @@ |
| 19 | 19 | 国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno |
| 20 | 20 | </description> |
| 21 | 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 | 24 | <tinyflow.version>1.0.2</tinyflow.version> |
| 25 | 25 | </properties> |
| 26 | 26 | |
| ... | ... | @@ -78,6 +78,12 @@ |
| 78 | 78 | <groupId>org.springframework.ai</groupId> |
| 79 | 79 | <artifactId>spring-ai-starter-model-openai</artifactId> |
| 80 | 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 | 87 | </dependency> |
| 82 | 88 | <dependency> |
| 83 | 89 | <groupId>org.springframework.ai</groupId> |
| ... | ... | @@ -86,6 +92,11 @@ |
| 86 | 92 | </dependency> |
| 87 | 93 | <dependency> |
| 88 | 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 | 100 | <artifactId>spring-ai-starter-model-deepseek</artifactId> |
| 90 | 101 | <version>${spring-ai.version}</version> |
| 91 | 102 | </dependency> |
| ... | ... | @@ -182,6 +193,24 @@ |
| 182 | 193 | </exclusions> |
| 183 | 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 | 214 | <!-- TinyFlow:AI 工作流 --> |
| 186 | 215 | <dependency> |
| 187 | 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 | 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 | 61 | Authorization: {{token}} |
| 62 | +tenant-id: {{adminTenantId}} | |
| 26 | 63 | |
| 27 | 64 | ### 删除消息 |
| 28 | 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 | 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 | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 5 | import lombok.Data; |
| 5 | 6 | |
| ... | ... | @@ -37,6 +38,9 @@ public class AiChatMessageRespVO { |
| 37 | 38 | @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊") |
| 38 | 39 | private String content; |
| 39 | 40 | |
| 41 | + @Schema(description = "推理内容", example = "要达到这个目标,你需要...") | |
| 42 | + private String reasoningContent; | |
| 43 | + | |
| 40 | 44 | @Schema(description = "是否携带上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") |
| 41 | 45 | private Boolean useContext; |
| 42 | 46 | |
| ... | ... | @@ -46,6 +50,12 @@ public class AiChatMessageRespVO { |
| 46 | 50 | @Schema(description = "知识库段落数组") |
| 47 | 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 | 59 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51") |
| 50 | 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 | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 4 | import jakarta.validation.constraints.NotEmpty; |
| 5 | 5 | import jakarta.validation.constraints.NotNull; |
| 6 | -import jakarta.validation.constraints.Size; | |
| 7 | 6 | import lombok.Data; |
| 8 | -import lombok.experimental.Accessors; | |
| 7 | + | |
| 8 | +import java.util.List; | |
| 9 | 9 | |
| 10 | 10 | @Schema(description = "管理后台 - AI 聊天消息发送 Request VO") |
| 11 | 11 | @Data |
| ... | ... | @@ -22,4 +22,10 @@ public class AiChatMessageSendReqVO { |
| 22 | 22 | @Schema(description = "是否携带上下文", example = "true") |
| 23 | 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 | 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 | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 5 | import lombok.Data; |
| 5 | 6 | |
| ... | ... | @@ -29,12 +30,18 @@ public class AiChatMessageSendRespVO { |
| 29 | 30 | @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊") |
| 30 | 31 | private String content; |
| 31 | 32 | |
| 33 | + @Schema(description = "推理内容", example = "要达到这个目标,你需要...") | |
| 34 | + private String reasoningContent; | |
| 35 | + | |
| 32 | 36 | @Schema(description = "知识库段落编号数组", example = "[1,2,3]") |
| 33 | 37 | private List<Long> segmentIds; |
| 34 | 38 | |
| 35 | 39 | @Schema(description = "知识库段落数组") |
| 36 | 40 | private List<AiChatMessageRespVO.KnowledgeSegment> segments; |
| 37 | 41 | |
| 42 | + @Schema(description = "联网搜索的网页内容数组") | |
| 43 | + private List<AiWebSearchResponse.WebPage> webSearchPages; | |
| 44 | + | |
| 38 | 45 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 39 | 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 | 52 | @Schema(description = "引用的工具编号列表", example = "1,2,3") |
| 53 | 53 | private List<Long> toolIds; |
| 54 | 54 | |
| 55 | + @Schema(description = "引用的 MCP Client 名字列表", example = "filesystem") | |
| 56 | + private List<String> mcpClientNames; | |
| 57 | + | |
| 55 | 58 | @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 56 | 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 | 37 | @Schema(description = "引用的工具编号列表", example = "1,2,3") |
| 38 | 38 | private List<Long> toolIds; |
| 39 | 39 | |
| 40 | + @Schema(description = "引用的 MCP Client 名字列表", example = "filesystem") | |
| 41 | + private List<String> mcpClientNames; | |
| 42 | + | |
| 40 | 43 | } |
| 41 | 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 | 50 | @Schema(description = "引用的工具编号列表", example = "1,2,3") |
| 51 | 51 | private List<Long> toolIds; |
| 52 | 52 | |
| 53 | + @Schema(description = "引用的 MCP Client 名字列表", example = "filesystem") | |
| 54 | + private List<String> mcpClientNames; | |
| 55 | + | |
| 53 | 56 | @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 54 | 57 | @NotNull(message = "是否公开不能为空") |
| 55 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 4 | 4 | import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; |
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler; | |
| 5 | 6 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; |
| 6 | 7 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; |
| 7 | 8 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; |
| 9 | +import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse; | |
| 8 | 10 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 9 | 11 | import com.baomidou.mybatisplus.annotation.TableField; |
| 10 | 12 | import com.baomidou.mybatisplus.annotation.TableId; |
| 11 | 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 | 19 | import org.springframework.ai.chat.messages.MessageType; |
| 14 | 20 | |
| 15 | 21 | import java.util.List; |
| ... | ... | @@ -87,6 +93,10 @@ public class AiChatMessageDO extends BaseDO { |
| 87 | 93 | * 聊天内容 |
| 88 | 94 | */ |
| 89 | 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 | 111 | @TableField(typeHandler = LongListTypeHandler.class) |
| 102 | 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 | 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | 5 | import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; |
| 6 | +import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler; | |
| 6 | 7 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; |
| 7 | 8 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 8 | 9 | import com.baomidou.mybatisplus.annotation.TableField; |
| ... | ... | @@ -80,6 +81,13 @@ public class AiChatRoleDO extends BaseDO { |
| 80 | 81 | */ |
| 81 | 82 | @TableField(typeHandler = LongListTypeHandler.class) |
| 82 | 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 | 1 | package cn.iocoder.yudao.module.ai.dal.dataobject.model; |
| 2 | 2 | |
| 3 | 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 | 6 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 7 | 7 | import com.baomidou.mybatisplus.annotation.TableId; |
| 8 | 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<String> { |
| 33 | 33 | |
| 34 | 34 | OPENAI("OpenAI", "OpenAI"), // OpenAI 官方 |
| 35 | 35 | AZURE_OPENAI("AzureOpenAI", "AzureOpenAI"), // OpenAI 微软 |
| 36 | + ANTHROPIC("Anthropic", "Anthropic"), // Anthropic Claude | |
| 37 | + GEMINI("Gemini", "Gemini"), // 谷歌 Gemini | |
| 36 | 38 | OLLAMA("Ollama", "Ollama"), |
| 37 | 39 | |
| 38 | 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 | 2 | |
| 3 | 3 | import cn.hutool.core.util.StrUtil; |
| 4 | 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 | 7 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; |
| 8 | 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 | 10 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel; |
| 10 | 11 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; |
| 11 | 12 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants; |
| 12 | 13 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel; |
| 13 | 14 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi; |
| 14 | 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 | 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 | 23 | import org.springframework.ai.embedding.BatchingStrategy; |
| 17 | 24 | import org.springframework.ai.embedding.TokenCountBatchingStrategy; |
| 18 | 25 | import org.springframework.ai.model.tool.ToolCallingManager; |
| 19 | 26 | import org.springframework.ai.openai.OpenAiChatModel; |
| 20 | 27 | import org.springframework.ai.openai.OpenAiChatOptions; |
| 21 | 28 | import org.springframework.ai.openai.api.OpenAiApi; |
| 29 | +import org.springframework.ai.support.ToolCallbacks; | |
| 22 | 30 | import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; |
| 23 | 31 | import org.springframework.ai.tokenizer.TokenCountEstimator; |
| 32 | +import org.springframework.ai.tool.ToolCallback; | |
| 24 | 33 | import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientProperties; |
| 25 | 34 | import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreProperties; |
| 26 | 35 | import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreProperties; |
| ... | ... | @@ -30,6 +39,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties |
| 30 | 39 | import org.springframework.context.annotation.Bean; |
| 31 | 40 | import org.springframework.context.annotation.Configuration; |
| 32 | 41 | |
| 42 | +import java.util.List; | |
| 43 | + | |
| 33 | 44 | /** |
| 34 | 45 | * 芋道 AI 自动配置 |
| 35 | 46 | * |
| ... | ... | @@ -52,19 +63,48 @@ public class AiAutoConfiguration { |
| 52 | 63 | // ========== 各种 AI Client 创建 ========== |
| 53 | 64 | |
| 54 | 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 | 94 | @ConditionalOnProperty(value = "yudao.ai.doubao.enable", havingValue = "true") |
| 56 | 95 | public DouBaoChatModel douBaoChatClient(YudaoAiProperties yudaoAiProperties) { |
| 57 | - YudaoAiProperties.DouBaoProperties properties = yudaoAiProperties.getDoubao(); | |
| 96 | + YudaoAiProperties.DouBao properties = yudaoAiProperties.getDoubao(); | |
| 58 | 97 | return buildDouBaoChatClient(properties); |
| 59 | 98 | } |
| 60 | 99 | |
| 61 | - public DouBaoChatModel buildDouBaoChatClient(YudaoAiProperties.DouBaoProperties properties) { | |
| 100 | + public DouBaoChatModel buildDouBaoChatClient(YudaoAiProperties.DouBao properties) { | |
| 62 | 101 | if (StrUtil.isEmpty(properties.getModel())) { |
| 63 | 102 | properties.setModel(DouBaoChatModel.MODEL_DEFAULT); |
| 64 | 103 | } |
| 65 | 104 | OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() |
| 66 | 105 | .openAiApi(OpenAiApi.builder() |
| 67 | 106 | .baseUrl(DouBaoChatModel.BASE_URL) |
| 107 | + .completionsPath(DouBaoChatModel.COMPLETE_PATH) | |
| 68 | 108 | .apiKey(properties.getApiKey()) |
| 69 | 109 | .build()) |
| 70 | 110 | .defaultOptions(OpenAiChatOptions.builder() |
| ... | ... | @@ -81,20 +121,20 @@ public class AiAutoConfiguration { |
| 81 | 121 | @Bean |
| 82 | 122 | @ConditionalOnProperty(value = "yudao.ai.siliconflow.enable", havingValue = "true") |
| 83 | 123 | public SiliconFlowChatModel siliconFlowChatClient(YudaoAiProperties yudaoAiProperties) { |
| 84 | - YudaoAiProperties.SiliconFlowProperties properties = yudaoAiProperties.getSiliconflow(); | |
| 124 | + YudaoAiProperties.SiliconFlow properties = yudaoAiProperties.getSiliconflow(); | |
| 85 | 125 | return buildSiliconFlowChatClient(properties); |
| 86 | 126 | } |
| 87 | 127 | |
| 88 | - public SiliconFlowChatModel buildSiliconFlowChatClient(YudaoAiProperties.SiliconFlowProperties properties) { | |
| 128 | + public SiliconFlowChatModel buildSiliconFlowChatClient(YudaoAiProperties.SiliconFlow properties) { | |
| 89 | 129 | if (StrUtil.isEmpty(properties.getModel())) { |
| 90 | 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 | 134 | .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL) |
| 95 | 135 | .apiKey(properties.getApiKey()) |
| 96 | 136 | .build()) |
| 97 | - .defaultOptions(OpenAiChatOptions.builder() | |
| 137 | + .defaultOptions(DeepSeekChatOptions.builder() | |
| 98 | 138 | .model(properties.getModel()) |
| 99 | 139 | .temperature(properties.getTemperature()) |
| 100 | 140 | .maxTokens(properties.getMaxTokens()) |
| ... | ... | @@ -108,11 +148,11 @@ public class AiAutoConfiguration { |
| 108 | 148 | @Bean |
| 109 | 149 | @ConditionalOnProperty(value = "yudao.ai.hunyuan.enable", havingValue = "true") |
| 110 | 150 | public HunYuanChatModel hunYuanChatClient(YudaoAiProperties yudaoAiProperties) { |
| 111 | - YudaoAiProperties.HunYuanProperties properties = yudaoAiProperties.getHunyuan(); | |
| 151 | + YudaoAiProperties.HunYuan properties = yudaoAiProperties.getHunyuan(); | |
| 112 | 152 | return buildHunYuanChatClient(properties); |
| 113 | 153 | } |
| 114 | 154 | |
| 115 | - public HunYuanChatModel buildHunYuanChatClient(YudaoAiProperties.HunYuanProperties properties) { | |
| 155 | + public HunYuanChatModel buildHunYuanChatClient(YudaoAiProperties.HunYuan properties) { | |
| 116 | 156 | if (StrUtil.isEmpty(properties.getModel())) { |
| 117 | 157 | properties.setModel(HunYuanChatModel.MODEL_DEFAULT); |
| 118 | 158 | } |
| ... | ... | @@ -122,13 +162,14 @@ public class AiAutoConfiguration { |
| 122 | 162 | StrUtil.startWithIgnoreCase(properties.getModel(), "deepseek") ? HunYuanChatModel.DEEP_SEEK_BASE_URL |
| 123 | 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 | 168 | .baseUrl(properties.getBaseUrl()) |
| 169 | + .completionsPath(HunYuanChatModel.COMPLETE_PATH) | |
| 129 | 170 | .apiKey(properties.getApiKey()) |
| 130 | 171 | .build()) |
| 131 | - .defaultOptions(OpenAiChatOptions.builder() | |
| 172 | + .defaultOptions(DeepSeekChatOptions.builder() | |
| 132 | 173 | .model(properties.getModel()) |
| 133 | 174 | .temperature(properties.getTemperature()) |
| 134 | 175 | .maxTokens(properties.getMaxTokens()) |
| ... | ... | @@ -142,25 +183,30 @@ public class AiAutoConfiguration { |
| 142 | 183 | @Bean |
| 143 | 184 | @ConditionalOnProperty(value = "yudao.ai.xinghuo.enable", havingValue = "true") |
| 144 | 185 | public XingHuoChatModel xingHuoChatClient(YudaoAiProperties yudaoAiProperties) { |
| 145 | - YudaoAiProperties.XingHuoProperties properties = yudaoAiProperties.getXinghuo(); | |
| 186 | + YudaoAiProperties.XingHuo properties = yudaoAiProperties.getXinghuo(); | |
| 146 | 187 | return buildXingHuoChatClient(properties); |
| 147 | 188 | } |
| 148 | 189 | |
| 149 | - public XingHuoChatModel buildXingHuoChatClient(YudaoAiProperties.XingHuoProperties properties) { | |
| 190 | + public XingHuoChatModel buildXingHuoChatClient(YudaoAiProperties.XingHuo properties) { | |
| 150 | 191 | if (StrUtil.isEmpty(properties.getModel())) { |
| 151 | 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 | 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 | 203 | .defaultOptions(OpenAiChatOptions.builder() |
| 159 | 204 | .model(properties.getModel()) |
| 160 | 205 | .temperature(properties.getTemperature()) |
| 161 | 206 | .maxTokens(properties.getMaxTokens()) |
| 162 | 207 | .topP(properties.getTopP()) |
| 163 | 208 | .build()) |
| 209 | + // TODO @芋艿:星火的 function call 有 bug,会报 ToolResponseMessage must have an id 错误!!! | |
| 164 | 210 | .toolCallingManager(getToolCallingManager()) |
| 165 | 211 | .build(); |
| 166 | 212 | return new XingHuoChatModel(openAiChatModel); |
| ... | ... | @@ -169,11 +215,11 @@ public class AiAutoConfiguration { |
| 169 | 215 | @Bean |
| 170 | 216 | @ConditionalOnProperty(value = "yudao.ai.baichuan.enable", havingValue = "true") |
| 171 | 217 | public BaiChuanChatModel baiChuanChatClient(YudaoAiProperties yudaoAiProperties) { |
| 172 | - YudaoAiProperties.BaiChuanProperties properties = yudaoAiProperties.getBaichuan(); | |
| 218 | + YudaoAiProperties.BaiChuan properties = yudaoAiProperties.getBaichuan(); | |
| 173 | 219 | return buildBaiChuanChatClient(properties); |
| 174 | 220 | } |
| 175 | 221 | |
| 176 | - public BaiChuanChatModel buildBaiChuanChatClient(YudaoAiProperties.BaiChuanProperties properties) { | |
| 222 | + public BaiChuanChatModel buildBaiChuanChatClient(YudaoAiProperties.BaiChuan properties) { | |
| 177 | 223 | if (StrUtil.isEmpty(properties.getModel())) { |
| 178 | 224 | properties.setModel(BaiChuanChatModel.MODEL_DEFAULT); |
| 179 | 225 | } |
| ... | ... | @@ -196,7 +242,7 @@ public class AiAutoConfiguration { |
| 196 | 242 | @Bean |
| 197 | 243 | @ConditionalOnProperty(value = "yudao.ai.midjourney.enable", havingValue = "true") |
| 198 | 244 | public MidjourneyApi midjourneyApi(YudaoAiProperties yudaoAiProperties) { |
| 199 | - YudaoAiProperties.MidjourneyProperties config = yudaoAiProperties.getMidjourney(); | |
| 245 | + YudaoAiProperties.Midjourney config = yudaoAiProperties.getMidjourney(); | |
| 200 | 246 | return new MidjourneyApi(config.getBaseUrl(), config.getApiKey(), config.getNotifyUrl()); |
| 201 | 247 | } |
| 202 | 248 | |
| ... | ... | @@ -222,4 +268,22 @@ public class AiAutoConfiguration { |
| 222 | 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 | 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 | 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 | 47 | * Midjourney 绘图 |
| 48 | 48 | */ |
| 49 | - private MidjourneyProperties midjourney; | |
| 49 | + private Midjourney midjourney; | |
| 50 | 50 | |
| 51 | 51 | /** |
| 52 | 52 | * Suno 音乐 |
| 53 | 53 | */ |
| 54 | 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 | 75 | @Data |
| 58 | - public static class DouBaoProperties { | |
| 76 | + public static class DouBao { | |
| 59 | 77 | |
| 60 | 78 | private String enable; |
| 61 | 79 | private String apiKey; |
| ... | ... | @@ -68,7 +86,7 @@ public class YudaoAiProperties { |
| 68 | 86 | } |
| 69 | 87 | |
| 70 | 88 | @Data |
| 71 | - public static class HunYuanProperties { | |
| 89 | + public static class HunYuan { | |
| 72 | 90 | |
| 73 | 91 | private String enable; |
| 74 | 92 | private String baseUrl; |
| ... | ... | @@ -82,7 +100,7 @@ public class YudaoAiProperties { |
| 82 | 100 | } |
| 83 | 101 | |
| 84 | 102 | @Data |
| 85 | - public static class SiliconFlowProperties { | |
| 103 | + public static class SiliconFlow { | |
| 86 | 104 | |
| 87 | 105 | private String enable; |
| 88 | 106 | private String apiKey; |
| ... | ... | @@ -95,7 +113,7 @@ public class YudaoAiProperties { |
| 95 | 113 | } |
| 96 | 114 | |
| 97 | 115 | @Data |
| 98 | - public static class XingHuoProperties { | |
| 116 | + public static class XingHuo { | |
| 99 | 117 | |
| 100 | 118 | private String enable; |
| 101 | 119 | private String appId; |
| ... | ... | @@ -110,7 +128,7 @@ public class YudaoAiProperties { |
| 110 | 128 | } |
| 111 | 129 | |
| 112 | 130 | @Data |
| 113 | - public static class BaiChuanProperties { | |
| 131 | + public static class BaiChuan { | |
| 114 | 132 | |
| 115 | 133 | private String enable; |
| 116 | 134 | private String apiKey; |
| ... | ... | @@ -123,7 +141,7 @@ public class YudaoAiProperties { |
| 123 | 141 | } |
| 124 | 142 | |
| 125 | 143 | @Data |
| 126 | - public static class MidjourneyProperties { | |
| 144 | + public static class Midjourney { | |
| 127 | 145 | |
| 128 | 146 | private String enable; |
| 129 | 147 | private String baseUrl; |
| ... | ... | @@ -134,12 +152,21 @@ public class YudaoAiProperties { |
| 134 | 152 | } |
| 135 | 153 | |
| 136 | 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 | 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
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 | 3 | import cn.hutool.core.io.FileUtil; |
| 4 | 4 | import cn.hutool.core.lang.Assert; |
| ... | ... | @@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.ai.framework.ai.config.AiAutoConfiguration; |
| 14 | 14 | import cn.iocoder.yudao.module.ai.framework.ai.config.YudaoAiProperties; |
| 15 | 15 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; |
| 16 | 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 | 18 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel; |
| 18 | 19 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; |
| 19 | 20 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants; |
| ... | ... | @@ -67,6 +68,7 @@ import org.springframework.ai.minimax.MiniMaxChatOptions; |
| 67 | 68 | import org.springframework.ai.minimax.MiniMaxEmbeddingModel; |
| 68 | 69 | import org.springframework.ai.minimax.MiniMaxEmbeddingOptions; |
| 69 | 70 | import org.springframework.ai.minimax.api.MiniMaxApi; |
| 71 | +import org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration; | |
| 70 | 72 | import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatAutoConfiguration; |
| 71 | 73 | import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingAutoConfiguration; |
| 72 | 74 | import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingProperties; |
| ... | ... | @@ -93,6 +95,8 @@ import org.springframework.ai.openai.OpenAiImageModel; |
| 93 | 95 | import org.springframework.ai.openai.api.OpenAiApi; |
| 94 | 96 | import org.springframework.ai.openai.api.OpenAiImageApi; |
| 95 | 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 | 100 | import org.springframework.ai.stabilityai.StabilityAiImageModel; |
| 97 | 101 | import org.springframework.ai.stabilityai.api.StabilityAiApi; |
| 98 | 102 | import org.springframework.ai.vectorstore.SimpleVectorStore; |
| ... | ... | @@ -168,6 +172,10 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 168 | 172 | return buildOpenAiChatModel(apiKey, url); |
| 169 | 173 | case AZURE_OPENAI: |
| 170 | 174 | return buildAzureOpenAiChatModel(apiKey, url); |
| 175 | + case ANTHROPIC: | |
| 176 | + return buildAnthropicChatModel(apiKey, url); | |
| 177 | + case GEMINI: | |
| 178 | + return buildGeminiChatModel(apiKey); | |
| 171 | 179 | case OLLAMA: |
| 172 | 180 | return buildOllamaChatModel(url); |
| 173 | 181 | default: |
| ... | ... | @@ -206,6 +214,10 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 206 | 214 | return SpringUtil.getBean(OpenAiChatModel.class); |
| 207 | 215 | case AZURE_OPENAI: |
| 208 | 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 | 221 | case OLLAMA: |
| 210 | 222 | return SpringUtil.getBean(OllamaChatModel.class); |
| 211 | 223 | default: |
| ... | ... | @@ -260,7 +272,7 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 260 | 272 | String cacheKey = buildClientCacheKey(MidjourneyApi.class, AiPlatformEnum.MIDJOURNEY.getPlatform(), apiKey, |
| 261 | 273 | url); |
| 262 | 274 | return Singleton.get(cacheKey, (Func0<MidjourneyApi>) () -> { |
| 263 | - YudaoAiProperties.MidjourneyProperties properties = SpringUtil.getBean(YudaoAiProperties.class) | |
| 275 | + YudaoAiProperties.Midjourney properties = SpringUtil.getBean(YudaoAiProperties.class) | |
| 264 | 276 | .getMidjourney(); |
| 265 | 277 | return new MidjourneyApi(url, apiKey, properties.getNotifyUrl()); |
| 266 | 278 | }); |
| ... | ... | @@ -347,7 +359,7 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 347 | 359 | * 可参考 {@link DashScopeImageAutoConfiguration} 的 dashScopeImageModel 方法 |
| 348 | 360 | */ |
| 349 | 361 | private static DashScopeImageModel buildTongYiImagesModel(String key) { |
| 350 | - DashScopeImageApi dashScopeImageApi = new DashScopeImageApi(key); | |
| 362 | + DashScopeImageApi dashScopeImageApi = DashScopeImageApi.builder().apiKey(key).build(); | |
| 351 | 363 | return DashScopeImageModel.builder() |
| 352 | 364 | .dashScopeApi(dashScopeImageApi) |
| 353 | 365 | .build(); |
| ... | ... | @@ -397,7 +409,7 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 397 | 409 | * 可参考 {@link AiAutoConfiguration#douBaoChatClient(YudaoAiProperties)} |
| 398 | 410 | */ |
| 399 | 411 | private ChatModel buildDouBaoChatModel(String apiKey) { |
| 400 | - YudaoAiProperties.DouBaoProperties properties = new YudaoAiProperties.DouBaoProperties() | |
| 412 | + YudaoAiProperties.DouBao properties = new YudaoAiProperties.DouBao() | |
| 401 | 413 | .setApiKey(apiKey); |
| 402 | 414 | return new AiAutoConfiguration().buildDouBaoChatClient(properties); |
| 403 | 415 | } |
| ... | ... | @@ -406,7 +418,7 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 406 | 418 | * 可参考 {@link AiAutoConfiguration#hunYuanChatClient(YudaoAiProperties)} |
| 407 | 419 | */ |
| 408 | 420 | private ChatModel buildHunYuanChatModel(String apiKey, String url) { |
| 409 | - YudaoAiProperties.HunYuanProperties properties = new YudaoAiProperties.HunYuanProperties() | |
| 421 | + YudaoAiProperties.HunYuan properties = new YudaoAiProperties.HunYuan() | |
| 410 | 422 | .setBaseUrl(url).setApiKey(apiKey); |
| 411 | 423 | return new AiAutoConfiguration().buildHunYuanChatClient(properties); |
| 412 | 424 | } |
| ... | ... | @@ -415,7 +427,7 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 415 | 427 | * 可参考 {@link AiAutoConfiguration#siliconFlowChatClient(YudaoAiProperties)} |
| 416 | 428 | */ |
| 417 | 429 | private ChatModel buildSiliconFlowChatModel(String apiKey) { |
| 418 | - YudaoAiProperties.SiliconFlowProperties properties = new YudaoAiProperties.SiliconFlowProperties() | |
| 430 | + YudaoAiProperties.SiliconFlow properties = new YudaoAiProperties.SiliconFlow() | |
| 419 | 431 | .setApiKey(apiKey); |
| 420 | 432 | return new AiAutoConfiguration().buildSiliconFlowChatClient(properties); |
| 421 | 433 | } |
| ... | ... | @@ -473,7 +485,7 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 473 | 485 | private static XingHuoChatModel buildXingHuoChatModel(String key) { |
| 474 | 486 | List<String> keys = StrUtil.split(key, '|'); |
| 475 | 487 | Assert.equals(keys.size(), 2, "XingHuoChatClient 的密钥需要 (appKey|secretKey) 格式"); |
| 476 | - YudaoAiProperties.XingHuoProperties properties = new YudaoAiProperties.XingHuoProperties() | |
| 488 | + YudaoAiProperties.XingHuo properties = new YudaoAiProperties.XingHuo() | |
| 477 | 489 | .setAppKey(keys.get(0)).setSecretKey(keys.get(1)); |
| 478 | 490 | return new AiAutoConfiguration().buildXingHuoChatClient(properties); |
| 479 | 491 | } |
| ... | ... | @@ -482,7 +494,7 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 482 | 494 | * 可参考 {@link AiAutoConfiguration#baiChuanChatClient(YudaoAiProperties)} |
| 483 | 495 | */ |
| 484 | 496 | private BaiChuanChatModel buildBaiChuanChatModel(String apiKey) { |
| 485 | - YudaoAiProperties.BaiChuanProperties properties = new YudaoAiProperties.BaiChuanProperties() | |
| 497 | + YudaoAiProperties.BaiChuan properties = new YudaoAiProperties.BaiChuan() | |
| 486 | 498 | .setApiKey(apiKey); |
| 487 | 499 | return new AiAutoConfiguration().buildBaiChuanChatClient(properties); |
| 488 | 500 | } |
| ... | ... | @@ -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 | 552 | * 可参考 {@link OpenAiImageAutoConfiguration} 的 openAiImageModel 方法 |
| 517 | 553 | */ |
| 518 | 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 | 6 | import org.springframework.ai.chat.model.ChatResponse; |
| 7 | 7 | import org.springframework.ai.chat.prompt.ChatOptions; |
| 8 | 8 | import org.springframework.ai.chat.prompt.Prompt; |
| 9 | -import org.springframework.ai.openai.OpenAiChatModel; | |
| 10 | 9 | import reactor.core.publisher.Flux; |
| 11 | 10 | |
| 12 | 11 | /** |
| ... | ... | @@ -19,13 +18,14 @@ import reactor.core.publisher.Flux; |
| 19 | 18 | public class DouBaoChatModel implements ChatModel { |
| 20 | 19 | |
| 21 | 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 | 23 | public static final String MODEL_DEFAULT = "doubao-1-5-lite-32k-250115"; |
| 24 | 24 | |
| 25 | 25 | /** |
| 26 | 26 | * 兼容 OpenAI 接口,进行复用 |
| 27 | 27 | */ |
| 28 | - private final OpenAiChatModel openAiChatModel; | |
| 28 | + private final ChatModel openAiChatModel; | |
| 29 | 29 | |
| 30 | 30 | @Override |
| 31 | 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 | 6 | import org.springframework.ai.chat.model.ChatResponse; |
| 7 | 7 | import org.springframework.ai.chat.prompt.ChatOptions; |
| 8 | 8 | import org.springframework.ai.chat.prompt.Prompt; |
| 9 | -import org.springframework.ai.openai.OpenAiChatModel; | |
| 10 | 9 | import reactor.core.publisher.Flux; |
| 11 | 10 | |
| 12 | 11 | /** |
| ... | ... | @@ -22,6 +21,7 @@ import reactor.core.publisher.Flux; |
| 22 | 21 | public class HunYuanChatModel implements ChatModel { |
| 23 | 22 | |
| 24 | 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 | 26 | public static final String MODEL_DEFAULT = "hunyuan-turbo"; |
| 27 | 27 | |
| ... | ... | @@ -32,7 +32,7 @@ public class HunYuanChatModel implements ChatModel { |
| 32 | 32 | /** |
| 33 | 33 | * 兼容 OpenAI 接口,进行复用 |
| 34 | 34 | */ |
| 35 | - private final OpenAiChatModel openAiChatModel; | |
| 35 | + private final ChatModel openAiChatModel; | |
| 36 | 36 | |
| 37 | 37 | @Override |
| 38 | 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 | 23 | /** |
| 24 | 24 | * 兼容 OpenAI 接口,进行复用 |
| 25 | 25 | */ |
| 26 | - private final OpenAiChatModel openAiChatModel; | |
| 26 | + private final ChatModel openAiChatModel; | |
| 27 | 27 | |
| 28 | 28 | @Override |
| 29 | 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 | 6 | import org.springframework.ai.chat.model.ChatResponse; |
| 7 | 7 | import org.springframework.ai.chat.prompt.ChatOptions; |
| 8 | 8 | import org.springframework.ai.chat.prompt.Prompt; |
| 9 | -import org.springframework.ai.openai.OpenAiChatModel; | |
| 10 | 9 | import reactor.core.publisher.Flux; |
| 11 | 10 | |
| 12 | 11 | /** |
| ... | ... | @@ -18,28 +17,34 @@ import reactor.core.publisher.Flux; |
| 18 | 17 | @RequiredArgsConstructor |
| 19 | 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 | 35 | @Override |
| 31 | 36 | public ChatResponse call(Prompt prompt) { |
| 32 | - return openAiChatModel.call(prompt); | |
| 37 | + return openAiChatModelV1.call(prompt); | |
| 33 | 38 | } |
| 34 | 39 | |
| 35 | 40 | @Override |
| 36 | 41 | public Flux<ChatResponse> stream(Prompt prompt) { |
| 37 | - return openAiChatModel.stream(prompt); | |
| 42 | + return openAiChatModelV1.stream(prompt); | |
| 38 | 43 | } |
| 39 | 44 | |
| 40 | 45 | @Override |
| 41 | 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 | 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 | 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 | 6 | import org.springframework.context.annotation.Bean; |
| 6 | 7 | import org.springframework.context.annotation.Configuration; |
| 7 | 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
| 8 | 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 | 17 | public class SecurityConfiguration { |
| 15 | 18 | |
| 16 | - @Bean("iotAuthorizeRequestsCustomizer") | |
| 19 | + @Resource | |
| 20 | + private Optional<McpServerProperties> serverProperties; | |
| 21 | + | |
| 22 | + @Bean("aiAuthorizeRequestsCustomizer") | |
| 17 | 23 | public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { |
| 18 | 24 | return new AuthorizeRequestsCustomizer() { |
| 19 | 25 | |
| 20 | 26 | @Override |
| 21 | 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
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.ai.service.chat; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.codec.Base64; | |
| 3 | 4 | import cn.hutool.core.collection.CollUtil; |
| 5 | +import cn.hutool.core.io.file.FileNameUtil; | |
| 4 | 6 | import cn.hutool.core.util.ObjUtil; |
| 5 | 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 | 9 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 9 | 10 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 10 | 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 | 22 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; |
| 22 | 23 | import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper; |
| 23 | 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 | 29 | import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; |
| 25 | 30 | import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; |
| 26 | 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 | 33 | import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; |
| 29 | 34 | import cn.iocoder.yudao.module.ai.service.model.AiModelService; |
| 30 | 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 | 40 | import jakarta.annotation.Resource; |
| 32 | 41 | import lombok.extern.slf4j.Slf4j; |
| 33 | 42 | import org.springframework.ai.chat.messages.Message; |
| ... | ... | @@ -39,6 +48,11 @@ import org.springframework.ai.chat.model.ChatResponse; |
| 39 | 48 | import org.springframework.ai.chat.model.StreamingChatModel; |
| 40 | 49 | import org.springframework.ai.chat.prompt.ChatOptions; |
| 41 | 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 | 56 | import org.springframework.stereotype.Service; |
| 43 | 57 | import org.springframework.transaction.annotation.Transactional; |
| 44 | 58 | import reactor.core.publisher.Flux; |
| ... | ... | @@ -65,12 +79,31 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_N |
| 65 | 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 | 89 | * 知识库转 {@link UserMessage} 的内容模版 |
| 69 | 90 | */ |
| 70 | 91 | private static final String KNOWLEDGE_USER_MESSAGE_TEMPLATE = "使用 <Reference></Reference> 标记中的内容作为本次对话的参考:\n\n" + |
| 71 | 92 | "%s\n\n" + // 多个 <Reference></Reference> 的拼接 |
| 72 | 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 | 107 | @Resource |
| 75 | 108 | private AiChatMessageMapper chatMessageMapper; |
| 76 | 109 | |
| ... | ... | @@ -87,6 +120,21 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { |
| 87 | 120 | @Resource |
| 88 | 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 | 138 | @Transactional(rollbackFor = Exception.class) |
| 91 | 139 | public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) { |
| 92 | 140 | // 1.1 校验对话存在 |
| ... | ... | @@ -100,27 +148,35 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { |
| 100 | 148 | AiModelDO model = modalService.validateModel(conversation.getModelId()); |
| 101 | 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 | 160 | // 3. 插入 user 发送消息 |
| 107 | 161 | AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, |
| 108 | 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 | 166 | AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model, |
| 113 | 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 | 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 | 180 | Map<Long, AiKnowledgeDocumentDO> documentMap = knowledgeDocumentService.getKnowledgeDocumentMap( |
| 125 | 181 | convertSet(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getDocumentId)); |
| 126 | 182 | List<AiChatMessageRespVO.KnowledgeSegment> segments = BeanUtils.toBean(knowledgeSegments, |
| ... | ... | @@ -131,7 +187,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { |
| 131 | 187 | return new AiChatMessageSendRespVO() |
| 132 | 188 | .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) |
| 133 | 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 | 194 | @Override |
| ... | ... | @@ -148,29 +205,36 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { |
| 148 | 205 | AiModelDO model = modalService.validateModel(conversation.getModelId()); |
| 149 | 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 | 217 | // 3. 插入 user 发送消息 |
| 156 | 218 | AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, |
| 157 | 219 | userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(), |
| 158 | - null); | |
| 220 | + null, sendReqVO.getAttachmentUrls(), null); | |
| 159 | 221 | |
| 160 | 222 | // 4.1 插入 assistant 接收消息 |
| 161 | 223 | AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model, |
| 162 | 224 | userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(), |
| 163 | - knowledgeSegments); | |
| 225 | + knowledgeSegments, null, webSearchResponse); | |
| 164 | 226 | |
| 165 | 227 | // 4.2 构建 Prompt,并进行调用 |
| 166 | - Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO); | |
| 228 | + Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, webSearchResponse, model, sendReqVO); | |
| 167 | 229 | Flux<ChatResponse> streamResponse = chatModel.stream(prompt); |
| 168 | 230 | |
| 169 | 231 | // 4.3 流式返回 |
| 170 | 232 | StringBuffer contentBuffer = new StringBuffer(); |
| 233 | + StringBuffer reasoningContentBuffer = new StringBuffer(); | |
| 171 | 234 | return streamResponse.map(chunk -> { |
| 172 | - // 处理知识库的返回,只有首次才有 | |
| 235 | + // 仅首次:返回知识库、联网搜索 | |
| 173 | 236 | List<AiChatMessageRespVO.KnowledgeSegment> segments = null; |
| 237 | + List<AiWebSearchResponse.WebPage> webSearchPages = null; | |
| 174 | 238 | if (StrUtil.isEmpty(contentBuffer)) { |
| 175 | 239 | Map<Long, AiKnowledgeDocumentDO> documentMap = TenantUtils.executeIgnore(() -> |
| 176 | 240 | knowledgeDocumentService.getKnowledgeDocumentMap( |
| ... | ... | @@ -179,24 +243,56 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { |
| 179 | 243 | AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId()); |
| 180 | 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 | 259 | return success(new AiChatMessageSendRespVO() |
| 188 | 260 | .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) |
| 189 | 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 | 265 | }).doOnComplete(() -> { |
| 192 | 266 | // 忽略租户,因为 Flux 异步无法透传租户 |
| 193 | 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 | 270 | }).doOnError(throwable -> { |
| 196 | 271 | log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable); |
| 197 | 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 | 296 | }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR))); |
| 201 | 297 | } |
| 202 | 298 | |
| ... | ... | @@ -211,7 +307,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { |
| 211 | 307 | return Collections.emptyList(); |
| 212 | 308 | } |
| 213 | 309 | |
| 214 | - // 2. 遍历找回 | |
| 310 | + // 2. 遍历召回 | |
| 215 | 311 | List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = new ArrayList<>(); |
| 216 | 312 | for (Long knowledgeId : role.getKnowledgeIds()) { |
| 217 | 313 | knowledgeSegments.addAll(knowledgeSegmentService.searchKnowledgeSegment(new AiKnowledgeSegmentSearchReqBO() |
| ... | ... | @@ -222,6 +318,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { |
| 222 | 318 | |
| 223 | 319 | private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages, |
| 224 | 320 | List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments, |
| 321 | + AiWebSearchResponse webSearchResponse, | |
| 225 | 322 | AiModelDO model, AiChatMessageSendReqVO sendReqVO) { |
| 226 | 323 | List<Message> chatMessages = new ArrayList<>(); |
| 227 | 324 | // 1.1 System Context 角色设定 |
| ... | ... | @@ -231,8 +328,14 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { |
| 231 | 328 | |
| 232 | 329 | // 1.2 历史 history message 历史消息 |
| 233 | 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 | 340 | // 1.3 当前 user message 新发送消息 |
| 238 | 341 | chatMessages.add(new UserMessage(sendReqVO.getContent())); |
| ... | ... | @@ -245,23 +348,76 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { |
| 245 | 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 | 376 | // 2.2 构建 ChatOptions 对象 |
| 259 | 377 | AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); |
| 260 | 378 | ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), |
| 261 | - conversation.getTemperature(), conversation.getMaxTokens(), toolNames, toolContext); | |
| 379 | + conversation.getTemperature(), conversation.getMaxTokens(), | |
| 380 | + toolCallbacks, toolContext); | |
| 262 | 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 | 422 | * 从历史消息中,获得倒序的 n 组消息作为消息上下文 |
| 267 | 423 | * <p> |
| ... | ... | @@ -302,14 +458,56 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { |
| 302 | 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 | 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 | 503 | AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId) |
| 310 | 504 | .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId) |
| 311 | 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 | 511 | message.setCreateTime(LocalDateTime.now()); |
| 314 | 512 | chatMessageMapper.insert(message); |
| 315 | 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 | 18 | import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO; |
| 19 | 19 | import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO; |
| 20 | 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 | 25 | import jakarta.annotation.Resource; |
| 22 | 26 | import lombok.extern.slf4j.Slf4j; |
| 23 | 27 | import org.springframework.ai.document.Document; |
| ... | ... | @@ -27,6 +31,7 @@ import org.springframework.ai.transformer.splitter.TokenTextSplitter; |
| 27 | 31 | import org.springframework.ai.vectorstore.SearchRequest; |
| 28 | 32 | import org.springframework.ai.vectorstore.VectorStore; |
| 29 | 33 | import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder; |
| 34 | +import org.springframework.beans.factory.annotation.Autowired; | |
| 30 | 35 | import org.springframework.context.annotation.Lazy; |
| 31 | 36 | import org.springframework.stereotype.Service; |
| 32 | 37 | |
| ... | ... | @@ -36,6 +41,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU |
| 36 | 41 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 37 | 42 | import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG; |
| 38 | 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 | 47 | * AI 知识库分片 Service 实现类 |
| ... | ... | @@ -55,6 +61,11 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService |
| 55 | 61 | VECTOR_STORE_METADATA_DOCUMENT_ID, String.class, |
| 56 | 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 | 69 | @Resource |
| 59 | 70 | private AiKnowledgeSegmentMapper segmentMapper; |
| 60 | 71 | |
| ... | ... | @@ -69,6 +80,9 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService |
| 69 | 80 | @Resource |
| 70 | 81 | private TokenCountEstimator tokenCountEstimator; |
| 71 | 82 | |
| 83 | + @Autowired(required = false) // 由于 spring.ai.model.rerank 配置项,可以关闭 RerankModel 的功能,所以这里只能不强制注入 | |
| 84 | + private RerankModel rerankModel; | |
| 85 | + | |
| 72 | 86 | @Override |
| 73 | 87 | public PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) { |
| 74 | 88 | return segmentMapper.selectPage(pageReqVO); |
| ... | ... | @@ -211,28 +225,16 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService |
| 211 | 225 | // 1. 校验 |
| 212 | 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 | 232 | List<AiKnowledgeSegmentDO> segments = segmentMapper |
| 230 | 233 | .selectListByVectorIds(convertList(documents, Document::getId)); |
| 231 | 234 | if (CollUtil.isEmpty(segments)) { |
| 232 | 235 | return ListUtil.empty(); |
| 233 | 236 | } |
| 234 | - | |
| 235 | - // 3. 增加召回次数 | |
| 237 | + // 3.2 增加召回次数 | |
| 236 | 238 | segmentMapper.updateRetrievalCountIncrByIds(convertList(segments, AiKnowledgeSegmentDO::getId)); |
| 237 | 239 | |
| 238 | 240 | // 4. 构建结果 |
| ... | ... | @@ -249,6 +251,42 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService |
| 249 | 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 | 290 | @Override |
| 253 | 291 | public List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens) { |
| 254 | 292 | // 1. 读取 URL 内容 | ... | ... |
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.ai.service.model; |
| 2 | 2 | |
| 3 | 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 | 5 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; |
| 6 | 6 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi; |
| 7 | 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 | 1 | package cn.iocoder.yudao.module.ai.service.model; |
| 2 | 2 | |
| 3 | -import cn.hutool.extra.spring.SpringUtil; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 4 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 6 | 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 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; |
| 9 | 8 | import cn.iocoder.yudao.module.ai.dal.mysql.model.AiToolMapper; |
| 10 | 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 | 12 | import org.springframework.stereotype.Service; |
| 13 | 13 | import org.springframework.validation.annotation.Validated; |
| 14 | 14 | |
| ... | ... | @@ -31,6 +31,9 @@ public class AiToolServiceImpl implements AiToolService { |
| 31 | 31 | @Resource |
| 32 | 32 | private AiToolMapper toolMapper; |
| 33 | 33 | |
| 34 | + @Resource | |
| 35 | + private ToolCallbackResolver toolCallbackResolver; | |
| 36 | + | |
| 34 | 37 | @Override |
| 35 | 38 | public Long createTool(AiToolSaveReqVO createReqVO) { |
| 36 | 39 | // 校验名称是否存在 |
| ... | ... | @@ -70,9 +73,8 @@ public class AiToolServiceImpl implements AiToolService { |
| 70 | 73 | } |
| 71 | 74 | |
| 72 | 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 | 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
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
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
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/package-info.java
0 → 100644
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 | 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 | 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 | 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
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 | 8 | import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; |
| 9 | 9 | import org.springaicommunity.moonshot.MoonshotChatOptions; |
| 10 | 10 | import org.springaicommunity.qianfan.QianFanChatOptions; |
| 11 | +import org.springframework.ai.anthropic.AnthropicChatOptions; | |
| 11 | 12 | import org.springframework.ai.azure.openai.AzureOpenAiChatOptions; |
| 12 | 13 | import org.springframework.ai.chat.messages.*; |
| 14 | +import org.springframework.ai.chat.model.ChatResponse; | |
| 13 | 15 | import org.springframework.ai.chat.prompt.ChatOptions; |
| 16 | +import org.springframework.ai.deepseek.DeepSeekAssistantMessage; | |
| 17 | +import org.springframework.ai.deepseek.DeepSeekChatOptions; | |
| 14 | 18 | import org.springframework.ai.minimax.MiniMaxChatOptions; |
| 15 | 19 | import org.springframework.ai.ollama.api.OllamaOptions; |
| 16 | 20 | import org.springframework.ai.openai.OpenAiChatOptions; |
| 21 | +import org.springframework.ai.tool.ToolCallback; | |
| 17 | 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 | 27 | * Spring AI 工具类 |
| ... | ... | @@ -36,40 +38,47 @@ public class AiUtils { |
| 36 | 38 | } |
| 37 | 39 | |
| 38 | 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 | 43 | toolContext = ObjUtil.defaultIfNull(toolContext, Collections.emptyMap()); |
| 42 | 44 | // noinspection EnhancedSwitchMigration |
| 43 | 45 | switch (platform) { |
| 44 | 46 | case TONG_YI: |
| 45 | 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 | 50 | case YI_YAN: |
| 48 | 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 | 59 | case ZHI_PU: |
| 50 | 60 | return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) |
| 51 | - .toolNames(toolNames).toolContext(toolContext).build(); | |
| 61 | + .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); | |
| 52 | 62 | case MINI_MAX: |
| 53 | 63 | return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) |
| 54 | - .toolNames(toolNames).toolContext(toolContext).build(); | |
| 64 | + .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); | |
| 55 | 65 | case MOONSHOT: |
| 56 | 66 | return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) |
| 57 | - .toolNames(toolNames).toolContext(toolContext).build(); | |
| 67 | + .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); | |
| 58 | 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 | 70 | case BAI_CHUAN: // 复用 OpenAI 客户端 |
| 65 | 71 | return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) |
| 66 | - .toolNames(toolNames).toolContext(toolContext).build(); | |
| 72 | + .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); | |
| 67 | 73 | case AZURE_OPENAI: |
| 68 | 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 | 79 | case OLLAMA: |
| 71 | 80 | return OllamaOptions.builder().model(model).temperature(temperature).numPredict(maxTokens) |
| 72 | - .toolNames(toolNames).toolContext(toolContext).build(); | |
| 81 | + .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); | |
| 73 | 82 | default: |
| 74 | 83 | throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); |
| 75 | 84 | } |
| ... | ... | @@ -98,4 +107,27 @@ public class AiUtils { |
| 98 | 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 | 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 | 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 | 8 | import org.springframework.ai.chat.messages.UserMessage; |
| 9 | 9 | import org.springframework.ai.chat.model.ChatResponse; |
| 10 | 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 | 14 | import reactor.core.publisher.Flux; |
| 15 | 15 | |
| 16 | 16 | import java.util.ArrayList; |
| ... | ... | @@ -23,13 +23,18 @@ import java.util.List; |
| 23 | 23 | */ |
| 24 | 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 | 31 | .baseUrl(DouBaoChatModel.BASE_URL) |
| 32 | + .completionsPath(DouBaoChatModel.COMPLETE_PATH) | |
| 29 | 33 | .apiKey("5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272") // apiKey |
| 30 | 34 | .build()) |
| 31 | - .defaultOptions(OpenAiChatOptions.builder() | |
| 35 | + .defaultOptions(DeepSeekChatOptions.builder() | |
| 32 | 36 | .model("doubao-1-5-lite-32k-250115") // 模型(doubao) |
| 37 | +// .model("doubao-seed-1-6-thinking-250715") // 模型(doubao) | |
| 33 | 38 | // .model("deepseek-r1-250120") // 模型(deepseek) |
| 34 | 39 | .temperature(0.7) |
| 35 | 40 | .build()) |
| ... | ... | @@ -51,14 +56,13 @@ public class DouBaoChatModelTests { |
| 51 | 56 | System.out.println(response); |
| 52 | 57 | } |
| 53 | 58 | |
| 54 | - // TODO @芋艿:因为使用的是 v1 api,导致 deepseek-r1-250120 不返回 think 过程,后续需要优化 | |
| 55 | 59 | @Test |
| 56 | 60 | @Disabled |
| 57 | 61 | public void testStream() { |
| 58 | 62 | // 准备参数 |
| 59 | 63 | List<Message> messages = new ArrayList<>(); |
| 60 | 64 | messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); |
| 61 | - messages.add(new UserMessage("1 + 1 = ?")); | |
| 65 | + messages.add(new UserMessage("详细推理下,帮我设计一个用户中心!")); | |
| 62 | 66 | |
| 63 | 67 | // 调用 |
| 64 | 68 | Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages)); |
| ... | ... | @@ -66,4 +70,23 @@ public class DouBaoChatModelTests { |
| 66 | 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 | 8 | import org.springframework.ai.chat.messages.UserMessage; |
| 9 | 9 | import org.springframework.ai.chat.model.ChatResponse; |
| 10 | 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 | 14 | import reactor.core.publisher.Flux; |
| 15 | 15 | |
| 16 | 16 | import java.util.ArrayList; |
| ... | ... | @@ -23,12 +23,13 @@ import java.util.List; |
| 23 | 23 | */ |
| 24 | 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 | 28 | .baseUrl(HunYuanChatModel.BASE_URL) |
| 29 | - .apiKey("sk-bcd") // apiKey | |
| 29 | + .completionsPath(HunYuanChatModel.COMPLETE_PATH) | |
| 30 | + .apiKey("sk-abc") // apiKey | |
| 30 | 31 | .build()) |
| 31 | - .defaultOptions(OpenAiChatOptions.builder() | |
| 32 | + .defaultOptions(DeepSeekChatOptions.builder() | |
| 32 | 33 | .model(HunYuanChatModel.MODEL_DEFAULT) // 模型 |
| 33 | 34 | .temperature(0.7) |
| 34 | 35 | .build()) |
| ... | ... | @@ -64,12 +65,33 @@ public class HunYuanChatModelTests { |
| 64 | 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 | 90 | .baseUrl(HunYuanChatModel.DEEP_SEEK_BASE_URL) |
| 91 | + .completionsPath(HunYuanChatModel.COMPLETE_PATH) | |
| 70 | 92 | .apiKey("sk-abc") // apiKey |
| 71 | 93 | .build()) |
| 72 | - .defaultOptions(OpenAiChatOptions.builder() | |
| 94 | + .defaultOptions(DeepSeekChatOptions.builder() | |
| 73 | 95 | // .model(HunYuanChatModel.DEEP_SEEK_MODEL_DEFAULT) // 模型("deepseek-v3") |
| 74 | 96 | .model("deepseek-r1") // 模型("deepseek-r1") |
| 75 | 97 | .temperature(0.7) |
| ... | ... | @@ -94,7 +116,7 @@ public class HunYuanChatModelTests { |
| 94 | 116 | |
| 95 | 117 | @Test |
| 96 | 118 | @Disabled |
| 97 | - public void testStream_deekseek() { | |
| 119 | + public void testStream_deepseek() { | |
| 98 | 120 | // 准备参数 |
| 99 | 121 | List<Message> messages = new ArrayList<>(); |
| 100 | 122 | messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); |
| ... | ... | @@ -106,5 +128,23 @@ public class HunYuanChatModelTests { |
| 106 | 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 | 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 | 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 | 20 | * {@link OllamaChatModel} 集成测试 |
| ... | ... | @@ -9,43 +23,65 @@ import org.springframework.ai.ollama.OllamaChatModel; |
| 9 | 23 | */ |
| 10 | 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 | 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 | 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 | 1 | package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; |
| 2 | 2 | |
| 3 | +import com.azure.ai.openai.models.ReasoningEffortValue; | |
| 3 | 4 | import org.junit.jupiter.api.Disabled; |
| 4 | 5 | import org.junit.jupiter.api.Test; |
| 5 | 6 | import org.springframework.ai.chat.messages.Message; |
| ... | ... | @@ -25,10 +26,11 @@ public class OpenAIChatModelTests { |
| 25 | 26 | private final OpenAiChatModel chatModel = OpenAiChatModel.builder() |
| 26 | 27 | .openAiApi(OpenAiApi.builder() |
| 27 | 28 | .baseUrl("https://api.holdai.top") |
| 28 | - .apiKey("sk-PytRecQlmjEteoa2RRN6cGnwslo72UUPLQVNEMS6K9yjbmpD") // apiKey | |
| 29 | + .apiKey("sk-z5joyRoV1iFEnh2SAi8QPNrIZTXyQSyxTmD5CoNDQbFixK2l") // apiKey | |
| 29 | 30 | .build()) |
| 30 | 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 | 34 | .temperature(0.7) |
| 33 | 35 | .build()) |
| 34 | 36 | .build(); |
| ... | ... | @@ -54,7 +56,7 @@ public class OpenAIChatModelTests { |
| 54 | 56 | // 准备参数 |
| 55 | 57 | List<Message> messages = new ArrayList<>(); |
| 56 | 58 | messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); |
| 57 | - messages.add(new UserMessage("1 + 1 = ?")); | |
| 59 | + messages.add(new UserMessage("帮我推理下,怎么实现一个用户中心!")); | |
| 58 | 60 | |
| 59 | 61 | // 调用 |
| 60 | 62 | Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages)); |
| ... | ... | @@ -65,4 +67,29 @@ public class OpenAIChatModelTests { |
| 65 | 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 | import org.springframework.ai.chat.messages.UserMessage; |
| 10 | 10 | import org.springframework.ai.chat.model.ChatResponse; |
| 11 | 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 | 15 | import reactor.core.publisher.Flux; |
| 16 | 16 | |
| 17 | 17 | import java.util.ArrayList; |
| ... | ... | @@ -24,12 +24,12 @@ import java.util.List; |
| 24 | 24 | */ |
| 25 | 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 | 29 | .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL) |
| 30 | 30 | .apiKey("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // apiKey |
| 31 | 31 | .build()) |
| 32 | - .defaultOptions(OpenAiChatOptions.builder() | |
| 32 | + .defaultOptions(DeepSeekChatOptions.builder() | |
| 33 | 33 | .model(SiliconFlowApiConstants.MODEL_DEFAULT) // 模型 |
| 34 | 34 | // .model("deepseek-ai/DeepSeek-R1") // 模型(deepseek-ai/DeepSeek-R1)可用赠费 |
| 35 | 35 | // .model("Pro/deepseek-ai/DeepSeek-R1") // 模型(Pro/deepseek-ai/DeepSeek-R1)需要付费 |
| ... | ... | @@ -67,4 +67,23 @@ public class SiliconFlowChatModelTests { |
| 67 | 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 | 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 | 4 | import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; |
| 4 | 5 | import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; |
| 5 | 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 | 13 | import org.junit.jupiter.api.Disabled; |
| 7 | 14 | import org.junit.jupiter.api.Test; |
| 8 | 15 | import org.springframework.ai.chat.messages.Message; |
| ... | ... | @@ -10,11 +17,14 @@ import org.springframework.ai.chat.messages.SystemMessage; |
| 10 | 17 | import org.springframework.ai.chat.messages.UserMessage; |
| 11 | 18 | import org.springframework.ai.chat.model.ChatResponse; |
| 12 | 19 | import org.springframework.ai.chat.prompt.Prompt; |
| 20 | +import org.springframework.ai.document.Document; | |
| 13 | 21 | import reactor.core.publisher.Flux; |
| 14 | 22 | |
| 15 | 23 | import java.util.ArrayList; |
| 16 | 24 | import java.util.List; |
| 17 | 25 | |
| 26 | +import static java.util.Arrays.asList; | |
| 27 | + | |
| 18 | 28 | /** |
| 19 | 29 | * {@link DashScopeChatModel} 集成测试类 |
| 20 | 30 | * |
| ... | ... | @@ -26,11 +36,13 @@ public class TongYiChatModelTests { |
| 26 | 36 | .dashScopeApi(DashScopeApi.builder() |
| 27 | 37 | .apiKey("sk-47aa124781be4bfb95244cc62f63f7d0") |
| 28 | 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 | 42 | // .withModel("deepseek-r1") // 模型(deepseek-r1) |
| 32 | 43 | // .withModel("deepseek-v3") // 模型(deepseek-v3) |
| 33 | 44 | // .withModel("deepseek-r1-distill-qwen-1.5b") // 模型(deepseek-r1-distill-qwen-1.5b) |
| 45 | +// .withEnableThinking(true) | |
| 34 | 46 | .build()) |
| 35 | 47 | .build(); |
| 36 | 48 | |
| ... | ... | @@ -54,8 +66,8 @@ public class TongYiChatModelTests { |
| 54 | 66 | public void testStream() { |
| 55 | 67 | // 准备参数 |
| 56 | 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 | 73 | Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages)); |
| ... | ... | @@ -66,4 +78,52 @@ public class TongYiChatModelTests { |
| 66 | 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 | 8 | import org.springframework.ai.chat.messages.UserMessage; |
| 9 | 9 | import org.springframework.ai.chat.model.ChatResponse; |
| 10 | 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 | 14 | import reactor.core.publisher.Flux; |
| 15 | 15 | |
| 16 | 16 | import java.util.ArrayList; |
| ... | ... | @@ -23,13 +23,15 @@ import java.util.List; |
| 23 | 23 | */ |
| 24 | 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 | 30 | .apiKey("75b161ed2aef4719b275d6e7f2a4d4cd:YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz") // appKey:secretKey |
| 30 | 31 | .build()) |
| 31 | - .defaultOptions(OpenAiChatOptions.builder() | |
| 32 | - .model("generalv3.5") // 模型 | |
| 32 | + .defaultOptions(DeepSeekChatOptions.builder() | |
| 33 | +// .model("generalv3.5") // 模型 | |
| 34 | + .model("x1") // 模型 | |
| 33 | 35 | .temperature(0.7) |
| 34 | 36 | .build()) |
| 35 | 37 | .build(); |
| ... | ... | @@ -64,4 +66,23 @@ public class XingHuoChatModelTests { |
| 64 | 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 | 23 | public class ZhiPuAiChatModelTests { |
| 24 | 24 | |
| 25 | 25 | private final ZhiPuAiChatModel chatModel = new ZhiPuAiChatModel( |
| 26 | - new ZhiPuAiApi("32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs"), // 密钥 | |
| 26 | + new ZhiPuAiApi("2f35fb6ca4ea41fab898729b7fac086c.6ESSfPcCkxaKEUlR"), // 密钥 | |
| 27 | 27 | ZhiPuAiChatOptions.builder() |
| 28 | 28 | .model(ZhiPuAiApi.ChatModel.GLM_4.getName()) // 模型 |
| 29 | 29 | .build() |
| ... | ... | @@ -61,4 +61,24 @@ public class ZhiPuAiChatModelTests { |
| 61 | 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 | 16 | */ |
| 17 | 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 | 25 | @Test |
| 23 | 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 | 36 | |
| 37 | 37 | @Test |
| 38 | 38 | public void testMcpGetUserInfo() { |
| 39 | - | |
| 40 | 39 | // 打印结果 |
| 41 | 40 | System.out.println(chatClient.prompt() |
| 42 | 41 | .user("目前有哪些工具可以使用") |
| 43 | 42 | .call() |
| 44 | 43 | .content()); |
| 45 | 44 | System.out.println("===================================="); |
| 45 | + | |
| 46 | 46 | // 打印结果 |
| 47 | 47 | System.out.println(chatClient.prompt() |
| 48 | 48 | .user("小新的年龄是多少") |
| 49 | 49 | .call() |
| 50 | 50 | .content()); |
| 51 | 51 | System.out.println("===================================="); |
| 52 | + | |
| 52 | 53 | // 打印结果 |
| 53 | 54 | System.out.println(chatClient.prompt() |
| 54 | 55 | .user("获取小新的基本信息") |
| 55 | 56 | .call() |
| 56 | 57 | .content()); |
| 57 | 58 | System.out.println("===================================="); |
| 59 | + | |
| 58 | 60 | // 打印结果 |
| 59 | 61 | System.out.println(chatClient.prompt() |
| 60 | 62 | .user("小新是什么职业的") |
| 61 | 63 | .call() |
| 62 | 64 | .content()); |
| 63 | 65 | System.out.println("===================================="); |
| 66 | + | |
| 64 | 67 | // 打印结果 |
| 65 | 68 | System.out.println(chatClient.prompt() |
| 66 | 69 | .user("小新的教育背景") |
| 67 | 70 | .call() |
| 68 | 71 | .content()); |
| 69 | 72 | System.out.println("===================================="); |
| 73 | + | |
| 70 | 74 | // 打印结果 |
| 71 | 75 | System.out.println(chatClient.prompt() |
| 72 | 76 | .user("小新的兴趣爱好是什么") |
| 73 | 77 | .call() |
| 74 | 78 | .content()); |
| 75 | 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 | 29 | \ No newline at end of file | ... | ... |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/event/BpmProcessInstanceStatusEvent.java
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
| ... | ... | @@ -122,8 +122,9 @@ public interface BpmProcessInstanceConvert { |
| 122 | 122 | @Mapping(source = "from.id", target = "to.id", ignore = true) |
| 123 | 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 | 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 | 51 | String noPrefix = processIdRule.getPrefix() + infix + processIdRule.getPostfix(); |
| 52 | 52 | String key = RedisKeyConstants.BPM_PROCESS_ID + noPrefix; |
| 53 | 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 | 56 | stringRedisTemplate.expire(key, Duration.ofDays(1L)); |
| 57 | 57 | } |
| 58 | 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 | 737 | BoundaryEvent boundaryEvent = null; |
| 738 | 738 | if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { |
| 739 | 739 | boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), |
| 740 | - node.getDelaySetting().getDelayTime(), null, null); | |
| 740 | + null, null, node.getDelaySetting().getDelayTime()); | |
| 741 | 741 | } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { |
| 742 | 742 | boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), |
| 743 | - null, null, node.getDelaySetting().getDelayTime()); | |
| 743 | + node.getDelaySetting().getDelayTime(), null, null); | |
| 744 | 744 | } else { |
| 745 | 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 | 960 | // 1.3.1 获取父流程实例 并标记为不通过 |
| 961 | 961 | Execution execution = runtimeService.createExecutionQuery().executionId(instance.getSuperExecutionId()).singleResult(); |
| 962 | 962 | ProcessInstance parentProcessInstance = getProcessInstance(execution.getProcessInstanceId()); |
| 963 | - updateProcessInstanceReject(parentProcessInstance, REJECT_CHILD_PROCESS.getReason()); | |
| 963 | + updateProcessInstanceReject(parentProcessInstance, BpmReasonEnum.REJECT_CHILD_PROCESS.getReason()); | |
| 964 | 964 | |
| 965 | 965 | // 1.3.2 结束父流程。需要在子流程结束事务提交后执行 |
| 966 | 966 | TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { |
| ... | ... | @@ -971,7 +971,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService |
| 971 | 971 | if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) { |
| 972 | 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 | 987 | |
| 988 | 988 | // 3. 发送流程实例的状态事件 |
| 989 | 989 | processInstanceEventPublisher.sendProcessInstanceResultEvent( |
| 990 | - BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status)); | |
| 990 | + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status, reason)); | |
| 991 | 991 | |
| 992 | 992 | // 4. 流程后置通知 |
| 993 | 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 | 7 | import lombok.Data; |
| 8 | 8 | import lombok.EqualsAndHashCode; |
| 9 | 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 | 16 | @Schema(description = "管理后台 - 线索分页 Request VO") |
| 12 | 17 | @Data |
| ... | ... | @@ -42,4 +47,8 @@ public class CrmCluePageReqVO extends PageParam { |
| 42 | 47 | @Schema(description = "跟进状态", example = "true") |
| 43 | 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 | 10 | import lombok.Builder; |
| 11 | 11 | import lombok.Data; |
| 12 | 12 | import lombok.NoArgsConstructor; |
| 13 | -import lombok.experimental.Accessors; | |
| 14 | 13 | |
| 15 | 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 | 20 | @Builder |
| 22 | 21 | @AllArgsConstructor |
| 23 | 22 | @NoArgsConstructor |
| 24 | -@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题 | |
| 25 | 23 | public class CrmCustomerImportExcelVO { |
| 26 | 24 | |
| 27 | 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<CrmClueDO> { |
| 33 | 33 | .eqIfPresent(CrmClueDO::getLevel, pageReqVO.getLevel()) |
| 34 | 34 | .eqIfPresent(CrmClueDO::getSource, pageReqVO.getSource()) |
| 35 | 35 | .eqIfPresent(CrmClueDO::getFollowUpStatus, pageReqVO.getFollowUpStatus()) |
| 36 | + .betweenIfPresent(CrmClueDO::getCreateTime, pageReqVO.getCreateTime()) | |
| 36 | 37 | .orderByDesc(CrmClueDO::getId); |
| 37 | 38 | return selectJoinPage(pageReqVO, CrmClueDO.class, query); |
| 38 | 39 | } | ... | ... |
yudao-module-infra/pom.xml
| ... | ... | @@ -101,7 +101,7 @@ |
| 101 | 101 | <artifactId>commons-net</artifactId> <!-- 文件客户端:解决 ftp 连接 --> |
| 102 | 102 | </dependency> |
| 103 | 103 | <dependency> |
| 104 | - <groupId>com.jcraft</groupId> | |
| 104 | + <groupId>com.github.mwiede</groupId> | |
| 105 | 105 | <artifactId>jsch</artifactId> <!-- 文件客户端:解决 sftp 连接 --> |
| 106 | 106 | </dependency> |
| 107 | 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 | 26 | import org.springframework.web.bind.annotation.*; |
| 27 | 27 | import org.springframework.web.multipart.MultipartFile; |
| 28 | 28 | |
| 29 | +import java.nio.charset.StandardCharsets; | |
| 29 | 30 | import java.util.List; |
| 30 | 31 | |
| 31 | 32 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| ... | ... | @@ -99,8 +100,10 @@ public class FileController { |
| 99 | 100 | if (StrUtil.isEmpty(path)) { |
| 100 | 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 | 109 | byte[] content = fileService.getFileContent(configId, path); | ... | ... |