Commit 39916475600675569e9b7ca1245720d369f20f68

Authored by YunaiV
2 parents 113dfae7 b634a1e9

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

Showing 594 changed files with 23312 additions and 9190 deletions

Too many changes.

To preserve performance only 100 of 594 files are displayed.

.gitignore
... ... @@ -51,3 +51,4 @@ rebel.xml
51 51 application-my.yaml
52 52  
53 53 /yudao-ui-app/unpackage/
  54 +**/.DS_Store
... ...
.image/common/ai-feature.png

20.7 KB | W: | H:

24.9 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
... ... @@ -32,7 +32,7 @@
32 32 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
33 33  
34 34 <properties>
35   - <revision>2.4.0-SNAPSHOT</revision>
  35 + <revision>2.4.1-SNAPSHOT</revision>
36 36 <!-- Maven 相关 -->
37 37 <java.version>17</java.version>
38 38 <maven.compiler.source>${java.version}</maven.compiler.source>
... ...
script/idea/http-client.env.json
... ... @@ -2,16 +2,16 @@
2 2 "local": {
3 3 "baseUrl": "http://127.0.0.1:48080/admin-api",
4 4 "token": "test1",
5   - "adminTenentId": "1",
  5 + "adminTenantId": "1",
6 6  
7 7 "appApi": "http://127.0.0.1:48080/app-api",
8 8 "appToken": "test247",
9   - "appTenentId": "1"
  9 + "appTenantId": "1"
10 10 },
11 11 "gateway": {
12 12 "baseUrl": "http://127.0.0.1:8888/admin-api",
13 13 "token": "test1",
14   - "adminTenentId": "1",
  14 + "adminTenantId": "1",
15 15  
16 16 "appApi": "http://127.0.0.1:8888/app-api",
17 17 "appToken": "test1",
... ...
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: 31/12/2024 09:16:18
  14 + Date: 17/03/2025 13:14:16
15 15 */
16 16  
17 17 SET NAMES utf8mb4;
... ... @@ -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 = 21226 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
  94 +) ENGINE = InnoDB AUTO_INCREMENT = 21482 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 = 2483 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
  131 +) ENGINE = InnoDB AUTO_INCREMENT = 2538 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 = 187 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
  169 +) ENGINE = InnoDB AUTO_INCREMENT = 191 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
170 170  
171 171 -- ----------------------------
172 172 -- Records of infra_codegen_table
... ... @@ -250,7 +250,7 @@ CREATE TABLE `infra_file` (
250 250 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
251 251 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
252 252 PRIMARY KEY (`id`) USING BTREE
253   -) ENGINE = InnoDB AUTO_INCREMENT = 1577 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
  253 +) ENGINE = InnoDB AUTO_INCREMENT = 1657 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
254 254  
255 255 -- ----------------------------
256 256 -- Records of infra_file
... ... @@ -444,7 +444,7 @@ CREATE TABLE `system_dict_data` (
444 444 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
445 445 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
446 446 PRIMARY KEY (`id`) USING BTREE
447   -) ENGINE = InnoDB AUTO_INCREMENT = 1683 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
  447 +) ENGINE = InnoDB AUTO_INCREMENT = 3000 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
448 448  
449 449 -- ----------------------------
450 450 -- Records of system_dict_data
... ... @@ -831,7 +831,7 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
831 831 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 (1551, 2, '描述模式', '2', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:37', '1', '2024-06-28 01:22:24', b'0');
832 832 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 (1552, 8, 'Suno', 'Suno', 'ai_platform', 0, '', '', '', '1', '2024-06-29 09:13:36', '1', '2024-06-29 09:13:41', b'0');
833 833 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 (1553, 9, 'DeepSeek', 'DeepSeek', 'ai_platform', 0, '', '', '', '1', '2024-07-06 12:04:30', '1', '2024-07-06 12:05:20', b'0');
834   -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 (1554, 10, '智谱', 'ZhiPu', 'ai_platform', 0, '', '', '', '1', '2024-07-06 18:00:35', '1', '2024-07-06 18:00:35', b'0');
  834 +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 (1554, 13, '智谱', 'ZhiPu', 'ai_platform', 0, '', '', '', '1', '2024-07-06 18:00:35', '1', '2025-02-24 20:18:41', b'0');
835 835 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 (1555, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', b'0');
836 836 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 (1556, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', b'0');
837 837 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 (1557, 6, '文章', '6', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:05', '1', '2024-07-07 15:50:05', b'0');
... ... @@ -870,31 +870,190 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
870 870 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 (1591, 4, '七牛云', 'QINIU', 'system_sms_channel_code', 0, '', '', '', '1', '2024-08-31 08:45:03', '1', '2024-08-31 08:45:24', b'0');
871 871 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 (1592, 3, '新人券', '3', 'promotion_coupon_take_type', 0, 'info', '', '新人注册后,自动发放', '1', '2024-09-03 11:57:16', '1', '2024-09-03 11:57:28', b'0');
872 872 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 (1593, 5, '微信零钱', '5', 'brokerage_withdraw_type', 0, '', '', '自动打款', '1', '2024-10-13 11:06:48', '1', '2024-10-13 11:06:59', b'0');
873   -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 (1655, 0, '标准数据格式(JSON)', '0', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:26', '1', '2024-09-06 14:31:02', b'0');
874   -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 (1656, 1, '透传/自定义', '1', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:37', '1', '2024-09-06 14:30:54', b'0');
875   -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 (1657, 0, '直连设备', '0', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:54:58', '1', '2024-09-06 21:57:01', b'0');
876   -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 (1658, 2, '网关设备', '2', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:08', '1', '2024-09-06 21:56:46', b'0');
877   -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 (1659, 1, '网关子设备', '1', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:20', '1', '2024-09-06 21:57:10', b'0');
878   -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 (1661, 1, '已发布', '1', 'iot_product_status', 0, 'success', '', '', '1', '2024-08-10 12:10:33', '1', '2024-09-06 22:06:22', b'0');
879   -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 (1663, 0, '开发中', '0', 'iot_product_status', 0, 'default', '', '', '1', '2024-08-10 14:19:18', '1', '2024-09-07 10:58:07', b'0');
880   -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 (1665, 0, '弱校验', '0', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:05:48', '1', '2024-09-06 22:02:44', b'0');
881   -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1666, 1, '免校验', '1', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:06:03', '1', '2024-09-06 22:02:51', b'0');
882   -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1667, 0, 'Wi-Fi', '0', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:04:47', '1', '2024-09-06 22:04:47', b'0');
883   -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1668, 1, '蜂窝(2G / 3G / 4G / 5G)', '1', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:14', '1', '2024-09-06 22:05:14', 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 (1669, 2, '以太网', '2', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:35', '1', '2024-09-06 22:05:35', 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 (1670, 3, '其他', '3', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:52', '1', '2024-09-06 22:05:52', b'0');
886   -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1671, 0, '自定义', '0', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:10', '1', '2024-09-06 22:26:10', b'0');
887   -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1672, 1, 'Modbus', '1', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:21', '1', '2024-09-06 22:26:21', b'0');
888   -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1673, 2, 'OPC UA', '2', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:31', '1', '2024-09-06 22:26:31', b'0');
889   -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1674, 3, 'ZigBee', '3', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:39', '1', '2024-09-06 22:26:39', b'0');
890   -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1675, 4, 'BLE', '4', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:48', '1', '2024-09-06 22:26:48', 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 (1676, 0, '未激活', '0', 'iot_device_status', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2024-09-21 08:13:34', 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 (1677, 1, '在线', '1', 'iot_device_status', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2024-09-21 08:13:48', 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 (1678, 2, '离线', '2', 'iot_device_status', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2024-09-21 08:13:59', 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 (1679, 3, '已禁用', '3', 'iot_device_status', 0, '', '', '', '1', '2024-09-21 08:14:13', '1', '2024-09-21 08:14:13', b'0');
895   -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1680, 1, '属性', '1', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:01', '1', '2024-09-29 20:09:41', b'0');
896   -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1681, 2, '服务', '2', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:11', '1', '2024-09-29 20:08:23', 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 (1682, 3, '事件', '3', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:20', '1', '2024-09-29 20:08:20', b'0');
  873 +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 (1683, 10, '字节豆包', 'DouBao', 'ai_platform', 0, '', '', '', '1', '2025-02-23 19:51:40', '1', '2025-02-23 19:52:02', b'0');
  874 +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 (1684, 11, '腾讯混元', 'HunYuan', 'ai_platform', 0, '', '', '', '1', '2025-02-23 20:58:04', '1', '2025-02-23 20:58:04', b'0');
  875 +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 (1685, 12, '硅基流动', 'SiliconFlow', 'ai_platform', 0, '', '', '', '1', '2025-02-24 20:19:09', '1', '2025-02-24 20:19:09', b'0');
  876 +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 (1686, 1, '聊天', '1', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:26:34', '1', '2025-03-03 12:26:34', b'0');
  877 +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 (1687, 2, '图像', '2', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:23', '1', '2025-03-03 12:27:23', b'0');
  878 +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 (1688, 3, '音频', '3', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:51', '1', '2025-03-03 12:27:51', b'0');
  879 +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 (1689, 4, '视频', '4', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:03', '1', '2025-03-03 12:28:03', b'0');
  880 +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 (1690, 5, '向量', '5', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:15', '1', '2025-03-03 12:28:15', b'0');
  881 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1691, 6, '重排', '6', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:26', '1', '2025-03-03 12:28:26', b'0');
  882 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1692, 14, 'MiniMax', 'MiniMax', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:04:51', '1', '2025-03-11 20:04:51', b'0');
  883 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1693, 15, '月之暗灭', 'Moonshot', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:05:08', '1', '2025-03-11 20:05:08', b'0');
  884 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2000, 0, '标准数据格式(JSON)', '0', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:26', '1', '2025-03-17 09:28:16', b'0');
  885 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2001, 1, '透传/自定义', '1', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:37', '1', '2025-03-17 09:28:19', b'0');
  886 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2002, 0, '直连设备', '0', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:54:58', '1', '2025-03-17 09:28:22', b'0');
  887 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2003, 2, '网关设备', '2', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:08', '1', '2025-03-17 09:28:28', b'0');
  888 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2004, 1, '网关子设备', '1', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:20', '1', '2025-03-17 09:28:31', b'0');
  889 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2005, 1, '已发布', '1', 'iot_product_status', 0, 'success', '', '', '1', '2024-08-10 12:10:33', '1', '2025-03-17 09:28:34', b'0');
  890 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2006, 0, '开发中', '0', 'iot_product_status', 0, 'default', '', '', '1', '2024-08-10 14:19:18', '1', '2025-03-17 09:28:39', b'0');
  891 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2007, 0, '弱校验', '0', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:05:48', '1', '2025-03-17 09:28:41', b'0');
  892 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2008, 1, '免校验', '1', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:06:03', '1', '2025-03-17 09:28:44', b'0');
  893 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2009, 0, 'Wi-Fi', '0', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:04:47', '1', '2025-03-17 09:28:47', b'0');
  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');
  895 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2011, 2, '以太网', '2', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:35', '1', '2025-03-17 09:28:51', b'0');
  896 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2012, 3, '其他', '3', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:52', '1', '2025-03-17 09:28:54', b'0');
  897 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2013, 0, '自定义', '0', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:10', '1', '2025-03-17 09:28:56', b'0');
  898 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2014, 1, 'Modbus', '1', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:21', '1', '2025-03-17 09:28:58', b'0');
  899 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2015, 2, 'OPC UA', '2', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:31', '1', '2025-03-17 09:29:00', b'0');
  900 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2016, 3, 'ZigBee', '3', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:39', '1', '2025-03-17 09:29:04', b'0');
  901 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2017, 4, 'BLE', '4', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:48', '1', '2025-03-17 09:29:06', b'0');
  902 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2018, 0, '未激活', '0', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2025-03-17 09:29:09', b'0');
  903 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2019, 1, '在线', '1', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2025-03-17 09:29:12', b'0');
  904 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2020, 2, '离线', '2', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2025-03-17 09:29:14', b'0');
  905 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2021, 1, '属性', '1', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:01', '1', '2025-03-17 09:29:24', b'0');
  906 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2022, 2, '服务', '2', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:11', '1', '2025-03-17 09:29:27', b'0');
  907 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2023, 3, '事件', '3', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:20', '1', '2025-03-17 09:29:29', b'0');
  908 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2024, 1, 'JAR 部署', '0', 'iot_plugin_deploy_type', 0, '', '', '', '1', '2024-12-13 10:55:32', '1', '2025-03-17 09:29:32', b'0');
  909 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2025, 2, '独立部署', '1', 'iot_plugin_deploy_type', 0, '', '', '', '1', '2024-12-13 10:55:43', '1', '2025-03-17 09:29:34', b'0');
  910 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2026, 0, '停止', '0', 'iot_plugin_status', 0, 'danger', '', '', '1', '2024-12-13 11:07:37', '1', '2025-03-17 09:29:37', b'0');
  911 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2027, 1, '运行', '1', 'iot_plugin_status', 0, '', '', '', '1', '2024-12-13 11:07:45', '1', '2025-03-17 09:34:17', b'0');
  912 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2028, 0, '普通插件', '0', 'iot_plugin_type', 0, '', '', '', '1', '2024-12-13 11:08:32', '1', '2025-03-17 09:34:19', b'0');
  913 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2029, 1, '设备插件', '1', 'iot_plugin_type', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:22', b'0');
  914 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2030, 1, '升每分钟', 'L/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:24', b'0');
  915 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2031, 2, '毫克每千克', 'mg/kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:27', b'0');
  916 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2032, 3, '浊度', 'NTU', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:31', b'0');
  917 +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 (2033, 4, 'PH值', 'pH', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:36', b'0');
  918 +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 (2034, 5, '土壤EC值', 'dS/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:43', b'0');
  919 +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 (2035, 6, '太阳总辐射', 'W/㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:20', b'0');
  920 +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 (2036, 7, '降雨量', 'mm/hour', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:24', b'0');
  921 +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 (2037, 8, '乏', 'var', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:27', b'0');
  922 +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 (2038, 9, '厘泊', 'cP', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:33', b'0');
  923 +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 (2039, 10, '饱和度', 'aw', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:11', b'0');
  924 +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 (2040, 11, '个', 'pcs', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:19', b'0');
  925 +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 (2041, 12, '厘斯', 'cst', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:22', b'0');
  926 +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 (2042, 13, '巴', 'bar', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:24', b'0');
  927 +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 (2043, 14, '纳克每升', 'ppt', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:27', b'0');
  928 +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 (2044, 15, '微克每升', 'ppb', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:31', b'0');
  929 +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 (2045, 16, '微西每厘米', 'uS/cm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:34', b'0');
  930 +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 (2046, 17, '牛顿每库仑', 'N/C', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:38', b'0');
  931 +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 (2047, 18, '伏特每米', 'V/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:43', b'0');
  932 +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 (2048, 19, '滴速', 'ml/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:46', b'0');
  933 +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 (2049, 20, '毫米汞柱', 'mmHg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:48', b'0');
  934 +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 (2050, 21, '血糖', 'mmol/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:54', b'0');
  935 +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 (2051, 22, '毫米每秒', 'mm/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:02', b'0');
  936 +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 (2052, 23, '转每分钟', 'turn/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:07', b'0');
  937 +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 (2053, 24, '次', 'count', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:09', b'0');
  938 +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 (2054, 25, '档', 'gear', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:11', b'0');
  939 +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 (2055, 26, '步', 'stepCount', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:13', b'0');
  940 +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 (2056, 27, '标准立方米每小时', 'Nm3/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:15', b'0');
  941 +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 (2057, 28, '千伏', 'kV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:20', b'0');
  942 +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 (2058, 29, '千伏安', 'kVA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:24', b'0');
  943 +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 (2060, 30, '千乏', 'kVar', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  944 +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 (2061, 31, '微瓦每平方厘米', 'uw/cm2', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  945 +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 (2062, 32, '只', '只', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  946 +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 (2063, 33, '相对湿度', '%RH', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  947 +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 (2064, 34, '立方米每秒', 'm³/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  948 +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 (2065, 35, '公斤每秒', 'kg/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  949 +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 (2066, 36, '转每分钟', 'r/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  950 +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 (2067, 37, '吨每小时', 't/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  951 +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 (2068, 38, '千卡每小时', 'KCL/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  952 +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 (2069, 39, '升每秒', 'L/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  953 +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 (2070, 40, '兆帕', 'Mpa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  954 +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 (2071, 41, '立方米每小时', 'm³/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  955 +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 (2072, 42, '千乏时', 'kvarh', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  956 +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 (2073, 43, '微克每升', 'μg/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  957 +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 (2074, 44, '千卡路里', 'kcal', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  958 +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 (2075, 45, '吉字节', 'GB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  959 +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 (2076, 46, '兆字节', 'MB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  960 +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 (2077, 47, '千字节', 'KB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  961 +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 (2078, 48, '字节', 'B', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  962 +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 (2079, 49, '微克每平方分米每天', 'μg/(d㎡·d)', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  963 +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 (2080, 50, '无', '', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  964 +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 (2081, 51, '百万分率', 'ppm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  965 +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 (2082, 52, '像素', 'pixel', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  966 +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 (2083, 53, '照度', 'Lux', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  967 +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 (2084, 54, '重力加速度', 'grav', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  968 +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 (2085, 55, '分贝', 'dB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  969 +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 (2086, 56, '百分比', '%', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  970 +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 (2087, 57, '流明', 'lm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  971 +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 (2088, 58, '比特', 'bit', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  972 +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 (2089, 59, '克每毫升', 'g/mL', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  973 +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 (2090, 60, '克每升', 'g/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  974 +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 (2091, 61, '毫克每升', 'mg/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  975 +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 (2092, 62, '微克每立方米', 'μg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  976 +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 (2093, 63, '毫克每立方米', 'mg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  977 +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 (2094, 64, '克每立方米', 'g/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  978 +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 (2095, 65, '千克每立方米', 'kg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  979 +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 (2096, 66, '纳法', 'nF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  980 +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 (2097, 67, '皮法', 'pF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  981 +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 (2098, 68, '微法', 'μF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  982 +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 (2099, 69, '法拉', 'F', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  983 +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 (2100, 70, '欧姆', 'Ω', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  984 +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 (2101, 71, '微安', 'μA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  985 +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 (2102, 72, '毫安', 'mA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  986 +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 (2103, 73, '千安', 'kA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  987 +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 (2104, 74, '安培', 'A', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  988 +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 (2105, 75, '毫伏', 'mV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  989 +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 (2106, 76, '伏特', 'V', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  990 +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 (2107, 77, '毫秒', 'ms', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  991 +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 (2108, 78, '秒', 's', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  992 +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 (2109, 79, '分钟', 'min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  993 +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 (2110, 80, '小时', 'h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  994 +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 (2111, 81, '日', 'day', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  995 +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 (2112, 82, '周', 'week', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  996 +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 (2113, 83, '月', 'month', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  997 +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 (2114, 84, '年', 'year', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  998 +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 (2115, 85, '节', 'kn', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  999 +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 (2116, 86, '千米每小时', 'km/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1000 +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 (2117, 87, '米每秒', 'm/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1001 +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 (2118, 88, '秒', '″', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1002 +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 (2119, 89, '分', '′', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1003 +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 (2120, 90, '度', '°', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1004 +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 (2121, 91, '弧度', 'rad', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1005 +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 (2122, 92, '赫兹', 'Hz', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1006 +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 (2123, 93, '微瓦', 'μW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1007 +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 (2124, 94, '毫瓦', 'mW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1008 +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 (2125, 95, '千瓦特', 'kW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1009 +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 (2126, 96, '瓦特', 'W', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1010 +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 (2127, 97, '卡路里', 'cal', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1011 +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 (2128, 98, '千瓦时', 'kW·h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1012 +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 (2129, 99, '瓦时', 'Wh', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1013 +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 (2130, 100, '电子伏', 'eV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1014 +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 (2131, 101, '千焦', 'kJ', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1015 +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 (2132, 102, '焦耳', 'J', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1016 +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 (2133, 103, '华氏度', '℉', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1017 +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 (2134, 104, '开尔文', 'K', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1018 +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 (2135, 105, '吨', 't', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1019 +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 (2136, 106, '摄氏度', '°C', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1020 +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 (2137, 107, '毫帕', 'mPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1021 +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 (2138, 108, '百帕', 'hPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1022 +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 (2139, 109, '千帕', 'kPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1023 +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 (2140, 110, '帕斯卡', 'Pa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1024 +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 (2141, 111, '毫克', 'mg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1025 +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 (2142, 112, '克', 'g', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1026 +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 (2143, 113, '千克', 'kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1027 +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 (2144, 114, '牛', 'N', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1028 +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 (2145, 115, '毫升', 'mL', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1029 +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 (2146, 116, '升', 'L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1030 +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 (2147, 117, '立方毫米', 'mm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1031 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2148, 118, '立方厘米', 'cm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1032 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2149, 119, '立方千米', 'km³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1033 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2150, 120, '立方米', 'm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '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 (2151, 121, '公顷', 'h㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', 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 (2152, 122, '平方厘米', 'c㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40: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 (2153, 123, '平方毫米', 'm㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', 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 (2154, 124, '平方千米', 'k㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', 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 (2155, 125, '平方米', '㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', 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 (2156, 126, '纳米', 'nm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', 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 (2157, 127, '微米', 'μm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', 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 (2158, 128, '毫米', 'mm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', 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 (2159, 129, '厘米', 'cm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', 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 (2160, 130, '分米', 'dm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1044 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2161, 131, '千米', 'km', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  1045 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2162, 132, '米', 'm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0');
  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');
898 1057 COMMIT;
899 1058  
900 1059 -- ----------------------------
... ... @@ -914,7 +1073,7 @@ CREATE TABLE `system_dict_type` (
914 1073 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
915 1074 `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
916 1075 PRIMARY KEY (`id`) USING BTREE
917   -) ENGINE = InnoDB AUTO_INCREMENT = 640 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
  1076 +) ENGINE = InnoDB AUTO_INCREMENT = 2000 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
918 1077  
919 1078 -- ----------------------------
920 1079 -- Records of system_dict_type
... ... @@ -1013,14 +1172,21 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat
1013 1172 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (627, '写作格式', 'ai_write_format', 0, '', '1', '2024-07-07 15:14:34', '1', '2024-07-07 15:14:34', b'0', '1970-01-01 00:00:00');
1014 1173 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');
1015 1174 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');
1016   -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (630, 'IOT 接入网关协议', 'iot_protocol_type', 0, '', '1', '2024-09-06 22:20:17', '1', '2024-09-06 22:20:17', b'0', '1970-01-01 00:00:00');
1017   -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (631, 'IOT 设备状态', 'iot_device_status', 0, '', '1', '2024-09-21 08:12:55', '1', '2024-09-21 08:12:55', b'0', '1970-01-01 00:00:00');
1018   -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (632, 'IOT 物模型功能类型', 'iot_product_function_type', 0, '', '1', '2024-09-29 20:02:36', '1', '2024-09-29 20:09:26', b'0', '1970-01-01 00:00:00');
1019   -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (634, 'IOT 数据格式', 'iot_data_format', 0, '', '1', '2024-08-10 11:52:58', '1', '2024-09-06 14:30:14', b'0', '1970-01-01 00:00:00');
1020   -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (635, 'IOT 产品设备类型', 'iot_product_device_type', 0, '', '1', '2024-08-10 11:54:30', '1', '2024-08-10 04:06:56', b'0', '1970-01-01 00:00:00');
1021   -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (637, 'IOT 产品状态', 'iot_product_status', 0, '', '1', '2024-08-10 12:06:09', '1', '2024-08-10 12:06:09', b'0', '1970-01-01 00:00:00');
1022   -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (638, 'IOT 数据校验级别', 'iot_validate_type', 0, '', '1', '2024-09-06 20:05:13', '1', '2024-09-06 20:05:13', b'0', '1970-01-01 00:00:00');
1023   -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (639, 'IOT 联网方式', 'iot_net_type', 0, '', '1', '2024-09-06 22:04:13', '1', '2024-09-06 22:04:13', b'0', '1970-01-01 00:00:00');
  1175 +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');
  1176 +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');
  1177 +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');
  1178 +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');
  1179 +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');
  1180 +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');
  1181 +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');
  1182 +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');
  1183 +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');
  1184 +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');
  1185 +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');
  1186 +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');
  1187 +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');
  1188 +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');
  1189 +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-03-17 09:25:43', b'0', '1970-01-01 00:00:00');
1024 1190 COMMIT;
1025 1191  
1026 1192 -- ----------------------------
... ... @@ -1044,7 +1210,7 @@ CREATE TABLE `system_login_log` (
1044 1210 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
1045 1211 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
1046 1212 PRIMARY KEY (`id`) USING BTREE
1047   -) ENGINE = InnoDB AUTO_INCREMENT = 3415 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
  1213 +) ENGINE = InnoDB AUTO_INCREMENT = 3446 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
1048 1214  
1049 1215 -- ----------------------------
1050 1216 -- Records of system_login_log
... ... @@ -1175,16 +1341,16 @@ CREATE TABLE `system_menu` (
1175 1341 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
1176 1342 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
1177 1343 PRIMARY KEY (`id`) USING BTREE
1178   -) ENGINE = InnoDB AUTO_INCREMENT = 2913 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
  1344 +) ENGINE = InnoDB AUTO_INCREMENT = 5000 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
1179 1345  
1180 1346 -- ----------------------------
1181 1347 -- Records of system_menu
1182 1348 -- ----------------------------
1183 1349 BEGIN;
1184   -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 (1, '系统管理', '', 1, 10, 0, '/system', 'ep:tools', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-06-18 01:19:41', b'0');
  1350 +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 (1, '系统管理', '', 1, 10, 0, '/system', 'ep:tools', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2025-03-15 21:30:27', b'0');
1185 1351 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 (2, '基础设施', '', 1, 20, 0, '/infra', 'ep:monitor', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-03-01 08:28:40', b'0');
1186 1352 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 (5, 'OA 示例', '', 1, 40, 1185, 'oa', 'fa:road', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-09-20 16:26:19', '1', '2024-02-29 12:38:13', b'0');
1187   -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 (100, '用户管理', 'system:user:list', 2, 1, 1, 'user', 'ep:avatar', 'system/user/index', 'SystemUser', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:02:04', b'0');
  1353 +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 (100, '用户管理', 'system:user:list', 2, 1, 1, 'user', 'ep:avatar', 'system/user/index', 'SystemUser', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2025-03-15 21:30:41', b'0');
1188 1354 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 (101, '角色管理', '', 2, 2, 1, 'role', 'ep:user', 'system/role/index', 'SystemRole', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-05-01 18:35:29', b'0');
1189 1355 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 (102, '菜单管理', '', 2, 3, 1, 'menu', 'ep:menu', 'system/menu/index', 'SystemMenu', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:03:50', b'0');
1190 1356 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 (103, '部门管理', '', 2, 4, 1, 'dept', 'fa:address-card', 'system/dept/index', 'SystemDept', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:06:28', b'0');
... ... @@ -1373,7 +1539,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
1373 1539 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 (1241, '文件配置删除', 'infra:file-config:delete', 3, 4, 1237, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', b'0');
1374 1540 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 (1242, '文件配置导出', 'infra:file-config:export', 3, 5, 1237, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', b'0');
1375 1541 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 (1243, '文件管理', '', 2, 6, 2, 'file', 'ep:files', NULL, '', 0, b'1', b'1', b'1', '1', '2022-03-16 23:47:40', '1', '2024-04-23 00:02:11', b'0');
1376   -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 (1254, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'ep:avatar', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-04-23 01:03:15', '1', '2024-09-06 09:19:42', b'0');
  1542 +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 (1254, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'ep:avatar', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-04-23 01:03:15', '104', '2025-01-04 10:59:37', b'0');
1377 1543 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 (1255, '数据源配置', '', 2, 1, 2, 'data-source-config', 'ep:data-analysis', 'infra/dataSourceConfig/index', 'InfraDataSourceConfig', 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '1', '2024-02-29 08:51:25', b'0');
1378 1544 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 (1256, '数据源配置查询', 'infra:data-source-config:query', 3, 1, 1255, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0');
1379 1545 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 (1257, '数据源配置创建', 'infra:data-source-config:create', 3, 2, 1255, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0');
... ... @@ -1696,7 +1862,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
1696 1862 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 (2431, '回款计划更新', 'crm:receivable-plan:update', 3, 3, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');
1697 1863 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 (2432, '回款计划删除', 'crm:receivable-plan:delete', 3, 4, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');
1698 1864 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 (2433, '回款计划导出', 'crm:receivable-plan:export', 3, 5, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');
1699   -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 (2435, '商城装修', '', 2, 20, 2030, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', 'DiyTemplate', 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0');
  1865 +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 (2435, '商城装修', '', 2, 20, 2030, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', '', 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '1', '2025-03-15 21:34:33', b'0');
1700 1866 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 (2436, '装修模板', '', 2, 1, 2435, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', 'DiyTemplate', 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0');
1701 1867 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 (2437, '装修模板查询', 'promotion:diy-template:query', 3, 1, 2436, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0');
1702 1868 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 (2438, '装修模板创建', 'promotion:diy-template:create', 3, 2, 2436, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0');
... ... @@ -1978,11 +2144,11 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
1978 2144 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 (2763, 'API 密钥创建', 'ai:api-key:create', 3, 2, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:26', b'0');
1979 2145 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 (2764, 'API 密钥更新', 'ai:api-key:update', 3, 3, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:42', b'0');
1980 2146 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 (2765, 'API 密钥删除', 'ai:api-key:delete', 3, 4, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:48', b'0');
1981   -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 (2767, '聊天模型', '', 2, 0, 2760, 'chat-model', 'fa-solid:abacus', 'ai/model/chatModel/index.vue', 'AiChatModel', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-10 22:44:16', b'0');
1982   -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 (2768, '聊天模型查询', 'ai:chat-model:query', 3, 1, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:02', b'0');
1983   -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 (2769, '聊天模型创建', 'ai:chat-model:create', 3, 2, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:12', b'0');
1984   -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 (2770, '聊天模型更新', 'ai:chat-model:update', 3, 3, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:18', b'0');
1985   -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 (2771, '聊天模型删除', 'ai:chat-model:delete', 3, 4, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:23', b'0');
  2147 +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 (2767, '模型配置', '', 2, 0, 2760, 'model', 'fa-solid:abacus', 'ai/model/model/index.vue', 'AiModel', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:57:41', b'0');
  2148 +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 (2768, '聊天模型查询', 'ai:model:query', 3, 1, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:19:46', b'0');
  2149 +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 (2769, '聊天模型创建', 'ai:model:create', 3, 2, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:10', b'0');
  2150 +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 (2770, '聊天模型更新', 'ai:model:update', 3, 3, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:14', b'0');
  2151 +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 (2771, '聊天模型删除', 'ai:model:delete', 3, 4, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:27', b'0');
1986 2152 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 (2773, '聊天角色', '', 2, 0, 2760, 'chat-role', 'fa:user-secret', 'ai/model/chatRole/index.vue', 'AiChatRole', 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '1', '2024-05-13 20:41:45', b'0');
1987 2153 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 (2774, '聊天角色查询', 'ai:chat-role:query', 3, 1, 2773, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', b'0');
1988 2154 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 (2775, '聊天角色创建', 'ai:chat-role:create', 3, 2, 2773, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', b'0');
... ... @@ -2008,7 +2174,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
2008 2174 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 (2795, 'AI 写作删除', 'ai:write:delete', 3, 4, 2793, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', b'0');
2009 2175 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 (2796, 'AI 音乐', '', 2, 4, 2758, 'music', 'fa:music', 'ai/music/index/index.vue', 'AiMusic', 0, b'1', b'1', b'1', '1', '2024-07-17 09:21:12', '1', '2024-07-29 21:11:52', b'0');
2010 2176 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 (2797, '客服中心', '', 2, 100, 2362, 'kefu', 'fa-solid:user-alt', 'mall/promotion/kefu/index', 'KeFu', 0, b'1', b'1', b'1', '1', '2024-07-17 23:49:05', '1', '2024-07-17 23:49:16', b'0');
2011   -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 (2798, 'AI 思维导图', '', 2, 5, 2758, 'mind-map', 'fa:sitemap', 'ai/mindmap/index/index.vue', 'AiMindMap', 0, b'1', b'1', b'1', '1', '2024-07-29 21:31:59', '1', '2024-07-29 21:33:20', b'0');
  2177 +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 (2798, 'AI 思维导图', '', 2, 6, 2758, 'mind-map', 'fa:sitemap', 'ai/mindmap/index/index.vue', 'AiMindMap', 0, b'1', b'1', b'1', '1', '2024-07-29 21:31:59', '1', '2025-03-02 18:57:31', b'0');
2012 2178 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 (2799, '导图管理', '', 2, 14, 2760, 'mind-map', 'fa:map', 'ai/mindmap/manager/index', 'AiMindMapManager', 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '1', '2024-08-10 17:24:28', b'0');
2013 2179 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 (2800, '思维导图查询', 'ai:mind-map:query', 3, 1, 2799, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', b'0');
2014 2180 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 (2801, '思维导图删除', 'ai:mind-map:delete', 3, 4, 2799, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', b'0');
... ... @@ -2024,27 +2190,70 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
2024 2190 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 (2811, '积分商城活动更新', 'promotion:point-activity:update', 3, 3, 2808, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:10', b'0');
2025 2191 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 (2812, '积分商城活动删除', 'promotion:point-activity:delete', 3, 4, 2808, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:12', b'0');
2026 2192 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 (2813, '积分商城活动导出', 'promotion:point-activity:export', 3, 5, 2808, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:27', b'0');
2027   -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 (2892, 'IOT 物联网', '', 1, 500, 0, '/iot', 'fa-solid:hdd', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:55:29', '1', '2024-08-10 09:55:29', b'0');
2028   -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 (2893, '设备接入', '', 1, 1, 2892, 'device', 'ep:platform', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:57:56', '1', '2024-10-20 18:57:43', b'0');
2029   -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 (2894, '产品管理', '', 2, 0, 2893, 'product', '', 'iot/product/index', 'IoTProduct', 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '1', '2024-09-16 19:50:42', b'0');
2030   -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 (2895, '产品查询', 'iot:product:query', 3, 1, 2894, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-08-10 02:38:02', b'0');
2031   -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 (2896, '产品创建', 'iot:product:create', 3, 2, 2894, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-08-10 02:38:02', b'0');
2032   -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 (2897, '产品更新', 'iot:product:update', 3, 3, 2894, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-08-10 02:38:02', b'0');
2033   -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 (2898, '产品删除', 'iot:product:delete', 3, 4, 2894, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-08-10 02:38:02', b'0');
2034   -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 (2899, '产品导出', 'iot:product:export', 3, 5, 2894, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-08-10 02:38:02', b'0');
2035   -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 (2900, '设备管理', '', 2, 0, 2893, 'device', '', 'iot/device/index', 'IoTDevice', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-09-16 19:50:53', b'0');
2036   -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 (2901, '设备查询', 'iot:device:query', 3, 1, 2900, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-09-16 19:37:00', b'0');
2037   -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 (2902, '设备创建', 'iot:device:create', 3, 2, 2900, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-09-16 19:37:09', b'0');
2038   -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 (2903, '设备更新', 'iot:device:update', 3, 3, 2900, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-09-16 19:37:18', b'0');
2039   -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 (2904, '设备删除', 'iot:device:delete', 3, 4, 2900, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-09-16 19:37:42', b'0');
2040   -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 (2905, '设备导出', 'iot:device:export', 3, 5, 2900, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-09-16 19:37:49', b'0');
2041   -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 (2906, 'IoT 产品物模型管理', '', 1, 0, 2893, 'think-model-function', '', '', '', 0, b'0', b'1', b'1', '', '2024-09-25 22:12:09', '1', '2024-09-29 20:52:12', b'0');
2042   -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 (2907, 'IoT 产品物模型查询', 'iot:think-model-function:query', 3, 1, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0');
2043   -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 (2908, 'IoT 产品物模型创建', 'iot:think-model-function:create', 3, 2, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0');
2044   -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 (2909, 'IoT 产品物模型更新', 'iot:think-model-function:update', 3, 3, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0');
2045   -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 (2910, 'IoT 产品物模型删除', 'iot:think-model-function:delete', 3, 4, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0');
2046   -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 (2911, 'IoT 产品物模型导出', 'iot:think-model-function:export', 3, 5, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0');
2047 2193 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 (2912, '创建推广员', 'trade:brokerage-user:create', 3, 7, 2346, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-12-01 14:32:39', '1', '2024-12-01 14:32:39', b'0');
  2194 +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 (2913, '流程清理', 'bpm:model:clean', 3, 7, 1193, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-17 19:32:06', '1', '2025-01-17 19:32:06', b'0');
  2195 +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 (2914, '积分商城活动关闭', 'promotion:point-activity:close', 3, 6, 2808, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-23 20:23:34', '1', '2025-01-23 20:23:34', b'0');
  2196 +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 (2915, 'AI 知识库', '', 2, 5, 2758, 'knowledge', 'ep:notebook', 'ai/knowledge/knowledge/index', 'AiKnowledge', 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '1', '2025-03-02 18:58:37', b'0');
  2197 +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 (2916, 'AI 知识库查询', 'ai:knowledge:query', 3, 1, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0');
  2198 +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 (2917, 'AI 知识库创建', 'ai:knowledge:create', 3, 2, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0');
  2199 +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 (2918, 'AI 知识库更新', 'ai:knowledge:update', 3, 3, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0');
  2200 +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 (2919, 'AI 知识库删除', 'ai:knowledge:delete', 3, 4, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0');
  2201 +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 (2920, '工具管理', '', 2, 0, 2760, 'tool', 'fa-solid:tools', 'ai/model/tool/index.vue', 'AiTool', 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '1', '2025-03-14 19:20:18', b'0');
  2202 +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 (2921, '工具查询', 'ai:tool:query', 3, 1, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0');
  2203 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2922, '工具创建', 'ai:tool:create', 3, 2, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0');
  2204 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2923, '工具更新', 'ai:tool:update', 3, 3, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0');
  2205 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  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 (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');
  2207 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2208 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2209 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2210 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2211 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  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 (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');
  2213 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2214 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2215 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2216 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2217 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  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 (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');
  2219 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2220 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2221 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2222 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  2229 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2230 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2231 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2232 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  2233 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
  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 (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');
2048 2257 COMMIT;
2049 2258  
2050 2259 -- ----------------------------
... ... @@ -2166,7 +2375,7 @@ CREATE TABLE `system_oauth2_access_token` (
2166 2375 PRIMARY KEY (`id`) USING BTREE,
2167 2376 INDEX `idx_access_token`(`access_token` ASC) USING BTREE,
2168 2377 INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE
2169   -) ENGINE = InnoDB AUTO_INCREMENT = 12055 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
  2378 +) ENGINE = InnoDB AUTO_INCREMENT = 13787 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
2170 2379  
2171 2380 -- ----------------------------
2172 2381 -- Records of system_oauth2_access_token
... ... @@ -2288,7 +2497,7 @@ CREATE TABLE `system_oauth2_refresh_token` (
2288 2497 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
2289 2498 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
2290 2499 PRIMARY KEY (`id`) USING BTREE
2291   -) ENGINE = InnoDB AUTO_INCREMENT = 1711 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
  2500 +) ENGINE = InnoDB AUTO_INCREMENT = 1735 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
2292 2501  
2293 2502 -- ----------------------------
2294 2503 -- Records of system_oauth2_refresh_token
... ... @@ -2322,7 +2531,7 @@ CREATE TABLE `system_operate_log` (
2322 2531 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
2323 2532 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
2324 2533 PRIMARY KEY (`id`) USING BTREE
2325   -) ENGINE = InnoDB AUTO_INCREMENT = 9064 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本';
  2534 +) ENGINE = InnoDB AUTO_INCREMENT = 9065 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本';
2326 2535  
2327 2536 -- ----------------------------
2328 2537 -- Records of system_operate_log
... ... @@ -2783,9 +2992,7 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
2783 2992 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2139, 2, 1011, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
2784 2993 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2140, 2, 1012, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
2785 2994 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2141, 2, 1013, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
2786   -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2142, 2, 1014, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
2787 2995 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2143, 2, 1015, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
2788   -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2144, 2, 1016, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
2789 2996 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2145, 2, 1017, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
2790 2997 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2146, 2, 1018, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
2791 2998 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2147, 2, 1019, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
... ... @@ -3305,7 +3512,7 @@ CREATE TABLE `system_sms_code` (
3305 3512 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
3306 3513 PRIMARY KEY (`id`) USING BTREE,
3307 3514 INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'
3308   -) ENGINE = InnoDB AUTO_INCREMENT = 645 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
  3515 +) ENGINE = InnoDB AUTO_INCREMENT = 649 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
3309 3516  
3310 3517 -- ----------------------------
3311 3518 -- Records of system_sms_code
... ... @@ -3346,7 +3553,7 @@ CREATE TABLE `system_sms_log` (
3346 3553 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
3347 3554 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
3348 3555 PRIMARY KEY (`id`) USING BTREE
3349   -) ENGINE = InnoDB AUTO_INCREMENT = 1241 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
  3556 +) ENGINE = InnoDB AUTO_INCREMENT = 1279 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
3350 3557  
3351 3558 -- ----------------------------
3352 3559 -- Records of system_sms_log
... ... @@ -3376,7 +3583,7 @@ CREATE TABLE `system_sms_template` (
3376 3583 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
3377 3584 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
3378 3585 PRIMARY KEY (`id`) USING BTREE
3379   -) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信模板';
  3586 +) ENGINE = InnoDB AUTO_INCREMENT = 19 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信模板';
3380 3587  
3381 3588 -- ----------------------------
3382 3589 -- Records of system_sms_template
... ... @@ -3395,6 +3602,7 @@ INSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `cont
3395 3602 INSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (15, 1, 0, 'user-update-password', '会员用户 - 修改密码', '您的验证码{code},该验证码 5 分钟内有效,请勿泄漏于他人!', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-08-19 11:34:18', b'0');
3396 3603 INSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (16, 1, 0, 'user-reset-password', '会员用户 - 重置密码', '您的验证码{code},该验证码 5 分钟内有效,请勿泄漏于他人!', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-12-02 22:35:27', b'0');
3397 3604 INSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (17, 2, 0, 'bpm_task_timeout', '【工作流】任务审批超时', '您收到了一条超时的待办任务:{processInstanceName}-{taskName},处理链接:{detailUrl}', '[\"processInstanceName\",\"taskName\",\"detailUrl\"]', '', 'X', 4, 'DEBUG_DING_TALK', '1', '2024-08-16 21:59:15', '1', '2024-08-16 21:59:34', b'0');
  3605 +INSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (18, 1, 0, 'admin-reset-password', '后台用户 - 忘记密码', '您的验证码{code},该验证码 5 分钟内有效,请勿泄漏于他人!', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2025-03-16 14:19:34', '1', '2025-03-16 14:19:45', b'0');
3398 3606 COMMIT;
3399 3607  
3400 3608 -- ----------------------------
... ... @@ -3588,7 +3796,7 @@ CREATE TABLE `system_user_role` (
3588 3796 `deleted` bit(1) NULL DEFAULT b'0' COMMENT '是否删除',
3589 3797 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
3590 3798 PRIMARY KEY (`id`) USING BTREE
3591   -) ENGINE = InnoDB AUTO_INCREMENT = 47 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户和角色关联表';
  3799 +) ENGINE = InnoDB AUTO_INCREMENT = 48 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户和角色关联表';
3592 3800  
3593 3801 -- ----------------------------
3594 3802 -- Records of system_user_role
... ... @@ -3604,12 +3812,12 @@ INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_t
3604 3812 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (15, 111, 110, '110', '2022-02-23 13:14:38', '110', '2022-02-23 13:14:38', b'0', 121);
3605 3813 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (16, 113, 111, '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', b'0', 122);
3606 3814 INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (18, 1, 2, '1', '2022-05-12 20:39:29', '1', '2022-05-12 20:39:29', b'0', 1);
3607   -INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (20, 104, 101, '1', '2022-05-28 15:43:57', '1', '2022-05-28 15:43:57', b'0', 1);
3608 3815 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);
3609 3816 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);
3610 3817 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);
3611 3818 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);
3612 3819 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);
  3820 +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);
3613 3821 COMMIT;
3614 3822  
3615 3823 -- ----------------------------
... ... @@ -3644,10 +3852,10 @@ CREATE TABLE `system_users` (
3644 3852 -- Records of system_users
3645 3853 -- ----------------------------
3646 3854 BEGIN;
3647   -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$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1,2]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/bf2002b38950c904243be7c825d3f82e29f25a44526583c3fde2ebdff3a87f75.png', 0, '0:0:0:0:0:0:0:1', '2024-12-28 20:29:58', 'admin', '2021-01-05 17:03:47', NULL, '2024-12-28 20:29:58', b'0', 1);
  3855 +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$Q3WCEQJbSZ0zT/7ryYTb3OgtrhwIZXu4ah5RQ5/YQDQ7DpW7N7oNa', '芋道源码', '管理员', 103, '[1,2]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/bf2002b38950c904243be7c825d3f82e29f25a44526583c3fde2ebdff3a87f75.png', 0, '0:0:0:0:0:0:0:1', '2025-03-16 14:20:16', 'admin', '2021-01-05 17:03:47', NULL, '2025-03-16 14:20:16', b'0', 1);
3648 3856 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$IgUse/ibRzAZ3rngCThmtemJeoh15Ux1TQ2hIMe4iwt/K3LcFHEda', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-11-02 14:00:46', '', '2021-01-07 09:07:17', NULL, '2024-11-02 14:00:46', b'0', 1);
3649 3857 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, '', 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2024-08-11 17:48:12', b'0', 1);
3650   -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$jDFLttgfik0QqJKAbfhMa.2A9xXoZmAIxakdFJUzkX.MgBKT6ddo6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-09-17 15:05:43', '', '2021-01-21 02:13:53', NULL, '2024-09-17 15:05:43', b'0', 1);
  3858 +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, '', 0, '0:0:0:0:0:0:0:1', '2025-01-04 10:40:49', '', '2021-01-21 02:13:53', NULL, '2025-01-04 10:40:49', b'0', 1);
3651 3859 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, '', 0, '', NULL, '1', '2022-02-20 22:59:33', '1', '2022-02-27 08:26:51', b'0', 118);
3652 3860 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, '', 0, '', NULL, '1', '2022-02-20 23:00:50', '1', '2022-02-27 08:26:53', b'0', 119);
3653 3861 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 (109, 'admin109', '$2a$10$JAqvH0tEc0I7dfDVBI7zyuB4E3j.uH6daIjV53.vUS6PknFkDJkuK', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 23:11:50', '1', '2022-02-27 08:26:56', b'0', 120);
... ...
yudao-dependencies/pom.xml
... ... @@ -14,7 +14,7 @@
14 14 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
15 15  
16 16 <properties>
17   - <revision>2.4.0-SNAPSHOT</revision>
  17 + <revision>2.4.1-SNAPSHOT</revision>
18 18 <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
19 19 <!-- 统一依赖管理 -->
20 20 <spring.boot.version>3.4.1</spring.boot.version>
... ... @@ -32,8 +32,9 @@
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 36 <!-- 消息队列 -->
36   - <rocketmq-spring.version>2.3.1</rocketmq-spring.version>
  37 + <rocketmq-spring.version>2.3.2</rocketmq-spring.version>
37 38 <!-- 服务保障相关 -->
38 39 <lock4j.version>2.2.7</lock4j.version>
39 40 <!-- 监控相关 -->
... ... @@ -53,25 +54,27 @@
53 54 <mapstruct.version>1.6.3</mapstruct.version>
54 55 <hutool-5.version>5.8.35</hutool-5.version>
55 56 <hutool-6.version>6.0.0-M19</hutool-6.version>
56   - <easyexcel.verion>4.0.3</easyexcel.verion>
  57 + <easyexcel.version>4.0.3</easyexcel.version>
57 58 <velocity.version>2.4.1</velocity.version>
58 59 <fastjson.version>1.2.83</fastjson.version>
59 60 <guava.version>33.4.0-jre</guava.version>
60 61 <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
61 62 <commons-net.version>3.11.1</commons-net.version>
62 63 <jsch.version>0.1.55</jsch.version>
63   - <tika-core.version>2.9.2</tika-core.version>
  64 + <tika-core.version>3.1.0</tika-core.version>
64 65 <ip2region.version>2.7.0</ip2region.version>
65 66 <bizlog-sdk.version>3.0.6</bizlog-sdk.version>
66 67 <netty.version>4.1.116.Final</netty.version>
67 68 <mqtt.version>1.2.5</mqtt.version>
  69 + <pf4j-spring.version>0.9.0</pf4j-spring.version>
  70 + <vertx.version>4.5.13</vertx.version>
68 71 <!-- 三方云服务相关 -->
69 72 <commons-io.version>2.17.0</commons-io.version>
70 73 <commons-compress.version>1.27.1</commons-compress.version>
71 74 <aws-java-sdk-s3.version>1.12.777</aws-java-sdk-s3.version>
72 75 <justauth.version>2.0.5</justauth.version>
73 76 <jimureport.version>1.8.1</jimureport.version>
74   - <weixin-java.version>4.6.0</weixin-java.version>
  77 + <weixin-java.version>4.7.2.B</weixin-java.version>
75 78 </properties>
76 79  
77 80 <dependencyManagement>
... ... @@ -263,6 +266,12 @@
263 266 <version>${kingbase.jdbc.version}</version>
264 267 </dependency>
265 268  
  269 + <dependency>
  270 + <groupId>com.taosdata.jdbc</groupId>
  271 + <artifactId>taos-jdbcdriver</artifactId>
  272 + <version>${taos.version}</version>
  273 + </dependency>
  274 +
266 275 <!-- Job 定时任务相关 -->
267 276 <dependency>
268 277 <groupId>cn.iocoder.boot</groupId>
... ... @@ -276,7 +285,6 @@
276 285 <artifactId>yudao-spring-boot-starter-mq</artifactId>
277 286 <version>${revision}</version>
278 287 </dependency>
279   -
280 288 <dependency>
281 289 <groupId>org.apache.rocketmq</groupId>
282 290 <artifactId>rocketmq-spring-boot-starter</artifactId>
... ... @@ -471,7 +479,7 @@
471 479 <dependency>
472 480 <groupId>com.alibaba</groupId>
473 481 <artifactId>easyexcel</artifactId>
474   - <version>${easyexcel.verion}</version>
  482 + <version>${easyexcel.version}</version>
475 483 </dependency>
476 484 <dependency>
477 485 <groupId>commons-io</groupId>
... ... @@ -591,6 +599,36 @@
591 599 </exclusions>
592 600 </dependency>
593 601  
  602 + <!-- PF4J -->
  603 + <dependency>
  604 + <groupId>org.pf4j</groupId>
  605 + <artifactId>pf4j-spring</artifactId>
  606 + <version>${pf4j-spring.version}</version>
  607 + <exclusions>
  608 + <exclusion>
  609 + <groupId>org.slf4j</groupId>
  610 + <artifactId>slf4j-log4j12</artifactId>
  611 + </exclusion>
  612 + </exclusions>
  613 + </dependency>
  614 +
  615 + <!-- Vert.x -->
  616 + <dependency>
  617 + <groupId>io.vertx</groupId>
  618 + <artifactId>vertx-core</artifactId>
  619 + <version>${vertx.version}</version>
  620 + </dependency>
  621 + <dependency>
  622 + <groupId>io.vertx</groupId>
  623 + <artifactId>vertx-web</artifactId>
  624 + <version>${vertx.version}</version>
  625 + </dependency>
  626 + <dependency>
  627 + <groupId>io.vertx</groupId>
  628 + <artifactId>vertx-mqtt</artifactId>
  629 + <version>${vertx.version}</version>
  630 + </dependency>
  631 +
594 632 <!-- MQTT -->
595 633 <dependency>
596 634 <groupId>org.eclipse.paho</groupId>
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/RpcConstants.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.common.enums;
  2 +
  3 +/**
  4 + * RPC 相关的枚举
  5 + *
  6 + * 虽然放在 yudao-spring-boot-starter-rpc 会相对合适,但是每个 API 模块需要使用到,所以暂时只好放在此处
  7 + *
  8 + * @author 芋道源码
  9 + */
  10 +public class RpcConstants {
  11 +
  12 + /**
  13 + * RPC API 的前缀
  14 + */
  15 + public static final String RPC_API_PREFIX = "/rpc-api";
  16 +
  17 +}
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java
... ... @@ -7,13 +7,15 @@ import cn.hutool.core.util.ReflectUtil;
7 7 import cn.hutool.core.util.StrUtil;
8 8 import cn.hutool.http.HttpRequest;
9 9 import cn.hutool.http.HttpResponse;
  10 +import jakarta.servlet.http.HttpServletRequest;
10 11 import org.springframework.util.StringUtils;
11 12 import org.springframework.web.util.UriComponents;
12 13 import org.springframework.web.util.UriComponentsBuilder;
13 14  
14   -import jakarta.servlet.http.HttpServletRequest;
15 15 import java.net.URI;
  16 +import java.net.URLEncoder;
16 17 import java.nio.charset.Charset;
  18 +import java.nio.charset.StandardCharsets;
17 19 import java.util.Map;
18 20  
19 21 /**
... ... @@ -23,6 +25,16 @@ import java.util.Map;
23 25 */
24 26 public class HttpUtils {
25 27  
  28 + /**
  29 + * 编码 URL 参数
  30 + *
  31 + * @param value 参数
  32 + * @return 编码后的参数
  33 + */
  34 + public static String encodeUtf8(String value) {
  35 + return URLEncoder.encode(value, StandardCharsets.UTF_8);
  36 + }
  37 +
26 38 @SuppressWarnings("unchecked")
27 39 public static String replaceUrlQuery(String url, String key, String value) {
28 40 UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset());
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java
1 1 package cn.iocoder.yudao.framework.common.util.number;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.util.NumberUtil;
4 5 import cn.hutool.core.util.StrUtil;
5 6  
6 7 import java.math.BigDecimal;
  8 +import java.util.List;
7 9  
8 10 /**
9 11 * 数字的工具类,补全 {@link cn.hutool.core.util.NumberUtil} 的功能
... ... @@ -20,6 +22,18 @@ public class NumberUtils {
20 22 return StrUtil.isNotEmpty(str) ? Integer.valueOf(str) : null;
21 23 }
22 24  
  25 + public static boolean isAllNumber(List<String> values) {
  26 + if (CollUtil.isEmpty(values)) {
  27 + return false;
  28 + }
  29 + for (String value : values) {
  30 + if (!NumberUtil.isNumber(value)) {
  31 + return false;
  32 + }
  33 + }
  34 + return true;
  35 + }
  36 +
23 37 /**
24 38 * 通过经纬度获取地球上两点之间的距离
25 39 *
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java
... ... @@ -98,4 +98,8 @@ public class ServletUtils {
98 98 return JakartaServletUtil.getParamMap(request);
99 99 }
100 100  
  101 + public static Map<String, String> getHeaderMap(HttpServletRequest request) {
  102 + return JakartaServletUtil.getHeaderMap(request);
  103 + }
  104 +
101 105 }
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringExpressionUtils.java
... ... @@ -97,12 +97,26 @@ public class SpringExpressionUtils {
97 97 * @return 执行界面
98 98 */
99 99 public static Object parseExpression(String expressionString) {
  100 + return parseExpression(expressionString, null);
  101 + }
  102 +
  103 + /**
  104 + * 从 Bean 工厂,解析 EL 表达式的结果
  105 + *
  106 + * @param expressionString EL 表达式
  107 + * @param variables 变量
  108 + * @return 执行界面
  109 + */
  110 + public static Object parseExpression(String expressionString, Map<String, Object> variables) {
100 111 if (StrUtil.isBlank(expressionString)) {
101 112 return null;
102 113 }
103 114 Expression expression = EXPRESSION_PARSER.parseExpression(expressionString);
104 115 StandardEvaluationContext context = new StandardEvaluationContext();
105 116 context.setBeanResolver(new BeanFactoryResolver(SpringUtil.getApplicationContext()));
  117 + if (MapUtil.isNotEmpty(variables)) {
  118 + context.setVariables(variables);
  119 + }
106 120 return expression.getValue(context);
107 121 }
108 122  
... ...
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java
... ... @@ -45,6 +45,7 @@ public class TenantUtils {
45 45 *
46 46 * @param tenantId 租户编号
47 47 * @param callable 逻辑
  48 + * @return 结果
48 49 */
49 50 public static <V> V execute(Long tenantId, Callable<V> callable) {
50 51 Long oldTenantId = TenantContextHolder.getTenantId();
... ... @@ -79,6 +80,25 @@ public class TenantUtils {
79 80 }
80 81  
81 82 /**
  83 + * 忽略租户,执行对应的逻辑
  84 + *
  85 + * @param callable 逻辑
  86 + * @return 结果
  87 + */
  88 + public static <V> V executeIgnore(Callable<V> callable) {
  89 + Boolean oldIgnore = TenantContextHolder.isIgnore();
  90 + try {
  91 + TenantContextHolder.setIgnore(true);
  92 + // 执行逻辑
  93 + return callable.call();
  94 + } catch (Exception e) {
  95 + throw new RuntimeException(e);
  96 + } finally {
  97 + TenantContextHolder.setIgnore(oldIgnore);
  98 + }
  99 + }
  100 +
  101 + /**
82 102 * 将多租户编号,添加到 header 中
83 103 *
84 104 * @param headers HTTP 请求 headers
... ...
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
1 1 package cn.iocoder.yudao.framework.excel.core.util;
2 2  
  3 +import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
3 4 import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler;
4 5 import com.alibaba.excel.EasyExcel;
5 6 import com.alibaba.excel.converters.longconverter.LongStringConverter;
... ... @@ -8,8 +9,6 @@ import jakarta.servlet.http.HttpServletResponse;
8 9 import org.springframework.web.multipart.MultipartFile;
9 10  
10 11 import java.io.IOException;
11   -import java.net.URLEncoder;
12   -import java.nio.charset.StandardCharsets;
13 12 import java.util.List;
14 13  
15 14 /**
... ... @@ -40,7 +39,7 @@ public class ExcelUtils {
40 39 .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
41 40 .sheet(sheetName).doWrite(data);
42 41 // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了
43   - response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name()));
  42 + response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename));
44 43 response.setContentType("application/vnd.ms-excel;charset=UTF-8");
45 44 }
46 45  
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml
... ... @@ -63,6 +63,11 @@
63 63 <artifactId>opengauss-jdbc</artifactId>
64 64 <optional>true</optional>
65 65 </dependency>
  66 + <dependency>
  67 + <groupId>com.taosdata.jdbc</groupId>
  68 + <artifactId>taos-jdbcdriver</artifactId>
  69 + <optional>true</optional>
  70 + </dependency>
66 71  
67 72 <dependency>
68 73 <groupId>com.alibaba</groupId>
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
... ... @@ -92,10 +92,36 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; {
92 92  
93 93 default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,
94 94 SFunction<T, ?> field3, Object value3) {
95   - return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)
96   - .eq(field3, value3));
  95 + return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2).eq(field3, value3));
97 96 }
98 97  
  98 + /**
  99 + * 获取满足条件的第 1 条记录
  100 + *
  101 + * 目的:解决并发场景下,插入多条记录后,使用 selectOne 会报错的问题
  102 + *
  103 + * @param field 字段名
  104 + * @param value 字段值
  105 + * @return 实体
  106 + */
  107 + default T selectFirstOne(SFunction<T, ?> field, Object value) {
  108 + // 如果明确使用 MySQL 等场景,可以考虑使用 LIMIT 1 进行优化
  109 + List<T> list = selectList(new LambdaQueryWrapper<T>().eq(field, value));
  110 + return CollUtil.getFirst(list);
  111 + }
  112 +
  113 + default T selectFirstOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) {
  114 + List<T> list = selectList(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
  115 + return CollUtil.getFirst(list);
  116 + }
  117 +
  118 + default T selectFirstOne(SFunction<T,?> field1, Object value1, SFunction<T,?> field2, Object value2,
  119 + SFunction<T,?> field3, Object value3) {
  120 + List<T> list = selectList(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2).eq(field3, value3));
  121 + return CollUtil.getFirst(list);
  122 + }
  123 +
  124 +
99 125 default Long selectCount() {
100 126 return selectCount(new QueryWrapper<>());
101 127 }
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/EncryptTypeHandler.java
... ... @@ -13,7 +13,7 @@ import java.sql.ResultSet;
13 13 import java.sql.SQLException;
14 14  
15 15 /**
16   - * 字段字段的 TypeHandler 实现类,基于 {@link cn.hutool.crypto.symmetric.AES} 实现
  16 + * 字段字段的 TypeHandler 实现类,基于 {@link AES} 实现
17 17 * 可通过 jasypt.encryptor.password 配置项,设置密钥
18 18 *
19 19 * @author 芋道源码
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/LongSetTypeHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mybatis.core.type;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.util.string.StrUtils;
  5 +import org.apache.ibatis.type.JdbcType;
  6 +import org.apache.ibatis.type.MappedJdbcTypes;
  7 +import org.apache.ibatis.type.MappedTypes;
  8 +import org.apache.ibatis.type.TypeHandler;
  9 +
  10 +import java.sql.CallableStatement;
  11 +import java.sql.PreparedStatement;
  12 +import java.sql.ResultSet;
  13 +import java.sql.SQLException;
  14 +import java.util.List;
  15 +import java.util.Set;
  16 +
  17 +/**
  18 + * Set<Long> 的类型转换器实现类,对应数据库的 varchar 类型
  19 + *
  20 + * @author 芋道源码
  21 + */
  22 +@MappedJdbcTypes(JdbcType.VARCHAR)
  23 +@MappedTypes(List.class)
  24 +public class LongSetTypeHandler implements TypeHandler<Set<Long>> {
  25 +
  26 + private static final String COMMA = ",";
  27 +
  28 + @Override
  29 + public void setParameter(PreparedStatement ps, int i, Set<Long> strings, JdbcType jdbcType) throws SQLException {
  30 + // 设置占位符
  31 + ps.setString(i, CollUtil.join(strings, COMMA));
  32 + }
  33 +
  34 + @Override
  35 + public Set<Long> getResult(ResultSet rs, String columnName) throws SQLException {
  36 + String value = rs.getString(columnName);
  37 + return getResult(value);
  38 + }
  39 +
  40 + @Override
  41 + public Set<Long> getResult(ResultSet rs, int columnIndex) throws SQLException {
  42 + String value = rs.getString(columnIndex);
  43 + return getResult(value);
  44 + }
  45 +
  46 + @Override
  47 + public Set<Long> getResult(CallableStatement cs, int columnIndex) throws SQLException {
  48 + String value = cs.getString(columnIndex);
  49 + return getResult(value);
  50 + }
  51 +
  52 + private Set<Long> getResult(String value) {
  53 + if (value == null) {
  54 + return null;
  55 + }
  56 + return StrUtils.splitToLongSet(value, COMMA);
  57 + }
  58 +}
... ...
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
... ... @@ -7,6 +7,7 @@ import com.google.common.collect.HashMultimap;
7 7 import com.google.common.collect.Multimap;
8 8 import jakarta.annotation.Resource;
9 9 import jakarta.annotation.security.PermitAll;
  10 +import jakarta.servlet.DispatcherType;
10 11 import org.springframework.boot.autoconfigure.AutoConfiguration;
11 12 import org.springframework.boot.autoconfigure.AutoConfigureOrder;
12 13 import org.springframework.context.ApplicationContext;
... ... @@ -142,7 +143,9 @@ public class YudaoWebSecurityConfigurerAdapter {
142 143 // ②:每个项目的自定义规则
143 144 .authorizeHttpRequests(c -> authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(c)))
144 145 // ③:兜底规则,必须认证
145   - .authorizeHttpRequests(c -> c.anyRequest().authenticated());
  146 + .authorizeHttpRequests(c -> c
  147 + .dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll() // WebFlux 异步请求,无需认证,目的:SSE 场景
  148 + .anyRequest().authenticated());
146 149  
147 150 // 添加 Token Filter
148 151 httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java
... ... @@ -62,9 +62,9 @@ public class BannerApplicationRunner implements ApplicationRunner {
62 62 if (isNotPresent("cn.iocoder.yudao.module.ai.framework.web.config.AiWebConfiguration")) {
63 63 System.out.println("[AI 大模型 yudao-module-ai - 已禁用][参考 https://doc.iocoder.cn/ai/build/ 开启]");
64 64 }
65   - // IOT 物联网
  65 + // IoT 物联网
66 66 if (isNotPresent("cn.iocoder.yudao.module.iot.framework.web.config.IotWebConfiguration")) {
67   - System.out.println("[IOT 物联网 yudao-module-iot - 已禁用][参考 https://doc.iocoder.cn/iot/build/ 开启]");
  67 + System.out.println("[IoT 物联网 yudao-module-iot - 已禁用][参考 https://doc.iocoder.cn/iot/build/ 开启]");
68 68 }
69 69 });
70 70 }
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/ApiRequestFilter.java
... ... @@ -20,8 +20,8 @@ public abstract class ApiRequestFilter extends OncePerRequestFilter {
20 20 @Override
21 21 protected boolean shouldNotFilter(HttpServletRequest request) {
22 22 // 只过滤 API 请求的地址
23   - return !StrUtil.startWithAny(request.getRequestURI(), webProperties.getAdminApi().getPrefix(),
24   - webProperties.getAppApi().getPrefix());
  23 + String apiUri = request.getRequestURI().substring(request.getContextPath().length());
  24 + return !StrUtil.startWithAny(apiUri, webProperties.getAdminApi().getPrefix(), webProperties.getAppApi().getPrefix());
25 25 }
26 26  
27 27 }
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
1 1 package cn.iocoder.yudao.framework.web.core.handler;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.exceptions.ExceptionUtil;
4 5 import cn.hutool.core.map.MapUtil;
5 6 import cn.hutool.core.util.ObjUtil;
... ... @@ -27,6 +28,7 @@ import org.springframework.security.access.AccessDeniedException;
27 28 import org.springframework.util.Assert;
28 29 import org.springframework.validation.BindException;
29 30 import org.springframework.validation.FieldError;
  31 +import org.springframework.validation.ObjectError;
30 32 import org.springframework.web.HttpRequestMethodNotSupportedException;
31 33 import org.springframework.web.bind.MethodArgumentNotValidException;
32 34 import org.springframework.web.bind.MissingServletRequestParameterException;
... ... @@ -37,6 +39,7 @@ import org.springframework.web.servlet.NoHandlerFoundException;
37 39 import org.springframework.web.servlet.resource.NoResourceFoundException;
38 40  
39 41 import java.time.LocalDateTime;
  42 +import java.util.List;
40 43 import java.util.Map;
41 44 import java.util.Set;
42 45  
... ... @@ -135,9 +138,23 @@ public class GlobalExceptionHandler {
135 138 @ExceptionHandler(MethodArgumentNotValidException.class)
136 139 public CommonResult<?> methodArgumentNotValidExceptionExceptionHandler(MethodArgumentNotValidException ex) {
137 140 log.warn("[methodArgumentNotValidExceptionExceptionHandler]", ex);
  141 + // 获取 errorMessage
  142 + String errorMessage = null;
138 143 FieldError fieldError = ex.getBindingResult().getFieldError();
139   - assert fieldError != null; // 断言,避免告警
140   - return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数不正确:%s", fieldError.getDefaultMessage()));
  144 + if (fieldError == null) {
  145 + // 组合校验,参考自 https://t.zsxq.com/3HVTx
  146 + List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();
  147 + if (CollUtil.isNotEmpty(allErrors)) {
  148 + errorMessage = allErrors.get(0).getDefaultMessage();
  149 + }
  150 + } else {
  151 + errorMessage = fieldError.getDefaultMessage();
  152 + }
  153 + // 转换 CommonResult
  154 + if (StrUtil.isEmpty(errorMessage)) {
  155 + return CommonResult.error(BAD_REQUEST);
  156 + }
  157 + return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数不正确:%s", errorMessage));
141 158 }
142 159  
143 160 /**
... ... @@ -378,11 +395,11 @@ public class GlobalExceptionHandler {
378 395 return CommonResult.error(NOT_IMPLEMENTED.getCode(),
379 396 "[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]");
380 397 }
381   - // 9. IOT 物联网
  398 + // 9. IoT 物联网
382 399 if (message.contains("iot_")) {
383   - log.error("[IOT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]");
  400 + log.error("[IoT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]");
384 401 return CommonResult.error(NOT_IMPLEMENTED.getCode(),
385   - "[IOT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]");
  402 + "[IoT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]");
386 403 }
387 404 return null;
388 405 }
... ...
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java
... ... @@ -35,11 +35,7 @@ public enum AiChatRoleEnum {
35 35 ### 微信
36 36 除此之外不要任何解释性语句。
37 37 """),
38   -
39   - AI_KNOWLEDGE_ROLE("知识库助手", """
40   - 给你提供一些数据参考:{info},请回答我的问题。
41   - 请你跟进数据参考与工具返回结果回复用户的请求。
42   - """);
  38 + ;
43 39  
44 40 /**
45 41 * 角色名
... ...
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java
... ... @@ -12,49 +12,53 @@ public interface ErrorCodeConstants {
12 12 // ========== API 密钥 1-040-000-000 ==========
13 13 ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "API 密钥不存在");
14 14 ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, "API 密钥已禁用!");
15   - ErrorCode API_KEY_MIDJOURNEY_NOT_FOUND = new ErrorCode(1_040_000_900, "Midjourney 模型不存在");
16   - ErrorCode API_KEY_SUNO_NOT_FOUND = new ErrorCode(1_040_000_901, "Suno 模型不存在");
17   - ErrorCode API_KEY_IMAGE_NODE_FOUND = new ErrorCode(1_040_000_902, "平台({}) 图片模型未配置");
18 15  
19   - // ========== API 聊天模型 1-040-001-000 ==========
20   - ErrorCode CHAT_MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!");
21   - ErrorCode CHAT_MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!");
22   - ErrorCode CHAT_MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认聊天模型");
  16 + // ========== API 模型 1-040-001-000 ==========
  17 + ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!");
  18 + ErrorCode MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!");
  19 + ErrorCode MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认模型");
  20 + ErrorCode MODEL_USE_TYPE_ERROR = new ErrorCode(1_040_001_003, "操作失败,该模型的模型类型不正确");
23 21  
24   - // ========== API 聊天模型 1-040-002-000 ==========
  22 + // ========== API 聊天角色 1-040-002-000 ==========
25 23 ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "聊天角色不存在");
26 24 ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "聊天角色({})已禁用!");
27 25  
28 26 // ========== API 聊天会话 1-040-003-000 ==========
29   -
30 27 ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "对话不存在!");
31 28 ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "操作失败,该聊天模型的配置不完整");
32 29  
33 30 // ========== API 聊天消息 1-040-004-000 ==========
34   -
35 31 ErrorCode CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "消息不存在!");
36 32 ErrorCode CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "对话生成异常!");
37 33  
38 34 // ========== API 绘画 1-040-005-000 ==========
39   -
40   - ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_022_005_000, "图片不存在!");
41   - ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_022_005_001, "Midjourney 提交失败!原因:{}");
42   - ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_022_005_002, "Midjourney 按钮 customId 不存在! {}");
43   - ErrorCode IMAGE_FAIL = new ErrorCode(1_022_005_002, "图片绘画失败! {}");
  35 + ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_040_005_000, "图片不存在!");
  36 + ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_040_005_001, "Midjourney 提交失败!原因:{}");
  37 + ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_040_005_002, "Midjourney 按钮 customId 不存在! {}");
44 38  
45 39 // ========== API 音乐 1-040-006-000 ==========
46   - ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_022_006_000, "音乐不存在!");
  40 + ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_040_006_000, "音乐不存在!");
47 41  
48   - // ========== API 写作 1-022-007-000 ==========
49   - ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_022_007_000, "作文不存在!");
50   - ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_022_07_001, "写作生成异常!");
  42 + // ========== API 写作 1-040-007-000 ==========
  43 + ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_040_007_000, "作文不存在!");
  44 + ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_040_07_001, "写作生成异常!");
51 45  
52 46 // ========== API 思维导图 1-040-008-000 ==========
53 47 ErrorCode MIND_MAP_NOT_EXISTS = new ErrorCode(1_040_008_000, "思维导图不存在!");
54 48  
55   - // ========== API 知识库 1-022-008-000 ==========
56   - ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_022_008_000, "知识库不存在!");
57   - ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_022_008_001, "文档不存在!");
58   - ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_022_008_002, "段落不存在!");
  49 + // ========== API 知识库 1-040-009-000 ==========
  50 + ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_040_009_000, "知识库不存在!");
  51 +
  52 + ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_040_009_101, "文档不存在!");
  53 + ErrorCode KNOWLEDGE_DOCUMENT_FILE_EMPTY = new ErrorCode(1_040_009_102, "文档内容为空!");
  54 + ErrorCode KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL = new ErrorCode(1_040_009_102, "文件下载失败!");
  55 + ErrorCode KNOWLEDGE_DOCUMENT_FILE_READ_FAIL = new ErrorCode(1_040_009_102, "文档加载失败!");
  56 +
  57 + ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_040_009_202, "段落不存在!");
  58 + ErrorCode KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG = new ErrorCode(1_040_009_203, "内容 Token 数为 {},超过最大限制 {}");
  59 +
  60 + // ========== AI 工具 1-040-010-000 ==========
  61 + ErrorCode TOOL_NOT_EXISTS = new ErrorCode(1_040_010_000, "工具不存在");
  62 + ErrorCode TOOL_NAME_NOT_EXISTS = new ErrorCode(1_040_010_001, "工具({})找不到 Bean");
59 63  
60 64 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http
... ... @@ -2,7 +2,7 @@
2 2 POST {{baseUrl}}/ai/chat/message/send
3 3 Content-Type: application/json
4 4 Authorization: {{token}}
5   -tenant-id: {{adminTenentId}}
  5 +tenant-id: {{adminTenantId}}
6 6  
7 7 {
8 8 "conversationId": "1781604279872581724",
... ... @@ -13,7 +13,7 @@ tenant-id: {{adminTenentId}}
13 13 POST {{baseUrl}}/ai/chat/message/send-stream
14 14 Content-Type: application/json
15 15 Authorization: {{token}}
16   -tenant-id: {{adminTenentId}}
  16 +tenant-id: {{adminTenantId}}
17 17  
18 18 {
19 19 "conversationId": "1781604279872581724",
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java
... ... @@ -12,15 +12,18 @@ import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessage
12 12 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
13 13 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
14 14 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
  15 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
  16 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
15 17 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
16 18 import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService;
17 19 import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService;
  20 +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
  21 +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
18 22 import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
19 23 import io.swagger.v3.oas.annotations.Operation;
20 24 import io.swagger.v3.oas.annotations.Parameter;
21 25 import io.swagger.v3.oas.annotations.tags.Tag;
22 26 import jakarta.annotation.Resource;
23   -import jakarta.annotation.security.PermitAll;
24 27 import jakarta.validation.Valid;
25 28 import lombok.extern.slf4j.Slf4j;
26 29 import org.springframework.http.MediaType;
... ... @@ -33,7 +36,7 @@ import java.util.List;
33 36 import java.util.Map;
34 37  
35 38 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
36   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  39 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
37 40 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
38 41  
39 42 @Tag(name = "管理后台 - 聊天消息")
... ... @@ -48,6 +51,10 @@ public class AiChatMessageController {
48 51 private AiChatConversationService chatConversationService;
49 52 @Resource
50 53 private AiChatRoleService chatRoleService;
  54 + @Resource
  55 + private AiKnowledgeSegmentService knowledgeSegmentService;
  56 + @Resource
  57 + private AiKnowledgeDocumentService knowledgeDocumentService;
51 58  
52 59 @Operation(summary = "发送消息(段式)", description = "一次性返回,响应较慢")
53 60 @PostMapping("/send")
... ... @@ -57,7 +64,6 @@ public class AiChatMessageController {
57 64  
58 65 @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快")
59 66 @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
60   - @PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题
61 67 public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {
62 68 return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId());
63 69 }
... ... @@ -71,8 +77,38 @@ public class AiChatMessageController {
71 77 if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
72 78 return success(Collections.emptyList());
73 79 }
  80 + // 1. 获取消息列表
74 81 List<AiChatMessageDO> messageList = chatMessageService.getChatMessageListByConversationId(conversationId);
75   - return success(BeanUtils.toBean(messageList, AiChatMessageRespVO.class));
  82 + if (CollUtil.isEmpty(messageList)) {
  83 + return success(Collections.emptyList());
  84 + }
  85 +
  86 + // 2. 拼接数据,主要是知识库段落信息
  87 + Map<Long, AiKnowledgeSegmentDO> segmentMap = knowledgeSegmentService.getKnowledgeSegmentMap(convertListByFlatMap(messageList,
  88 + message -> CollUtil.isEmpty(message.getSegmentIds()) ? null : message.getSegmentIds().stream()));
  89 + Map<Long, AiKnowledgeDocumentDO> documentMap = knowledgeDocumentService.getKnowledgeDocumentMap(
  90 + convertList(segmentMap.values(), AiKnowledgeSegmentDO::getDocumentId));
  91 + List<AiChatMessageRespVO> messageVOList = BeanUtils.toBean(messageList, AiChatMessageRespVO.class);
  92 + for (int i = 0; i < messageList.size(); i++) {
  93 + AiChatMessageDO message = messageList.get(i);
  94 + if (CollUtil.isEmpty(message.getSegmentIds())) {
  95 + continue;
  96 + }
  97 + // 设置知识库段落信息
  98 + messageVOList.get(i).setSegments(convertList(message.getSegmentIds(), segmentId -> {
  99 + AiKnowledgeSegmentDO segment = segmentMap.get(segmentId);
  100 + if (segment == null) {
  101 + return null;
  102 + }
  103 + AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());
  104 + if (document == null) {
  105 + return null;
  106 + }
  107 + return new AiChatMessageRespVO.KnowledgeSegment().setId(segment.getId()).setContent(segment.getContent())
  108 + .setDocumentId(segment.getDocumentId()).setDocumentName(document.getName());
  109 + }));
  110 + }
  111 + return success(messageVOList);
76 112 }
77 113  
78 114 @Operation(summary = "删除消息")
... ... @@ -105,7 +141,8 @@ public class AiChatMessageController {
105 141 Map<Long, AiChatRoleDO> roleMap = chatRoleService.getChatRoleMap(
106 142 convertSet(pageResult.getList(), AiChatMessageDO::getRoleId));
107 143 return success(BeanUtils.toBean(pageResult, AiChatMessageRespVO.class,
108   - respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(), role -> respVO.setRoleName(role.getName()))));
  144 + respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(),
  145 + role -> respVO.setRoleName(role.getName()))));
109 146 }
110 147  
111 148 @Operation(summary = "管理员删除消息")
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;
2 2  
3   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  3 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
4 4 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
5 5 import com.fhs.core.trans.anno.Trans;
6 6 import com.fhs.core.trans.constant.TransType;
... ... @@ -31,7 +31,7 @@ public class AiChatConversationRespVO implements VO {
31 31 private Long roleId;
32 32  
33 33 @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
34   - @Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = "name", ref = "modelName")
  34 + @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = "name", ref = "modelName")
35 35 private Long modelId;
36 36  
37 37 @Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922")
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java
... ... @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
5 5  
6 6 import java.time.LocalDateTime;
  7 +import java.util.List;
7 8  
8 9 @Schema(description = "管理后台 - AI 聊天消息 Response VO")
9 10 @Data
... ... @@ -39,6 +40,12 @@ public class AiChatMessageRespVO {
39 40 @Schema(description = "是否携带上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
40 41 private Boolean useContext;
41 42  
  43 + @Schema(description = "知识库段落编号数组", example = "[1,2,3]")
  44 + private List<Long> segmentIds;
  45 +
  46 + @Schema(description = "知识库段落数组")
  47 + private List<KnowledgeSegment> segments;
  48 +
42 49 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51")
43 50 private LocalDateTime createTime;
44 51  
... ... @@ -47,4 +54,22 @@ public class AiChatMessageRespVO {
47 54 @Schema(description = "角色名字", example = "小黄")
48 55 private String roleName;
49 56  
  57 + @Schema(description = "知识库段落", example = "Java 开发手册")
  58 + @Data
  59 + public static class KnowledgeSegment {
  60 +
  61 + @Schema(description = "段落编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  62 + private Long id;
  63 +
  64 + @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
  65 + private String content;
  66 +
  67 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
  68 + private Long documentId;
  69 +
  70 + @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品使用手册")
  71 + private String documentName;
  72 +
  73 + }
  74 +
50 75 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java
... ... @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
5 5  
6 6 import java.time.LocalDateTime;
  7 +import java.util.List;
7 8  
8 9 @Schema(description = "管理后台 - AI 聊天消息发送 Response VO")
9 10 @Data
... ... @@ -28,6 +29,12 @@ public class AiChatMessageSendRespVO {
28 29 @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊")
29 30 private String content;
30 31  
  32 + @Schema(description = "知识库段落编号数组", example = "[1,2,3]")
  33 + private List<Long> segmentIds;
  34 +
  35 + @Schema(description = "知识库段落数组")
  36 + private List<AiChatMessageRespVO.KnowledgeSegment> segments;
  37 +
31 38 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
32 39 private LocalDateTime createTime;
33 40  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java
... ... @@ -14,18 +14,15 @@ import java.util.Map;
14 14 @Data
15 15 public class AiImageDrawReqVO {
16 16  
17   - @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
18   - private String platform; // 参见 AiPlatformEnum 枚举
  17 + @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  18 + @NotNull(message = "模型编号不能为空")
  19 + private Long modelId;
19 20  
20 21 @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城")
21 22 @NotEmpty(message = "提示词不能为空")
22 23 @Size(max = 1200, message = "提示词最大 1200")
23 24 private String prompt;
24 25  
25   - @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "stable-diffusion-v1-6")
26   - @NotEmpty(message = "模型不能为空")
27   - private String model;
28   -
29 26 /**
30 27 * 1. dall-e-2 模型:256x256、512x512、1024x1024
31 28 * 2. dall-e-3 模型:1024x1024, 1792x1024, 或 1024x1792
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java
... ... @@ -13,9 +13,9 @@ public class AiMidjourneyImagineReqVO {
13 13 @NotEmpty(message = "提示词不能为空!")
14 14 private String prompt;
15 15  
16   - @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "midjourney")
17   - @NotEmpty(message = "模型不能为空")
18   - private String model; // 参考 MidjourneyApi.ModelEnum
  16 + @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  17 + @NotNull(message = "模型编号不能为空")
  18 + private Long modelId;
19 19  
20 20 @Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
21 21 @NotNull(message = "图片宽度不能为空")
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.http 0 → 100644
  1 +### 创建知识库
  2 +POST {{baseUrl}}/ai/knowledge/create
  3 +Content-Type: application/json
  4 +Authorization: {{token}}
  5 +tenant-id: {{adminTenantId}}
  6 +
  7 +{
  8 + "name": "测试标题",
  9 + "description": "测试描述",
  10 + "embeddingModelId": 30,
  11 + "topK": 3,
  12 + "similarityThreshold": 0.5,
  13 + "status": 0
  14 +}
  15 +
  16 +### 更新知识库
  17 +PUT {{baseUrl}}/ai/knowledge/update
  18 +Content-Type: application/json
  19 +Authorization: {{token}}
  20 +tenant-id: {{adminTenantId}}
  21 +
  22 +{
  23 + "id": 1,
  24 + "name": "测试标题(更新)",
  25 + "description": "测试描述",
  26 + "embeddingModelId": 30,
  27 + "topK": 5,
  28 + "similarityThreshold": 0.6,
  29 + "status": 0
  30 +}
  31 +
  32 +### 获取知识库分页
  33 +GET {{baseUrl}}/ai/knowledge/page?pageNo=1&pageSize=10
  34 +Authorization: {{token}}
  35 +tenant-id: {{adminTenantId}}
0 36 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO;
7 7 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
8 8 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeRespVO;
9   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
10 10 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
11 11 import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
12 12 import io.swagger.v3.oas.annotations.Operation;
  13 +import io.swagger.v3.oas.annotations.Parameter;
13 14 import io.swagger.v3.oas.annotations.tags.Tag;
14 15 import jakarta.annotation.Resource;
15 16 import jakarta.validation.Valid;
  17 +import org.springframework.security.access.prepost.PreAuthorize;
16 18 import org.springframework.validation.annotation.Validated;
17 19 import org.springframework.web.bind.annotation.*;
18 20  
  21 +import java.util.List;
  22 +
19 23 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
20   -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
21 25  
22 26 @Tag(name = "管理后台 - AI 知识库")
23 27 @RestController
... ... @@ -30,21 +34,42 @@ public class AiKnowledgeController {
30 34  
31 35 @GetMapping("/page")
32 36 @Operation(summary = "获取知识库分页")
  37 + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
33 38 public CommonResult<PageResult<AiKnowledgeRespVO>> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) {
34   - PageResult<AiKnowledgeDO> pageResult = knowledgeService.getKnowledgePage(getLoginUserId(), pageReqVO);
  39 + PageResult<AiKnowledgeDO> pageResult = knowledgeService.getKnowledgePage(pageReqVO);
35 40 return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class));
36 41 }
37 42  
  43 + @GetMapping("/get")
  44 + @Operation(summary = "获得知识库")
  45 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  46 + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
  47 + public CommonResult<AiKnowledgeRespVO> getKnowledge(@RequestParam("id") Long id) {
  48 + AiKnowledgeDO knowledge = knowledgeService.getKnowledge(id);
  49 + return success(BeanUtils.toBean(knowledge, AiKnowledgeRespVO.class));
  50 + }
  51 +
38 52 @PostMapping("/create")
39 53 @Operation(summary = "创建知识库")
40   - public CommonResult<Long> createKnowledge(@RequestBody @Valid AiKnowledgeCreateReqVO createReqVO) {
41   - return success(knowledgeService.createKnowledge(createReqVO, getLoginUserId()));
  54 + @PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
  55 + public CommonResult<Long> createKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO createReqVO) {
  56 + return success(knowledgeService.createKnowledge(createReqVO));
42 57 }
43 58  
44 59 @PutMapping("/update")
45 60 @Operation(summary = "更新知识库")
46   - public CommonResult<Boolean> updateKnowledge(@RequestBody @Valid AiKnowledgeUpdateReqVO updateReqVO) {
47   - knowledgeService.updateKnowledge(updateReqVO, getLoginUserId());
  61 + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
  62 + public CommonResult<Boolean> updateKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO updateReqVO) {
  63 + knowledgeService.updateKnowledge(updateReqVO);
48 64 return success(true);
49 65 }
  66 +
  67 + @GetMapping("/simple-list")
  68 + @Operation(summary = "获得知识库的精简列表")
  69 + public CommonResult<List<AiKnowledgeRespVO>> getKnowledgeSimpleList() {
  70 + List<AiKnowledgeDO> list = knowledgeService.getKnowledgeSimpleListByStatus(CommonStatusEnum.ENABLE.getStatus());
  71 + return success(convertList(list, knowledge -> new AiKnowledgeRespVO()
  72 + .setId(knowledge.getId()).setName(knowledge.getName())));
  73 + }
  74 +
50 75 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http 0 → 100644
  1 +### 创建知识文档
  2 +POST {{baseUrl}}/ai/knowledge/document/create
  3 +Content-Type: application/json
  4 +Authorization: Bearer {{token}}
  5 +tenant-id: {{adminTenantId}}
  6 +
  7 +{
  8 + "knowledgeId": 2,
  9 + "name": "测试文档",
  10 + "url": "https://static.iocoder.cn/README.md",
  11 + "segmentMaxTokens": 800
  12 +}
  13 +
  14 +### 批量创建知识文档
  15 +POST {{baseUrl}}/ai/knowledge/document/create-list
  16 +Content-Type: application/json
  17 +Authorization: Bearer {{token}}
  18 +tenant-id: {{adminTenantId}}
  19 +
  20 +{
  21 + "knowledgeId": 1,
  22 + "list": [
  23 + {
  24 + "name": "测试文档1",
  25 + "url": "https://static.iocoder.cn/README.md",
  26 + "segmentMaxTokens": 800
  27 + },
  28 + {
  29 + "name": "测试文档2",
  30 + "url": "https://static.iocoder.cn/README_yudao.md",
  31 + "segmentMaxTokens": 400
  32 + }
  33 + ]
  34 +}
  35 +
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java
... ... @@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
7   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentRespVO;
8   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.*;
9 7 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
10 8 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
11 9 import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
... ... @@ -13,9 +11,12 @@ import io.swagger.v3.oas.annotations.Operation;
13 11 import io.swagger.v3.oas.annotations.tags.Tag;
14 12 import jakarta.annotation.Resource;
15 13 import jakarta.validation.Valid;
  14 +import org.springframework.security.access.prepost.PreAuthorize;
16 15 import org.springframework.validation.annotation.Validated;
17 16 import org.springframework.web.bind.annotation.*;
18 17  
  18 +import java.util.List;
  19 +
19 20 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
20 21  
21 22 @Tag(name = "管理后台 - AI 知识库文档")
... ... @@ -27,25 +28,63 @@ public class AiKnowledgeDocumentController {
27 28 @Resource
28 29 private AiKnowledgeDocumentService documentService;
29 30  
30   - @PostMapping("/create")
31   - @Operation(summary = "新建文档")
32   - public CommonResult<Long> createKnowledgeDocument(@Valid AiKnowledgeDocumentCreateReqVO reqVO) {
33   - Long knowledgeDocumentId = documentService.createKnowledgeDocument(reqVO);
34   - return success(knowledgeDocumentId);
35   - }
36   -
37 31 @GetMapping("/page")
38 32 @Operation(summary = "获取文档分页")
39   - public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPage(@Valid AiKnowledgeDocumentPageReqVO pageReqVO) {
  33 + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
  34 + public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPage(
  35 + @Valid AiKnowledgeDocumentPageReqVO pageReqVO) {
40 36 PageResult<AiKnowledgeDocumentDO> pageResult = documentService.getKnowledgeDocumentPage(pageReqVO);
41 37 return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class));
42 38 }
43 39  
  40 + @GetMapping("/get")
  41 + @Operation(summary = "获取文档详情")
  42 + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
  43 + public CommonResult<AiKnowledgeDocumentRespVO> getKnowledgeDocument(@RequestParam("id") Long id) {
  44 + AiKnowledgeDocumentDO document = documentService.getKnowledgeDocument(id);
  45 + return success(BeanUtils.toBean(document, AiKnowledgeDocumentRespVO.class));
  46 + }
  47 +
  48 + @PostMapping("/create")
  49 + @Operation(summary = "新建文档(单个)")
  50 + @PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
  51 + public CommonResult<Long> createKnowledgeDocument(@RequestBody @Valid AiKnowledgeDocumentCreateReqVO reqVO) {
  52 + Long id = documentService.createKnowledgeDocument(reqVO);
  53 + return success(id);
  54 + }
  55 +
  56 + @PostMapping("/create-list")
  57 + @Operation(summary = "新建文档(多个)")
  58 + @PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
  59 + public CommonResult<List<Long>> createKnowledgeDocumentList(
  60 + @RequestBody @Valid AiKnowledgeDocumentCreateListReqVO reqVO) {
  61 + List<Long> ids = documentService.createKnowledgeDocumentList(reqVO);
  62 + return success(ids);
  63 + }
  64 +
44 65 @PutMapping("/update")
45 66 @Operation(summary = "更新文档")
  67 + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
46 68 public CommonResult<Boolean> updateKnowledgeDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) {
47 69 documentService.updateKnowledgeDocument(reqVO);
48 70 return success(true);
49 71 }
50 72  
  73 + @PutMapping("/update-status")
  74 + @Operation(summary = "更新文档状态")
  75 + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
  76 + public CommonResult<Boolean> updateKnowledgeDocumentStatus(
  77 + @Valid @RequestBody AiKnowledgeDocumentUpdateStatusReqVO reqVO) {
  78 + documentService.updateKnowledgeDocumentStatus(reqVO);
  79 + return success(true);
  80 + }
  81 +
  82 + @DeleteMapping("/delete")
  83 + @Operation(summary = "删除文档")
  84 + @PreAuthorize("@ss.hasPermission('ai:knowledge:delete')")
  85 + public CommonResult<Boolean> deleteKnowledgeDocument(@RequestParam("id") Long id) {
  86 + documentService.deleteKnowledgeDocument(id);
  87 + return success(true);
  88 + }
  89 +
51 90 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http 0 → 100644
  1 +### 切片内容
  2 +GET {{baseUrl}}/ai/knowledge/segment/split?url=https://static.iocoder.cn/README_yudao.md&segmentMaxTokens=800
  3 +Content-Type: application/json
  4 +Authorization: Bearer {{token}}
  5 +tenant-id: {{adminTenantId}}
  6 +
  7 +### 搜索段落内容
  8 +GET {{baseUrl}}/ai/knowledge/segment/search?knowledgeId=2&content=如何使用这个产品&topK=5&similarityThreshold=0.1
  9 +Content-Type: application/json
  10 +Authorization: Bearer {{token}}
  11 +tenant-id: {{adminTenantId}}
  12 +
  13 +### 获取文档处理列表
  14 +GET {{baseUrl}}/ai/knowledge/segment/get-process-list?documentIds=1,2,3
  15 +Content-Type: application/json
  16 +Authorization: Bearer {{token}}
  17 +tenant-id: {{adminTenantId}}
0 18 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
5 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
7   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentRespVO;
8   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO;
9   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.*;
  9 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
10 10 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
  11 +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
11 12 import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
  13 +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
  14 +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
12 15 import io.swagger.v3.oas.annotations.Operation;
  16 +import io.swagger.v3.oas.annotations.Parameter;
  17 +import io.swagger.v3.oas.annotations.Parameters;
13 18 import io.swagger.v3.oas.annotations.tags.Tag;
14 19 import jakarta.annotation.Resource;
15 20 import jakarta.validation.Valid;
  21 +import org.hibernate.validator.constraints.URL;
  22 +import org.springframework.security.access.prepost.PreAuthorize;
16 23 import org.springframework.validation.annotation.Validated;
17 24 import org.springframework.web.bind.annotation.*;
18 25  
  26 +import java.util.Collections;
  27 +import java.util.List;
  28 +import java.util.Map;
  29 +
19 30 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  31 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
20 32  
21 33 @Tag(name = "管理后台 - AI 知识库段落")
22 34 @RestController
... ... @@ -26,26 +38,93 @@ public class AiKnowledgeSegmentController {
26 38  
27 39 @Resource
28 40 private AiKnowledgeSegmentService segmentService;
  41 + @Resource
  42 + private AiKnowledgeDocumentService documentService;
  43 +
  44 + @GetMapping("/get")
  45 + @Operation(summary = "获取段落详情")
  46 + @Parameter(name = "id", description = "段落编号", required = true, example = "1024")
  47 + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
  48 + public CommonResult<AiKnowledgeSegmentRespVO> getKnowledgeSegment(@RequestParam("id") Long id) {
  49 + AiKnowledgeSegmentDO segment = segmentService.getKnowledgeSegment(id);
  50 + return success(BeanUtils.toBean(segment, AiKnowledgeSegmentRespVO.class));
  51 + }
29 52  
30 53 @GetMapping("/page")
31 54 @Operation(summary = "获取段落分页")
32   - public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPage(@Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
  55 + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
  56 + public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPage(
  57 + @Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
33 58 PageResult<AiKnowledgeSegmentDO> pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO);
34 59 return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class));
35 60 }
36 61  
  62 + @PostMapping("/create")
  63 + @Operation(summary = "创建段落")
  64 + @PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
  65 + public CommonResult<Long> createKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO createReqVO) {
  66 + return success(segmentService.createKnowledgeSegment(createReqVO));
  67 + }
  68 +
37 69 @PutMapping("/update")
38 70 @Operation(summary = "更新段落内容")
39   - public CommonResult<Boolean> updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentUpdateReqVO reqVO) {
  71 + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
  72 + public CommonResult<Boolean> updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO reqVO) {
40 73 segmentService.updateKnowledgeSegment(reqVO);
41 74 return success(true);
42 75 }
43 76  
44 77 @PutMapping("/update-status")
45 78 @Operation(summary = "启禁用段落内容")
46   - public CommonResult<Boolean> updateKnowledgeSegmentStatus(@Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
  79 + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
  80 + public CommonResult<Boolean> updateKnowledgeSegmentStatus(
  81 + @Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
47 82 segmentService.updateKnowledgeSegmentStatus(reqVO);
48 83 return success(true);
49 84 }
50 85  
  86 + @GetMapping("/split")
  87 + @Operation(summary = "切片内容")
  88 + @Parameters({
  89 + @Parameter(name = "url", description = "文档 URL", required = true),
  90 + @Parameter(name = "segmentMaxTokens", description = "分段的最大 Token 数", required = true)
  91 + })
  92 + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
  93 + public CommonResult<List<AiKnowledgeSegmentRespVO>> splitContent(
  94 + @RequestParam("url") @URL String url,
  95 + @RequestParam(value = "segmentMaxTokens") Integer segmentMaxTokens) {
  96 + List<AiKnowledgeSegmentDO> segments = segmentService.splitContent(url, segmentMaxTokens);
  97 + return success(BeanUtils.toBean(segments, AiKnowledgeSegmentRespVO.class));
  98 + }
  99 +
  100 + @GetMapping("/get-process-list")
  101 + @Operation(summary = "获取文档处理列表")
  102 + @Parameter(name = "documentIds", description = "文档编号列表", required = true, example = "1,2,3")
  103 + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
  104 + public CommonResult<List<AiKnowledgeSegmentProcessRespVO>> getKnowledgeSegmentProcessList(
  105 + @RequestParam("documentIds") List<Long> documentIds) {
  106 + List<AiKnowledgeSegmentProcessRespVO> list = segmentService.getKnowledgeSegmentProcessList(documentIds);
  107 + return success(list);
  108 + }
  109 +
  110 + @GetMapping("/search")
  111 + @Operation(summary = "搜索段落内容")
  112 + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
  113 + public CommonResult<List<AiKnowledgeSegmentSearchRespVO>> searchKnowledgeSegment(
  114 + @Valid AiKnowledgeSegmentSearchReqVO reqVO) {
  115 + // 1. 搜索段落
  116 + List<AiKnowledgeSegmentSearchRespBO> segments = segmentService
  117 + .searchKnowledgeSegment(BeanUtils.toBean(reqVO, AiKnowledgeSegmentSearchReqBO.class));
  118 + if (CollUtil.isEmpty(segments)) {
  119 + return success(Collections.emptyList());
  120 + }
  121 +
  122 + // 2. 拼接 VO
  123 + Map<Long, AiKnowledgeDocumentDO> documentMap = documentService.getKnowledgeDocumentMap(convertSet(
  124 + segments, AiKnowledgeSegmentSearchRespBO::getDocumentId));
  125 + return success(BeanUtils.toBean(segments, AiKnowledgeSegmentSearchRespVO.class,
  126 + segment -> MapUtils.findAndThen(documentMap, segment.getDocumentId(),
  127 + document -> segment.setDocumentName(document.getName()))));
  128 + }
  129 +
51 130 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotBlank;
  5 +import jakarta.validation.constraints.NotEmpty;
  6 +import jakarta.validation.constraints.NotNull;
  7 +import lombok.Data;
  8 +import org.hibernate.validator.constraints.URL;
  9 +
  10 +import java.util.List;
  11 +
  12 +@Schema(description = "管理后台 - AI 知识库文档批量创建 Request VO")
  13 +@Data
  14 +public class AiKnowledgeDocumentCreateListReqVO {
  15 +
  16 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204")
  17 + @NotNull(message = "知识库编号不能为空")
  18 + private Long knowledgeId;
  19 +
  20 + @Schema(description = "分段的最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
  21 + @NotNull(message = "分段的最大 Token 数不能为空")
  22 + private Integer segmentMaxTokens;
  23 +
  24 + @Schema(description = "文档列表", requiredMode = Schema.RequiredMode.REQUIRED)
  25 + @NotEmpty(message = "文档列表不能为空")
  26 + private List<Document> list;
  27 +
  28 + @Schema(description = "文档")
  29 + @Data
  30 + public static class Document {
  31 +
  32 + @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "三方登陆")
  33 + @NotBlank(message = "文档名称不能为空")
  34 + private String name;
  35 +
  36 + @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
  37 + @URL(message = "文档 URL 格式不正确")
  38 + private String url;
  39 +
  40 + }
  41 +
  42 +}
0 43 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java
... ... @@ -8,6 +8,9 @@ import lombok.Data;
8 8 @Data
9 9 public class AiKnowledgeDocumentPageReqVO extends PageParam {
10 10  
  11 + @Schema(description = "知识库编号", example = "1")
  12 + private Long knowledgeId;
  13 +
11 14 @Schema(description = "文档名称", example = "Java 开发手册")
12 15 private String name;
13 16  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
2 2  
3   -import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 3 import io.swagger.v3.oas.annotations.media.Schema;
5 4 import lombok.Data;
6 5  
7   -@Schema(description = "管理后台 - AI 知识库-文档 Response VO")
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - AI 知识库文档 Response VO")
8 9 @Data
9   -public class AiKnowledgeDocumentRespVO extends PageParam {
  10 +public class AiKnowledgeDocumentRespVO {
10 11  
11   - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
  12 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
12 13 private Long id;
13 14  
14 15 @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
15 16 private Long knowledgeId;
16 17  
17   - @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
  18 + @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
18 19 private String name;
19 20  
20   - @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....")
  21 + @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
  22 + private String url;
  23 +
  24 + @Schema(description = "文档内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....")
21 25 private String content;
22 26  
23   - @Schema(description = "文档 url", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
24   - private String url;
  27 + @Schema(description = "文档内容长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  28 + private Integer contentLength;
25 29  
26   - @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  30 + @Schema(description = "文档 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
27 31 private Integer tokens;
28 32  
29   - @Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008")
30   - private Integer wordCount;
  33 + @Schema(description = "分片最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
  34 + private Integer segmentMaxTokens;
31 35  
32   - @Schema(description = "切片状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
33   - private Integer sliceStatus;
  36 + @Schema(description = "召回次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  37 + private Integer retrievalCount;
34 38  
35   - @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  39 + @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
36 40 private Integer status;
37 41  
  42 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  43 + private LocalDateTime createTime;
  44 +
38 45 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
2 2  
3   -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4   -import cn.iocoder.yudao.framework.common.validation.InEnum;
5 3 import io.swagger.v3.oas.annotations.media.Schema;
6 4 import jakarta.validation.constraints.NotNull;
7 5 import lombok.Data;
8 6  
9   -
10   -@Schema(description = "管理后台 - AI 更新 知识库-文档 Request VO")
  7 +@Schema(description = "管理后台 - AI 知识库文档更新 Request VO")
11 8 @Data
12 9 public class AiKnowledgeDocumentUpdateReqVO {
13 10  
14   -
15 11 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
16 12 @NotNull(message = "编号不能为空")
17 13 private Long id;
18 14  
19   - @Schema(description = "是否启用", example = "1")
20   - @InEnum(CommonStatusEnum.class)
21   - private Integer status;
22   -
23 15 @Schema(description = "名称", example = "Java 开发手册")
24 16 private String name;
25 17  
  18 + @Schema(description = "分片最大 Token 数", example = "1000")
  19 + private Integer segmentMaxTokens;
  20 +
26 21 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import jakarta.validation.constraints.NotNull;
  7 +import lombok.Data;
  8 +
  9 +@Schema(description = "管理后台 - AI 知识库文档更新状态 Request VO")
  10 +@Data
  11 +public class AiKnowledgeDocumentUpdateStatusReqVO {
  12 +
  13 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
  14 + @NotNull(message = "编号不能为空")
  15 + private Long id;
  16 +
  17 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  18 + @NotNull(message = "状态不能为空")
  19 + @InEnum(CommonStatusEnum.class)
  20 + private Integer status;
  21 +
  22 +}
0 23 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
... ... @@ -23,24 +23,8 @@ public class AiKnowledgeDocumentCreateReqVO {
23 23 @URL(message = "文档 URL 格式不正确")
24 24 private String url;
25 25  
26   - @Schema(description = "每个段落的目标 token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
27   - @NotNull(message = "每个段落的目标 token 数不能为空")
28   - private Integer defaultSegmentTokens;
29   -
30   - @Schema(description = "每个段落的最小字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "350")
31   - @NotNull(message = "每个段落的最小字符数不能为空")
32   - private Integer minSegmentWordCount;
33   -
34   - @Schema(description = "丢弃阈值:低于此阈值的段落会被丢弃", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
35   - @NotNull(message = "丢弃阈值不能为空")
36   - private Integer minChunkLengthToEmbed;
37   -
38   - @Schema(description = "最大段落数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
39   - @NotNull(message = "最大段落数不能为空")
40   - private Integer maxNumSegments;
41   -
42   - @Schema(description = "分块是否保留分隔符", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
43   - @NotNull(message = "分块是否保留分隔符不能为空")
44   - private Boolean keepSeparator;
  26 + @Schema(description = "分段的最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
  27 + @NotNull(message = "分段的最大 Token 数不能为空")
  28 + private Integer segmentMaxTokens;
45 29  
46 30 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageParam;
  5 +import cn.iocoder.yudao.framework.common.validation.InEnum;
4 6 import io.swagger.v3.oas.annotations.media.Schema;
5 7 import lombok.Data;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
6 13  
7 14 @Schema(description = "管理后台 - AI 知识库的分页 Request VO")
8 15 @Data
9 16 public class AiKnowledgePageReqVO extends PageParam {
10 17  
11   - @Schema(description = "知识库名称", example = "Java 开发手册")
  18 + @Schema(description = "知识库名称", example = "芋艿")
12 19 private String name;
13 20  
  21 + @Schema(description = "是否启用", example = "1")
  22 + @InEnum(CommonStatusEnum.class)
  23 + private Integer status;
  24 +
  25 + @Schema(description = "创建时间")
  26 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  27 + private LocalDateTime[] createTime;
  28 +
14 29 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
5 5  
  6 +import java.time.LocalDateTime;
6 7  
7 8 @Schema(description = "管理后台 - AI 知识库 Response VO")
8 9 @Data
... ... @@ -17,10 +18,22 @@ public class AiKnowledgeRespVO {
17 18 @Schema(description = "知识库描述", example = "帮助你快速构建系统")
18 19 private String description;
19 20  
20   - @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14")
21   - private Long modelId;
  21 + @Schema(description = "向量模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14")
  22 + private Long embeddingModelId;
22 23  
23   - @Schema(description = "模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat")
24   - private String model;
  24 + @Schema(description = "向量模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat")
  25 + private String embeddingModel;
  26 +
  27 + @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  28 + private Integer topK;
  29 +
  30 + @Schema(description = "相似度阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.7")
  31 + private Double similarityThreshold;
  32 +
  33 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  34 + private Integer status;
  35 +
  36 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  37 + private LocalDateTime createTime;
25 38  
26 39 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
3 5 import io.swagger.v3.oas.annotations.media.Schema;
4 6 import jakarta.validation.constraints.NotBlank;
5 7 import jakarta.validation.constraints.NotNull;
6 8 import lombok.Data;
7 9  
8   -import java.util.List;
9   -
10   -@Schema(description = "管理后台 - AI 知识库创建 Request VO")
  10 +@Schema(description = "管理后台 - AI 知识库新增/修改 Request VO")
11 11 @Data
12   -public class AiKnowledgeCreateReqVO {
  12 +public class AiKnowledgeSaveReqVO {
  13 +
  14 + @Schema(description = "对话编号", example = "1204")
  15 + private Long id;
13 16  
14 17 @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南")
15 18 @NotBlank(message = "知识库名称不能为空")
... ... @@ -18,19 +21,21 @@ public class AiKnowledgeCreateReqVO {
18 21 @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "存储 ruoyi-vue-pro 操作文档")
19 22 private String description;
20 23  
21   - @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]")
22   - private List<Long> visibilityPermissions;
  24 + @Schema(description = "向量模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  25 + @NotNull(message = "向量模型不能为空")
  26 + private Long embeddingModelId;
23 27  
24   - @Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
25   - @NotNull(message = "嵌入模型不能为空")
26   - private Long modelId;
  28 + @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
  29 + @NotNull(message = "topK 不能为空")
  30 + private Integer topK;
27 31  
28 32 @Schema(description = "相似性阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5")
29 33 @NotNull(message = "相似性阈值不能为空")
30 34 private Double similarityThreshold;
31 35  
32   - @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
33   - @NotNull(message = "topK 不能为空")
34   - private Integer topK;
  36 + @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  37 + @NotNull(message = "是否启用不能为空")
  38 + @InEnum(CommonStatusEnum.class)
  39 + private Integer status;
35 40  
36 41 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java deleted
1   -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
2   -
3   -import io.swagger.v3.oas.annotations.media.Schema;
4   -import jakarta.validation.constraints.NotBlank;
5   -import jakarta.validation.constraints.NotNull;
6   -import lombok.Data;
7   -
8   -import java.util.List;
9   -
10   -@Schema(description = "管理后台 - AI 知识库更新【我的】 Request VO")
11   -@Data
12   -public class AiKnowledgeUpdateReqVO {
13   -
14   - @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204")
15   - @NotNull(message = "知识库编号不能为空")
16   - private Long id;
17   -
18   - @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "")
19   - @NotBlank(message = "知识库名称不能为空")
20   - private String name;
21   -
22   - @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "")
23   - private String description;
24   -
25   - @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
26   - private List<Long> visibilityPermissions;
27   -
28   - @Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
29   - @NotNull(message = "嵌入模型不能为空")
30   - private Long modelId;
31   -
32   -}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageParam;
  5 +import cn.iocoder.yudao.framework.common.validation.InEnum;
4 6 import io.swagger.v3.oas.annotations.media.Schema;
5 7 import lombok.Data;
6 8  
... ... @@ -8,13 +10,14 @@ import lombok.Data;
8 10 @Data
9 11 public class AiKnowledgeSegmentPageReqVO extends PageParam {
10 12  
11   - @Schema(description = "分段状态", example = "1")
12   - private Integer status;
13   -
14 13 @Schema(description = "文档编号", example = "1")
15 14 private Integer documentId;
16 15  
17 16 @Schema(description = "分段内容关键字", example = "Java 开发")
18   - private String keyword;
  17 + private String content;
  18 +
  19 + @Schema(description = "分段状态", example = "1")
  20 + @InEnum(CommonStatusEnum.class)
  21 + private Integer status;
19 22  
20 23 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - AI 知识库段落向量进度 Response VO")
  7 +@Data
  8 +public class AiKnowledgeSegmentProcessRespVO {
  9 +
  10 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  11 + private Long documentId;
  12 +
  13 + @Schema(description = "总段落数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  14 + private Long count;
  15 +
  16 + @Schema(description = "已向量化段落数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
  17 + private Long embeddingCount;
  18 +
  19 +}
0 20 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java
... ... @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
5 5  
6   -@Schema(description = "管理后台 - AI 知识库-文档 Response VO")
  6 +@Schema(description = "管理后台 - AI 知识库文档分片 Response VO")
7 7 @Data
8 8 public class AiKnowledgeSegmentRespVO {
9 9  
... ... @@ -22,13 +22,19 @@ public class AiKnowledgeSegmentRespVO {
22 22 @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
23 23 private String content;
24 24  
  25 + @Schema(description = "切片内容长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  26 + private Integer contentLength;
  27 +
25 28 @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
26 29 private Integer tokens;
27 30  
28   - @Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008")
29   - private Integer wordCount;
  31 + @Schema(description = "召回次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  32 + private Integer retrievalCount;
30 33  
31 34 @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
32 35 private Integer status;
33 36  
  37 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  38 + private Long createTime;
  39 +
34 40 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
4 5 import lombok.Data;
5 6  
6   -
7   -@Schema(description = "管理后台 - AI 更新 知识库-段落 request VO")
  7 +@Schema(description = "管理后台 - AI 新增/修改知识库段落 request VO")
8 8 @Data
9   -public class AiKnowledgeSegmentUpdateReqVO {
  9 +public class AiKnowledgeSegmentSaveReqVO {
10 10  
11   - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
  11 + @Schema(description = "编号", example = "24790")
12 12 private Long id;
13 13  
  14 + @Schema(description = "知识库文档编号", example = "1024")
  15 + private Long documentId;
  16 +
14 17 @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
  18 + @NotEmpty(message = "切片内容不能为空")
15 19 private String content;
16 20  
17 21 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java
... ... @@ -3,15 +3,25 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
5 5  
  6 +import jakarta.validation.constraints.NotEmpty;
  7 +import jakarta.validation.constraints.NotNull;
6 8  
7   -@Schema(description = "管理后台 - AI 知识库段落召回 Request VO")
  9 +@Schema(description = "管理后台 - AI 知识库段落搜索 Request VO")
8 10 @Data
9 11 public class AiKnowledgeSegmentSearchReqVO {
10 12  
11   - @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
  13 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + @NotNull(message = "知识库编号不能为空")
12 15 private Long knowledgeId;
13 16  
14   - @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 学习路线")
  17 + @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "如何使用这个产品")
  18 + @NotEmpty(message = "内容不能为空")
15 19 private String content;
16 20  
  21 + @Schema(description = "最大返回数量", example = "5")
  22 + private Integer topK;
  23 +
  24 + @Schema(description = "相似度阈值", example = "0.7")
  25 + private Double similarityThreshold;
  26 +
17 27 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - AI 知识库段落搜索 Response VO")
  7 +@Data
  8 +public class AiKnowledgeSegmentSearchRespVO extends AiKnowledgeSegmentRespVO {
  9 +
  10 + @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品使用手册")
  11 + private String documentName;
  12 +
  13 + @Schema(description = "相似度分数", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.95")
  14 + private Double score;
  15 +
  16 +}
0 17 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/AiMindMapController.java
... ... @@ -12,7 +12,6 @@ import io.swagger.v3.oas.annotations.Operation;
12 12 import io.swagger.v3.oas.annotations.Parameter;
13 13 import io.swagger.v3.oas.annotations.tags.Tag;
14 14 import jakarta.annotation.Resource;
15   -import jakarta.annotation.security.PermitAll;
16 15 import jakarta.validation.Valid;
17 16 import org.springframework.http.MediaType;
18 17 import org.springframework.security.access.prepost.PreAuthorize;
... ... @@ -32,7 +31,6 @@ public class AiMindMapController {
32 31  
33 32 @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
34 33 @Operation(summary = "导图生成(流式)", description = "流式返回,响应较快")
35   - @PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题
36 34 public Flux<CommonResult<String>> generateMindMap(@RequestBody @Valid AiMindMapGenerateReqVO generateReqVO) {
37 35 return mindMapService.generateMindMap(generateReqVO, getLoginUserId());
38 36 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapPageReqVO.java
... ... @@ -13,8 +13,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
13 13  
14 14 @Schema(description = "管理后台 - AI 思维导图分页 Request VO")
15 15 @Data
16   -@EqualsAndHashCode(callSuper = true)
17   -@ToString(callSuper = true)
18 16 public class AiMindMapPageReqVO extends PageParam {
19 17  
20 18 @Schema(description = "用户编号", example = "4325")
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java
... ... @@ -6,9 +6,8 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 6 import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
7 7 import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyRespVO;
8 8 import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
9   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelRespVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelRespVO;
10 10 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
11   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
12 11 import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
13 12 import io.swagger.v3.oas.annotations.Operation;
14 13 import io.swagger.v3.oas.annotations.Parameter;
... ... @@ -76,9 +75,9 @@ public class AiApiKeyController {
76 75  
77 76 @GetMapping("/simple-list")
78 77 @Operation(summary = "获得 API 密钥分页列表")
79   - public CommonResult<List<AiChatModelRespVO>> getApiKeySimpleList() {
  78 + public CommonResult<List<AiModelRespVO>> getApiKeySimpleList() {
80 79 List<AiApiKeyDO> list = apiKeyService.getApiKeyList();
81   - return success(convertList(list, key -> new AiChatModelRespVO().setId(key.getId()).setName(key.getName())));
  80 + return success(convertList(list, key -> new AiModelRespVO().setId(key.getId()).setName(key.getName())));
82 81 }
83 82  
84 83 }
85 84 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java deleted
1   -package cn.iocoder.yudao.module.ai.controller.admin.model;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
5   -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
7   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelRespVO;
8   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO;
9   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
10   -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
11   -import io.swagger.v3.oas.annotations.Operation;
12   -import io.swagger.v3.oas.annotations.Parameter;
13   -import io.swagger.v3.oas.annotations.tags.Tag;
14   -import jakarta.annotation.Resource;
15   -import jakarta.validation.Valid;
16   -import org.springframework.security.access.prepost.PreAuthorize;
17   -import org.springframework.validation.annotation.Validated;
18   -import org.springframework.web.bind.annotation.*;
19   -
20   -import java.util.List;
21   -
22   -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
23   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
24   -
25   -@Tag(name = "管理后台 - AI 聊天模型")
26   -@RestController
27   -@RequestMapping("/ai/chat-model")
28   -@Validated
29   -public class AiChatModelController {
30   -
31   - @Resource
32   - private AiChatModelService chatModelService;
33   -
34   - @PostMapping("/create")
35   - @Operation(summary = "创建聊天模型")
36   - @PreAuthorize("@ss.hasPermission('ai:chat-model:create')")
37   - public CommonResult<Long> createChatModel(@Valid @RequestBody AiChatModelSaveReqVO createReqVO) {
38   - return success(chatModelService.createChatModel(createReqVO));
39   - }
40   -
41   - @PutMapping("/update")
42   - @Operation(summary = "更新聊天模型")
43   - @PreAuthorize("@ss.hasPermission('ai:chat-model:update')")
44   - public CommonResult<Boolean> updateChatModel(@Valid @RequestBody AiChatModelSaveReqVO updateReqVO) {
45   - chatModelService.updateChatModel(updateReqVO);
46   - return success(true);
47   - }
48   -
49   - @DeleteMapping("/delete")
50   - @Operation(summary = "删除聊天模型")
51   - @Parameter(name = "id", description = "编号", required = true)
52   - @PreAuthorize("@ss.hasPermission('ai:chat-model:delete')")
53   - public CommonResult<Boolean> deleteChatModel(@RequestParam("id") Long id) {
54   - chatModelService.deleteChatModel(id);
55   - return success(true);
56   - }
57   -
58   - @GetMapping("/get")
59   - @Operation(summary = "获得聊天模型")
60   - @Parameter(name = "id", description = "编号", required = true, example = "1024")
61   - @PreAuthorize("@ss.hasPermission('ai:chat-model:query')")
62   - public CommonResult<AiChatModelRespVO> getChatModel(@RequestParam("id") Long id) {
63   - AiChatModelDO chatModel = chatModelService.getChatModel(id);
64   - return success(BeanUtils.toBean(chatModel, AiChatModelRespVO.class));
65   - }
66   -
67   - @GetMapping("/page")
68   - @Operation(summary = "获得聊天模型分页")
69   - @PreAuthorize("@ss.hasPermission('ai:chat-model:query')")
70   - public CommonResult<PageResult<AiChatModelRespVO>> getChatModelPage(@Valid AiChatModelPageReqVO pageReqVO) {
71   - PageResult<AiChatModelDO> pageResult = chatModelService.getChatModelPage(pageReqVO);
72   - return success(BeanUtils.toBean(pageResult, AiChatModelRespVO.class));
73   - }
74   -
75   - @GetMapping("/simple-list")
76   - @Operation(summary = "获得聊天模型列表")
77   - @Parameter(name = "status", description = "状态", required = true, example = "1")
78   - public CommonResult<List<AiChatModelRespVO>> getChatModelSimpleList(@RequestParam("status") Integer status) {
79   - List<AiChatModelDO> list = chatModelService.getChatModelListByStatus(status);
80   - return success(convertList(list, model -> new AiChatModelRespVO().setId(model.getId())
81   - .setName(model.getName()).setModel(model.getModel())));
82   - }
83   -
84   -}
85 0 \ No newline at end of file
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java
... ... @@ -75,7 +75,7 @@ public class AiChatRoleController {
75 75 @GetMapping("/category-list")
76 76 @Operation(summary = "获得聊天角色的分类列表")
77 77 public CommonResult<List<String>> getChatRoleCategoryList() {
78   - return success(chatRoleService.getChatRoleCategoryList());
  78 + return success(chatRoleService.getChatRoleCategoryList());
79 79 }
80 80  
81 81 // ========== 角色管理 ==========
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiModelController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelRespVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;
  10 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
  11 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
  12 +import io.swagger.v3.oas.annotations.Operation;
  13 +import io.swagger.v3.oas.annotations.Parameter;
  14 +import io.swagger.v3.oas.annotations.tags.Tag;
  15 +import jakarta.annotation.Resource;
  16 +import jakarta.validation.Valid;
  17 +import org.springframework.security.access.prepost.PreAuthorize;
  18 +import org.springframework.validation.annotation.Validated;
  19 +import org.springframework.web.bind.annotation.*;
  20 +
  21 +import java.util.List;
  22 +
  23 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  25 +
  26 +@Tag(name = "管理后台 - AI 模型")
  27 +@RestController
  28 +@RequestMapping("/ai/model")
  29 +@Validated
  30 +public class AiModelController {
  31 +
  32 + @Resource
  33 + private AiModelService modelService;
  34 +
  35 + @PostMapping("/create")
  36 + @Operation(summary = "创建模型")
  37 + @PreAuthorize("@ss.hasPermission('ai:model:create')")
  38 + public CommonResult<Long> createModel(@Valid @RequestBody AiModelSaveReqVO createReqVO) {
  39 + return success(modelService.createModel(createReqVO));
  40 + }
  41 +
  42 + @PutMapping("/update")
  43 + @Operation(summary = "更新模型")
  44 + @PreAuthorize("@ss.hasPermission('ai:model:update')")
  45 + public CommonResult<Boolean> updateModel(@Valid @RequestBody AiModelSaveReqVO updateReqVO) {
  46 + modelService.updateModel(updateReqVO);
  47 + return success(true);
  48 + }
  49 +
  50 + @DeleteMapping("/delete")
  51 + @Operation(summary = "删除模型")
  52 + @Parameter(name = "id", description = "编号", required = true)
  53 + @PreAuthorize("@ss.hasPermission('ai:model:delete')")
  54 + public CommonResult<Boolean> deleteModel(@RequestParam("id") Long id) {
  55 + modelService.deleteModel(id);
  56 + return success(true);
  57 + }
  58 +
  59 + @GetMapping("/get")
  60 + @Operation(summary = "获得模型")
  61 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  62 + @PreAuthorize("@ss.hasPermission('ai:model:query')")
  63 + public CommonResult<AiModelRespVO> getModel(@RequestParam("id") Long id) {
  64 + AiModelDO model = modelService.getModel(id);
  65 + return success(BeanUtils.toBean(model, AiModelRespVO.class));
  66 + }
  67 +
  68 + @GetMapping("/page")
  69 + @Operation(summary = "获得模型分页")
  70 + @PreAuthorize("@ss.hasPermission('ai:model:query')")
  71 + public CommonResult<PageResult<AiModelRespVO>> getModelPage(@Valid AiModelPageReqVO pageReqVO) {
  72 + PageResult<AiModelDO> pageResult = modelService.getModelPage(pageReqVO);
  73 + return success(BeanUtils.toBean(pageResult, AiModelRespVO.class));
  74 + }
  75 +
  76 + @GetMapping("/simple-list")
  77 + @Operation(summary = "获得模型列表")
  78 + @Parameter(name = "type", description = "类型", required = true, example = "1")
  79 + @Parameter(name = "platform", description = "平台", example = "midjourney")
  80 + public CommonResult<List<AiModelRespVO>> getModelSimpleList(
  81 + @RequestParam("type") Integer type,
  82 + @RequestParam(value = "platform", required = false) String platform) {
  83 + List<AiModelDO> list = modelService.getModelListByStatusAndType(
  84 + CommonStatusEnum.ENABLE.getStatus(), type, platform);
  85 + return success(convertList(list, model -> new AiModelRespVO().setId(model.getId())
  86 + .setName(model.getName()).setModel(model.getModel()).setPlatform(model.getPlatform())));
  87 + }
  88 +
  89 +}
0 90 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiToolController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolRespVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO;
  10 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
  11 +import cn.iocoder.yudao.module.ai.service.model.AiToolService;
  12 +import io.swagger.v3.oas.annotations.Operation;
  13 +import io.swagger.v3.oas.annotations.Parameter;
  14 +import io.swagger.v3.oas.annotations.tags.Tag;
  15 +import jakarta.annotation.Resource;
  16 +import jakarta.validation.Valid;
  17 +import org.springframework.security.access.prepost.PreAuthorize;
  18 +import org.springframework.validation.annotation.Validated;
  19 +import org.springframework.web.bind.annotation.*;
  20 +
  21 +import java.util.List;
  22 +
  23 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  25 +
  26 +@Tag(name = "管理后台 - AI 工具")
  27 +@RestController
  28 +@RequestMapping("/ai/tool")
  29 +@Validated
  30 +public class AiToolController {
  31 +
  32 + @Resource
  33 + private AiToolService toolService;
  34 +
  35 + @PostMapping("/create")
  36 + @Operation(summary = "创建工具")
  37 + @PreAuthorize("@ss.hasPermission('ai:tool:create')")
  38 + public CommonResult<Long> createTool(@Valid @RequestBody AiToolSaveReqVO createReqVO) {
  39 + return success(toolService.createTool(createReqVO));
  40 + }
  41 +
  42 + @PutMapping("/update")
  43 + @Operation(summary = "更新工具")
  44 + @PreAuthorize("@ss.hasPermission('ai:tool:update')")
  45 + public CommonResult<Boolean> updateTool(@Valid @RequestBody AiToolSaveReqVO updateReqVO) {
  46 + toolService.updateTool(updateReqVO);
  47 + return success(true);
  48 + }
  49 +
  50 + @DeleteMapping("/delete")
  51 + @Operation(summary = "删除工具")
  52 + @Parameter(name = "id", description = "编号", required = true)
  53 + @PreAuthorize("@ss.hasPermission('ai:tool:delete')")
  54 + public CommonResult<Boolean> deleteTool(@RequestParam("id") Long id) {
  55 + toolService.deleteTool(id);
  56 + return success(true);
  57 + }
  58 +
  59 + @GetMapping("/get")
  60 + @Operation(summary = "获得工具")
  61 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  62 + @PreAuthorize("@ss.hasPermission('ai:tool:query')")
  63 + public CommonResult<AiToolRespVO> getTool(@RequestParam("id") Long id) {
  64 + AiToolDO tool = toolService.getTool(id);
  65 + return success(BeanUtils.toBean(tool, AiToolRespVO.class));
  66 + }
  67 +
  68 + @GetMapping("/page")
  69 + @Operation(summary = "获得工具分页")
  70 + @PreAuthorize("@ss.hasPermission('ai:tool:query')")
  71 + public CommonResult<PageResult<AiToolRespVO>> getToolPage(@Valid AiToolPageReqVO pageReqVO) {
  72 + PageResult<AiToolDO> pageResult = toolService.getToolPage(pageReqVO);
  73 + return success(BeanUtils.toBean(pageResult, AiToolRespVO.class));
  74 + }
  75 +
  76 + @GetMapping("/simple-list")
  77 + @Operation(summary = "获得工具列表")
  78 + public CommonResult<List<AiToolRespVO>> getToolSimpleList() {
  79 + List<AiToolDO> list = toolService.getToolListByStatus(CommonStatusEnum.ENABLE.getStatus());
  80 + return success(convertList(list, tool -> new AiToolRespVO()
  81 + .setId(tool.getId()).setName(tool.getName())));
  82 + }
  83 +
  84 +}
0 85 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;
2 2  
3   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  3 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
4 4 import com.fhs.core.trans.anno.Trans;
5 5 import com.fhs.core.trans.constant.TransType;
6 6 import com.fhs.core.trans.vo.VO;
... ... @@ -8,6 +8,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
8 8 import lombok.Data;
9 9  
10 10 import java.time.LocalDateTime;
  11 +import java.util.List;
11 12  
12 13 @Schema(description = "管理后台 - AI 聊天角色 Response VO")
13 14 @Data
... ... @@ -20,7 +21,7 @@ public class AiChatRoleRespVO implements VO {
20 21 private Long userId;
21 22  
22 23 @Schema(description = "模型编号", example = "17640")
23   - @Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = {"name", "model"}, refs = {"modelName", "model"})
  24 + @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = { "name", "model" }, refs = { "modelName", "model" })
24 25 private Long modelId;
25 26 @Schema(description = "模型名字", example = "张三")
26 27 private String modelName;
... ... @@ -45,6 +46,12 @@ public class AiChatRoleRespVO implements VO {
45 46 @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED)
46 47 private String systemMessage;
47 48  
  49 + @Schema(description = "引用的知识库编号列表", example = "1,2,3")
  50 + private List<Long> knowledgeIds;
  51 +
  52 + @Schema(description = "引用的工具编号列表", example = "1,2,3")
  53 + private List<Long> toolIds;
  54 +
48 55 @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
49 56 private Boolean publicStatus;
50 57  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java
... ... @@ -5,6 +5,8 @@ import jakarta.validation.constraints.NotEmpty;
5 5 import lombok.Data;
6 6 import org.hibernate.validator.constraints.URL;
7 7  
  8 +import java.util.List;
  9 +
8 10 @Schema(description = "管理后台 - AI 聊天角色新增/修改【我的】 Request VO")
9 11 @Data
10 12 public class AiChatRoleSaveMyReqVO {
... ... @@ -29,4 +31,10 @@ public class AiChatRoleSaveMyReqVO {
29 31 @NotEmpty(message = "角色设定不能为空")
30 32 private String systemMessage;
31 33  
  34 + @Schema(description = "引用的知识库编号列表", example = "1,2,3")
  35 + private List<Long> knowledgeIds;
  36 +
  37 + @Schema(description = "引用的工具编号列表", example = "1,2,3")
  38 + private List<Long> toolIds;
  39 +
32 40 }
33 41 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java
... ... @@ -7,6 +7,8 @@ import lombok.*;
7 7 import jakarta.validation.constraints.*;
8 8 import org.hibernate.validator.constraints.URL;
9 9  
  10 +import java.util.List;
  11 +
10 12 @Schema(description = "管理后台 - AI 聊天角色新增/修改 Request VO")
11 13 @Data
12 14 public class AiChatRoleSaveReqVO {
... ... @@ -42,6 +44,12 @@ public class AiChatRoleSaveReqVO {
42 44 @NotEmpty(message = "角色设定不能为空")
43 45 private String systemMessage;
44 46  
  47 + @Schema(description = "引用的知识库编号列表", example = "1,2,3")
  48 + private List<Long> knowledgeIds;
  49 +
  50 + @Schema(description = "引用的工具编号列表", example = "1,2,3")
  51 + private List<Long> toolIds;
  52 +
45 53 @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
46 54 @NotNull(message = "是否公开不能为空")
47 55 private Boolean publicStatus;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelPageReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java
1   -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
2 2  
3 3 import lombok.*;
4 4 import io.swagger.v3.oas.annotations.media.Schema;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageParam;
6 6  
7   -@Schema(description = "管理后台 - API 聊天模型分页 Request VO")
  7 +@Schema(description = "管理后台 - API 模型分页 Request VO")
8 8 @Data
9   -public class AiChatModelPageReqVO extends PageParam {
  9 +public class AiModelPageReqVO extends PageParam {
10 10  
11 11 @Schema(description = "模型名字", example = "张三")
12 12 private String name;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelRespVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelRespVO.java
1   -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
5 5  
6 6 import java.time.LocalDateTime;
7 7  
8   -@Schema(description = "管理后台 - AI 聊天模型 Response VO")
  8 +@Schema(description = "管理后台 - AI 模型 Response VO")
9 9 @Data
10   -public class AiChatModelRespVO {
  10 +public class AiModelRespVO {
11 11  
12 12 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2630")
13 13 private Long id;
... ... @@ -24,6 +24,9 @@ public class AiChatModelRespVO {
24 24 @Schema(description = "模型平台", example = "OpenAI")
25 25 private String platform;
26 26  
  27 + @Schema(description = "模型类型", example = "1")
  28 + private Integer type;
  29 +
27 30 @Schema(description = "排序", example = "1")
28 31 private Integer sort;
29 32  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelSaveReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java
1   -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
2 2  
  3 +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum;
  4 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
3 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 6 import cn.iocoder.yudao.framework.common.validation.InEnum;
5 7 import io.swagger.v3.oas.annotations.media.Schema;
6   -import lombok.*;
7   -import jakarta.validation.constraints.*;
  8 +import jakarta.validation.constraints.NotEmpty;
  9 +import jakarta.validation.constraints.NotNull;
  10 +import lombok.Data;
8 11  
9   -@Schema(description = "管理后台 - API 聊天模型新增/修改 Request VO")
  12 +@Schema(description = "管理后台 - API 模型新增/修改 Request VO")
10 13 @Data
11   -public class AiChatModelSaveReqVO {
  14 +public class AiModelSaveReqVO {
12 15  
13 16 @Schema(description = "编号", example = "2630")
14 17 private Long id;
... ... @@ -27,8 +30,14 @@ public class AiChatModelSaveReqVO {
27 30  
28 31 @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
29 32 @NotEmpty(message = "模型平台不能为空")
  33 + @InEnum(AiPlatformEnum.class)
30 34 private String platform;
31 35  
  36 + @Schema(description = "模型类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  37 + @NotNull(message = "模型类型不能为空")
  38 + @InEnum(AiModelTypeEnum.class)
  39 + private Integer type;
  40 +
32 41 @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
33 42 @NotNull(message = "排序不能为空")
34 43 private Integer sort;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  5 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import lombok.Data;
  8 +import lombok.EqualsAndHashCode;
  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;
  15 +
  16 +@Schema(description = "管理后台 - AI 工具分页 Request VO")
  17 +@Data
  18 +public class AiToolPageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "工具名称", example = "王五")
  21 + private String name;
  22 +
  23 + @Schema(description = "工具描述", example = "你猜")
  24 + private String description;
  25 +
  26 + @Schema(description = "状态", example = "1")
  27 + @InEnum(CommonStatusEnum.class)
  28 + private Integer status;
  29 +
  30 + @Schema(description = "创建时间")
  31 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  32 + private LocalDateTime[] createTime;
  33 +
  34 +}
0 35 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - AI 工具 Response VO")
  9 +@Data
  10 +public class AiToolRespVO {
  11 +
  12 + @Schema(description = "工具编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19661")
  13 + private Long id;
  14 +
  15 + @Schema(description = "工具名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
  16 + private String name;
  17 +
  18 + @Schema(description = "工具描述", example = "你猜")
  19 + private String description;
  20 +
  21 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  22 + private Integer status;
  23 +
  24 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  25 + private LocalDateTime createTime;
  26 +
  27 +}
0 28 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import jakarta.validation.constraints.NotEmpty;
  7 +import lombok.Data;
  8 +
  9 +@Schema(description = "管理后台 - AI 工具新增/修改 Request VO")
  10 +@Data
  11 +public class AiToolSaveReqVO {
  12 +
  13 + @Schema(description = "工具编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19661")
  14 + private Long id;
  15 +
  16 + @Schema(description = "工具名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
  17 + @NotEmpty(message = "工具名称不能为空")
  18 + private String name;
  19 +
  20 + @Schema(description = "工具描述", example = "你猜")
  21 + private String description;
  22 +
  23 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  24 + @InEnum(CommonStatusEnum.class)
  25 + private Integer status;
  26 +
  27 +}
0 28 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java
... ... @@ -12,7 +12,6 @@ import io.swagger.v3.oas.annotations.Operation;
12 12 import io.swagger.v3.oas.annotations.Parameter;
13 13 import io.swagger.v3.oas.annotations.tags.Tag;
14 14 import jakarta.annotation.Resource;
15   -import jakarta.annotation.security.PermitAll;
16 15 import jakarta.validation.Valid;
17 16 import org.springframework.http.MediaType;
18 17 import org.springframework.security.access.prepost.PreAuthorize;
... ... @@ -32,7 +31,6 @@ public class AiWriteController {
32 31  
33 32 @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
34 33 @Operation(summary = "写作生成(流式)", description = "流式返回,响应较快")
35   - @PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题
36 34 public Flux<CommonResult<String>> generateWriteContent(@RequestBody @Valid AiWriteGenerateReqVO generateReqVO) {
37 35 return writeService.generateWriteContent(generateReqVO, getLoginUserId());
38 36 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWritePageReqVO.java
... ... @@ -13,8 +13,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
13 13  
14 14 @Schema(description = "管理后台 - AI 写作分页 Request VO")
15 15 @Data
16   -@EqualsAndHashCode(callSuper = true)
17   -@ToString(callSuper = true)
18 16 public class AiWritePageReqVO extends PageParam {
19 17  
20 18 @Schema(description = "用户编号", example = "28404")
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java
1 1 package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4   -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
5   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
6 4 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
7 6 import com.baomidou.mybatisplus.annotation.KeySequence;
8 7 import com.baomidou.mybatisplus.annotation.TableId;
9 8 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -22,7 +21,6 @@ import java.time.LocalDateTime;
22 21 @TableName("ai_chat_conversation")
23 22 @KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
24 23 @Data
25   -@EqualsAndHashCode(callSuper = true)
26 24 @Builder
27 25 @NoArgsConstructor
28 26 @AllArgsConstructor
... ... @@ -66,20 +64,15 @@ public class AiChatConversationDO extends BaseDO {
66 64 private Long roleId;
67 65  
68 66 /**
69   - * 知识库编号
70   - * <p>
71   - * 关联 {@link AiKnowledgeDO#getId()}
72   - */
73   - private Long knowledgeId;
74   -
75   - /**
76 67 * 模型编号
77 68 *
78   - * 关联 {@link AiChatModelDO#getId()} 字段
  69 + * 关联 {@link AiModelDO#getId()} 字段
79 70 */
80 71 private Long modelId;
81 72 /**
82 73 * 模型标志
  74 + *
  75 + * 冗余 {@link AiModelDO#getModel()} 字段
83 76 */
84 77 private String model;
85 78  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java
1 1 package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
4 5 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
5   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
6 6 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
7 8 import com.baomidou.mybatisplus.annotation.KeySequence;
8 9 import com.baomidou.mybatisplus.annotation.TableField;
9 10 import com.baomidou.mybatisplus.annotation.TableId;
10 11 import com.baomidou.mybatisplus.annotation.TableName;
11   -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
12 12 import lombok.*;
13 13 import org.springframework.ai.chat.messages.MessageType;
14 14  
... ... @@ -20,10 +20,9 @@ import java.util.List;
20 20 * @since 2024/4/14 17:35
21 21 * @since 2024/4/14 17:35
22 22 */
23   -@TableName("ai_chat_message")
  23 +@TableName(value = "ai_chat_message", autoResultMap = true)
24 24 @KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
25 25 @Data
26   -@EqualsAndHashCode(callSuper = true)
27 26 @Builder
28 27 @NoArgsConstructor
29 28 @AllArgsConstructor
... ... @@ -71,23 +70,16 @@ public class AiChatMessageDO extends BaseDO {
71 70 */
72 71 private Long roleId;
73 72  
74   -
75   - /**
76   - * 段落编号数组
77   - *
78   - * 关联 {@link AiKnowledgeSegmentDO#getId()} 字段
79   - */
80   - @TableField(typeHandler = JacksonTypeHandler.class)
81   - private List<Long> segmentIds;
82   -
83 73 /**
84 74 * 模型标志
  75 + *
  76 + * 冗余 {@link AiModelDO#getModel()}
85 77 */
86 78 private String model;
87 79 /**
88 80 * 模型编号
89 81 *
90   - * 关联 {@link AiChatModelDO#getId()} 字段
  82 + * 关联 {@link AiModelDO#getId()} 字段
91 83 */
92 84 private Long modelId;
93 85  
... ... @@ -101,4 +93,12 @@ public class AiChatMessageDO extends BaseDO {
101 93 */
102 94 private Boolean useContext;
103 95  
  96 + /**
  97 + * 知识库段落编号数组
  98 + *
  99 + * 关联 {@link AiKnowledgeSegmentDO#getId()} 字段
  100 + */
  101 + @TableField(typeHandler = LongListTypeHandler.class)
  102 + private List<Long> segmentIds;
  103 +
104 104 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java
... ... @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.image;
2 2  
3 3 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
6 6 import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
7 7 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
8 8 import com.baomidou.mybatisplus.annotation.KeySequence;
... ... @@ -53,9 +53,15 @@ public class AiImageDO extends BaseDO {
53 53 */
54 54 private String platform;
55 55 /**
56   - * 模型
  56 + * 模型编号
57 57 *
58   - * 冗余 {@link AiChatModelDO#getModel()}
  58 + * 关联 {@link AiModelDO#getId()}
  59 + */
  60 + private Long modelId;
  61 + /**
  62 + * 模型标识
  63 + *
  64 + * 冗余 {@link AiModelDO#getModel()}
59 65 */
60 66 private String model;
61 67  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
... ... @@ -2,15 +2,12 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5   -import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
6 6 import com.baomidou.mybatisplus.annotation.KeySequence;
7   -import com.baomidou.mybatisplus.annotation.TableField;
8 7 import com.baomidou.mybatisplus.annotation.TableId;
9 8 import com.baomidou.mybatisplus.annotation.TableName;
10 9 import lombok.Data;
11 10  
12   -import java.util.List;
13   -
14 11 /**
15 12 * AI 知识库 DO
16 13 *
... ... @@ -27,12 +24,6 @@ public class AiKnowledgeDO extends BaseDO {
27 24 @TableId
28 25 private Long id;
29 26 /**
30   - * 用户编号
31   - * <p>
32   - * 关联 AdminUserDO 的 userId 字段
33   - */
34   - private Long userId;
35   - /**
36 27 * 知识库名称
37 28 */
38 29 private String name;
... ... @@ -42,20 +33,17 @@ public class AiKnowledgeDO extends BaseDO {
42 33 private String description;
43 34  
44 35 /**
45   - * 可见权限,选择哪些人可见
46   - * <p>
47   - * -1 所有人可见,其他为各自用户编号
48   - */
49   - @TableField(typeHandler = LongListTypeHandler.class)
50   - private List<Long> visibilityPermissions;
51   - /**
52   - * 嵌入模型编号
  36 + * 向量模型编号
  37 + *
  38 + * 关联 {@link AiModelDO#getId()}
53 39 */
54   - private Long modelId;
  40 + private Long embeddingModelId;
55 41 /**
56 42 * 模型标识
  43 + *
  44 + * 冗余 {@link AiModelDO#getModel()}
57 45 */
58   - private String model;
  46 + private String embeddingModel;
59 47  
60 48 /**
61 49 * topK
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
... ... @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5   -import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum;
6 5 import com.baomidou.mybatisplus.annotation.KeySequence;
7 6 import com.baomidou.mybatisplus.annotation.TableId;
8 7 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -30,57 +29,35 @@ public class AiKnowledgeDocumentDO extends BaseDO {
30 29 */
31 30 private Long knowledgeId;
32 31 /**
33   - * 文件名称
  32 + * 文档名称
34 33 */
35 34 private String name;
36 35 /**
37   - * 内容
38   - */
39   - private String content;
40   - /**
41 36 * 文件 URL
42 37 */
43 38 private String url;
44 39 /**
45   - * 文档 token 数量
  40 + * 内容
46 41 */
47   - private Integer tokens;
  42 + private String content;
48 43 /**
49   - * 文档字符数
  44 + * 文档长度
50 45 */
51   - private Integer wordCount;
52   -
  46 + private Integer contentLength;
53 47  
54   - // ========== 自定义分段所用参数 ==========
55   - // TODO @新:3)defaultChunkSize、defaultChunkSize、minChunkSizeChars、maxNumChunks 这几个字段的命名,可能要微信一起讨论下。尽量命名保持风格统一哈。
56 48 /**
57   - * 每个文本块的目标 token 数
58   - */
59   - private Integer defaultSegmentTokens;
60   - /**
61   - * 每个文本块的最小字符数
62   - */
63   - private Integer minSegmentWordCount;
64   - /**
65   - * 低于此值的块会被丢弃
66   - */
67   - private Integer minChunkLengthToEmbed;
68   - /**
69   - * 最大块数
  49 + * 文档 token 数量
70 50 */
71   - private Integer maxNumSegments;
  51 + private Integer tokens;
72 52 /**
73   - * 分块是否保留分隔符
  53 + * 分片最大 Token 数
74 54 */
75   - private Boolean keepSeparator;
76   - // ===================================
  55 + private Integer segmentMaxTokens;
77 56  
78 57 /**
79   - * 切片状态
80   - * <p>
81   - * 枚举 {@link AiKnowledgeDocumentStatusEnum}
  58 + * 召回次数
82 59 */
83   - private Integer sliceStatus;
  60 + private Integer retrievalCount;
84 61  
85 62 /**
86 63 * 状态
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
... ... @@ -17,7 +17,10 @@ import lombok.Data;
17 17 @Data
18 18 public class AiKnowledgeSegmentDO extends BaseDO {
19 19  
20   - public static final String FIELD_KNOWLEDGE_ID = "knowledgeId";
  20 + /**
  21 + * 向量库的编号 - 空值
  22 + */
  23 + public static final String VECTOR_ID_EMPTY = "";
21 24  
22 25 /**
23 26 * 编号
... ... @@ -25,10 +28,6 @@ public class AiKnowledgeSegmentDO extends BaseDO {
25 28 @TableId
26 29 private Long id;
27 30 /**
28   - * 向量库的编号
29   - */
30   - private String vectorId;
31   - /**
32 31 * 知识库编号
33 32 * <p>
34 33 * 关联 {@link AiKnowledgeDO#getId()}
... ... @@ -45,13 +44,24 @@ public class AiKnowledgeSegmentDO extends BaseDO {
45 44 */
46 45 private String content;
47 46 /**
48   - * 字符数
  47 + * 切片内容长度
49 48 */
50   - private Integer wordCount;
  49 + private Integer contentLength;
  50 +
  51 + /**
  52 + * 向量库的编号
  53 + */
  54 + private String vectorId;
51 55 /**
52 56 * token 数量
53 57 */
54 58 private Integer tokens;
  59 +
  60 + /**
  61 + * 召回次数
  62 + */
  63 + private Integer retrievalCount;
  64 +
55 65 /**
56 66 * 状态
57 67 * <p>
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.mindmap;
2 2  
3 3 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
5 6 import com.baomidou.mybatisplus.annotation.KeySequence;
6 7 import com.baomidou.mybatisplus.annotation.TableId;
7 8 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -37,6 +38,12 @@ public class AiMindMapDO extends BaseDO {
37 38 */
38 39 private String platform;
39 40 /**
  41 + * 模型编号
  42 + *
  43 + * 关联 {@link AiModelDO#getId()}
  44 + */
  45 + private Long modelId;
  46 + /**
40 47 * 模型
41 48 */
42 49 private String model;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java
... ... @@ -16,7 +16,6 @@ import lombok.*;
16 16 @TableName("ai_api_key")
17 17 @KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
18 18 @Data
19   -@EqualsAndHashCode(callSuper = true)
20 19 @Builder
21 20 @NoArgsConstructor
22 21 @AllArgsConstructor
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java
... ... @@ -2,11 +2,16 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.model;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
  6 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
5 7 import com.baomidou.mybatisplus.annotation.KeySequence;
  8 +import com.baomidou.mybatisplus.annotation.TableField;
6 9 import com.baomidou.mybatisplus.annotation.TableId;
7 10 import com.baomidou.mybatisplus.annotation.TableName;
8 11 import lombok.*;
9 12  
  13 +import java.util.List;
  14 +
10 15 /**
11 16 * AI 聊天角色 DO
12 17 *
... ... @@ -16,7 +21,6 @@ import lombok.*;
16 21 @TableName(value = "ai_chat_role", autoResultMap = true)
17 22 @KeySequence("ai_chat_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
18 23 @Data
19   -@EqualsAndHashCode(callSuper = true)
20 24 @Builder
21 25 @NoArgsConstructor
22 26 @AllArgsConstructor
... ... @@ -58,11 +62,26 @@ public class AiChatRoleDO extends BaseDO {
58 62 /**
59 63 * 模型编号
60 64 *
61   - * 关联 {@link AiChatModelDO#getId()} 字段
  65 + * 关联 {@link AiModelDO#getId()} 字段
62 66 */
63 67 private Long modelId;
64 68  
65 69 /**
  70 + * 引用的知识库编号列表
  71 + *
  72 + * 关联 {@link AiKnowledgeDO#getId()} 字段
  73 + */
  74 + @TableField(typeHandler = LongListTypeHandler.class)
  75 + private List<Long> knowledgeIds;
  76 + /**
  77 + * 引用的工具编号列表
  78 + *
  79 + * 关联 {@link AiToolDO#getId()} 字段
  80 + */
  81 + @TableField(typeHandler = LongListTypeHandler.class)
  82 + private List<Long> toolIds;
  83 +
  84 + /**
66 85 * 是否公开
67 86 *
68 87 * 1. true - 公开;由管理员在【角色管理】所创建
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatModelDO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java
1 1 package cn.iocoder.yudao.module.ai.dal.dataobject.model;
2 2  
  3 +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum;
3 4 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 6 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
... ... @@ -9,21 +10,20 @@ import com.baomidou.mybatisplus.annotation.TableName;
9 10 import lombok.*;
10 11  
11 12 /**
12   - * AI 聊天模型 DO
  13 + * AI 模型 DO
13 14 *
14   - * 默认聊天模型:{@link #status} 为开启,并且 {@link #sort} 排序第一
  15 + * 默认模型:{@link #status} 为开启,并且 {@link #sort} 排序第一
15 16 *
16 17 * @author fansili
17 18 * @since 2024/4/24 19:39
18 19 */
19   -@TableName("ai_chat_model")
20   -@KeySequence("ai_chat_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  20 +@TableName("ai_model")
  21 +@KeySequence("ai_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
21 22 @Data
22   -@EqualsAndHashCode(callSuper = true)
23 23 @Builder
24 24 @NoArgsConstructor
25 25 @AllArgsConstructor
26   -public class AiChatModelDO extends BaseDO {
  26 +public class AiModelDO extends BaseDO {
27 27  
28 28 /**
29 29 * 编号
... ... @@ -50,6 +50,12 @@ public class AiChatModelDO extends BaseDO {
50 50 * 枚举 {@link AiPlatformEnum}
51 51 */
52 52 private String platform;
  53 + /**
  54 + * 类型
  55 + *
  56 + * 枚举 {@link AiModelTypeEnum}
  57 + */
  58 + private Integer type;
53 59  
54 60 /**
55 61 * 排序值
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.dataobject.model;
  2 +
  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;
  6 +import com.baomidou.mybatisplus.annotation.KeySequence;
  7 +import com.baomidou.mybatisplus.annotation.TableId;
  8 +import com.baomidou.mybatisplus.annotation.TableName;
  9 +import lombok.*;
  10 +
  11 +/**
  12 + * AI 工具 DO
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@TableName("ai_tool")
  17 +@KeySequence("ai_tool_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  18 +@Data
  19 +@Builder
  20 +@NoArgsConstructor
  21 +@AllArgsConstructor
  22 +public class AiToolDO extends BaseDO {
  23 +
  24 + /**
  25 + * 工具编号
  26 + */
  27 + @TableId
  28 + private Long id;
  29 + /**
  30 + * 工具名称
  31 + *
  32 + * 对应 Bean 的名字,例如说:
  33 + * 1. {@link DirectoryListToolFunction} 的 Bean 名字是 directory_list
  34 + * 2. {@link WeatherQueryToolFunction} 的 Bean 名字是 weather_query
  35 + */
  36 + private String name;
  37 + /**
  38 + * 工具描述
  39 + */
  40 + private String description;
  41 + /**
  42 + * 状态
  43 + *
  44 + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
  45 + */
  46 + private Integer status;
  47 +
  48 +}
0 49 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java
... ... @@ -84,6 +84,7 @@ public class AiMusicDO extends BaseDO {
84 84 * 枚举 {@link AiPlatformEnum}
85 85 */
86 86 private String platform;
  87 + // TODO @芋艿:modelId?
87 88 /**
88 89 * 模型
89 90 */
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java
... ... @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.write;
2 2  
3 3 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
  6 +import cn.iocoder.yudao.module.ai.enums.DictTypeConstants;
5 7 import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;
6 8 import com.baomidou.mybatisplus.annotation.KeySequence;
7 9 import com.baomidou.mybatisplus.annotation.TableId;
... ... @@ -45,6 +47,12 @@ public class AiWriteDO extends BaseDO {
45 47 */
46 48 private String platform;
47 49 /**
  50 + * 模型编号
  51 + *
  52 + * 关联 {@link AiModelDO#getId()}
  53 + */
  54 + private Long modelId;
  55 + /**
48 56 * 模型
49 57 */
50 58 private String model;
... ... @@ -66,25 +74,25 @@ public class AiWriteDO extends BaseDO {
66 74 /**
67 75 * 长度提示词
68 76 *
69   - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_LENGTH}
  77 + * 字典:{@link DictTypeConstants#AI_WRITE_LENGTH}
70 78 */
71 79 private Integer length;
72 80 /**
73 81 * 格式提示词
74 82 *
75   - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_FORMAT}
  83 + * 字典:{@link DictTypeConstants#AI_WRITE_FORMAT}
76 84 */
77 85 private Integer format;
78 86 /**
79 87 * 语气提示词
80 88 *
81   - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_TONE}
  89 + * 字典:{@link DictTypeConstants#AI_WRITE_TONE}
82 90 */
83 91 private Integer tone;
84 92 /**
85 93 * 语言提示词
86 94 *
87   - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_LANGUAGE}
  95 + * 字典:{@link DictTypeConstants#AI_WRITE_LANGUAGE}
88 96 */
89 97 private Integer language;
90 98  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java
... ... @@ -5,10 +5,14 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 6 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
7 7 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
  8 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
8 9 import org.apache.ibatis.annotations.Mapper;
9 10  
  11 +import java.util.Collection;
  12 +import java.util.List;
  13 +
10 14 /**
11   - * AI 知识库-文档 Mapper
  15 + * AI 知识库文档 Mapper
12 16 *
13 17 * @author xiaoxin
14 18 */
... ... @@ -17,8 +21,19 @@ public interface AiKnowledgeDocumentMapper extends BaseMapperX&lt;AiKnowledgeDocume
17 21  
18 22 default PageResult<AiKnowledgeDocumentDO> selectPage(AiKnowledgeDocumentPageReqVO reqVO) {
19 23 return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeDocumentDO>()
  24 + .eqIfPresent(AiKnowledgeDocumentDO::getKnowledgeId, reqVO.getKnowledgeId())
20 25 .likeIfPresent(AiKnowledgeDocumentDO::getName, reqVO.getName())
21 26 .orderByDesc(AiKnowledgeDocumentDO::getId));
22 27 }
23 28  
  29 + default void updateRetrievalCountIncr(Collection<Long> ids) {
  30 + update(new LambdaUpdateWrapper<AiKnowledgeDocumentDO>()
  31 + .setSql(" retrieval_count = retrieval_count + 1")
  32 + .in(AiKnowledgeDocumentDO::getId, ids));
  33 + }
  34 +
  35 + default List<AiKnowledgeDocumentDO> selectListByStatus(Integer status) {
  36 + return selectList(AiKnowledgeDocumentDO::getStatus, status);
  37 + }
  38 +
24 39 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java
1 1 package cn.iocoder.yudao.module.ai.dal.mysql.knowledge;
2 2  
3   -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
... ... @@ -8,19 +7,26 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnow
8 7 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
9 8 import org.apache.ibatis.annotations.Mapper;
10 9  
  10 +import java.util.List;
  11 +
11 12 /**
12   - * AI 知识库基础信息 Mapper
  13 + * AI 知识库 Mapper
13 14 *
14 15 * @author xiaoxin
15 16 */
16 17 @Mapper
17 18 public interface AiKnowledgeMapper extends BaseMapperX<AiKnowledgeDO> {
18 19  
19   - default PageResult<AiKnowledgeDO> selectPage(Long userId, AiKnowledgePageReqVO pageReqVO) {
  20 + default PageResult<AiKnowledgeDO> selectPage(AiKnowledgePageReqVO pageReqVO) {
20 21 return selectPage(pageReqVO, new LambdaQueryWrapperX<AiKnowledgeDO>()
21   - .eq(AiKnowledgeDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
22 22 .likeIfPresent(AiKnowledgeDO::getName, pageReqVO.getName())
23   - .and(e -> e.apply("FIND_IN_SET(" + userId + ",visibility_permissions)").or(m -> m.apply("FIND_IN_SET(-1,visibility_permissions)")))
  23 + .eqIfPresent(AiKnowledgeDO::getStatus, pageReqVO.getStatus())
  24 + .betweenIfPresent(AiKnowledgeDO::getCreateTime, pageReqVO.getCreateTime())
24 25 .orderByDesc(AiKnowledgeDO::getId));
25 26 }
  27 +
  28 + default List<AiKnowledgeDO> selectListByStatus(Integer status) {
  29 + return selectList(AiKnowledgeDO::getStatus, status);
  30 + }
  31 +
26 32 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
... ... @@ -3,14 +3,19 @@ package cn.iocoder.yudao.module.ai.dal.mysql.knowledge;
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
6 7 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;
7 9 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
  10 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  11 +import com.github.yulichang.wrapper.MPJLambdaWrapper;
8 12 import org.apache.ibatis.annotations.Mapper;
9 13  
  14 +import java.util.Collection;
10 15 import java.util.List;
11 16  
12 17 /**
13   - * AI 知识库-分片 Mapper
  18 + * AI 知识库分片 Mapper
14 19 *
15 20 * @author xiaoxin
16 21 */
... ... @@ -20,8 +25,8 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX&lt;AiKnowledgeSegment
20 25 default PageResult<AiKnowledgeSegmentDO> selectPage(AiKnowledgeSegmentPageReqVO reqVO) {
21 26 return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()
22 27 .eq(AiKnowledgeSegmentDO::getDocumentId, reqVO.getDocumentId())
  28 + .likeIfPresent(AiKnowledgeSegmentDO::getContent, reqVO.getContent())
23 29 .eqIfPresent(AiKnowledgeSegmentDO::getStatus, reqVO.getStatus())
24   - .likeIfPresent(AiKnowledgeSegmentDO::getContent, reqVO.getKeyword())
25 30 .orderByDesc(AiKnowledgeSegmentDO::getId));
26 31 }
27 32  
... ... @@ -31,4 +36,32 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX&lt;AiKnowledgeSegment
31 36 .orderByDesc(AiKnowledgeSegmentDO::getId));
32 37 }
33 38  
  39 + default List<AiKnowledgeSegmentDO> selectListByDocumentId(Long documentId) {
  40 + return selectList(new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()
  41 + .eq(AiKnowledgeSegmentDO::getDocumentId, documentId)
  42 + .orderByDesc(AiKnowledgeSegmentDO::getId));
  43 + }
  44 +
  45 + default List<AiKnowledgeSegmentDO> selectListByKnowledgeIdAndStatus(Long knowledgeId, Integer status) {
  46 + return selectList(AiKnowledgeSegmentDO::getKnowledgeId, knowledgeId,
  47 + AiKnowledgeSegmentDO::getStatus, status);
  48 + }
  49 +
  50 + default List<AiKnowledgeSegmentProcessRespVO> selectProcessList(Collection<Long> documentIds) {
  51 + MPJLambdaWrapper<AiKnowledgeSegmentDO> wrapper = new MPJLambdaWrapperX<AiKnowledgeSegmentDO>()
  52 + .selectAs(AiKnowledgeSegmentDO::getDocumentId, AiKnowledgeSegmentProcessRespVO::getDocumentId)
  53 + .selectCount(AiKnowledgeSegmentDO::getId, "count")
  54 + .select("COUNT(CASE WHEN vector_id > '" + AiKnowledgeSegmentDO.VECTOR_ID_EMPTY
  55 + + "' THEN 1 ELSE NULL END) AS embeddingCount")
  56 + .in(AiKnowledgeSegmentDO::getDocumentId, documentIds)
  57 + .groupBy(AiKnowledgeSegmentDO::getDocumentId);
  58 + return selectJoinList(AiKnowledgeSegmentProcessRespVO.class, wrapper);
  59 + }
  60 +
  61 + default void updateRetrievalCountIncrByIds(List<Long> ids) {
  62 + update(new LambdaUpdateWrapper<AiKnowledgeSegmentDO>()
  63 + .setSql(" retrieval_count = retrieval_count + 1")
  64 + .in(AiKnowledgeSegmentDO::getId, ids));
  65 + }
  66 +
34 67 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.mysql.model;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
  8 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +import javax.annotation.Nullable;
  12 +import java.util.List;
  13 +
  14 +/**
  15 + * API 模型 Mapper
  16 + *
  17 + * @author fansili
  18 + */
  19 +@Mapper
  20 +public interface AiChatMapper extends BaseMapperX<AiModelDO> {
  21 +
  22 + default AiModelDO selectFirstByStatus(Integer type, Integer status) {
  23 + return selectOne(new QueryWrapperX<AiModelDO>()
  24 + .eq("type", type)
  25 + .eq("status", status)
  26 + .limitN(1)
  27 + .orderByAsc("sort"));
  28 + }
  29 +
  30 + default PageResult<AiModelDO> selectPage(AiModelPageReqVO reqVO) {
  31 + return selectPage(reqVO, new LambdaQueryWrapperX<AiModelDO>()
  32 + .likeIfPresent(AiModelDO::getName, reqVO.getName())
  33 + .eqIfPresent(AiModelDO::getModel, reqVO.getModel())
  34 + .eqIfPresent(AiModelDO::getPlatform, reqVO.getPlatform())
  35 + .orderByAsc(AiModelDO::getSort));
  36 + }
  37 +
  38 + default List<AiModelDO> selectListByStatusAndType(Integer status, Integer type,
  39 + @Nullable String platform) {
  40 + return selectList(new LambdaQueryWrapperX<AiModelDO>()
  41 + .eq(AiModelDO::getStatus, status)
  42 + .eq(AiModelDO::getType, type)
  43 + .eqIfPresent(AiModelDO::getPlatform, platform)
  44 + .orderByAsc(AiModelDO::getSort));
  45 + }
  46 +
  47 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatModelMapper.java deleted
1   -package cn.iocoder.yudao.module.ai.dal.mysql.model;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5   -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6   -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
7   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
8   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
9   -import org.apache.ibatis.annotations.Mapper;
10   -
11   -import java.util.Collection;
12   -import java.util.List;
13   -
14   -/**
15   - * API 聊天模型 Mapper
16   - *
17   - * @author fansili
18   - */
19   -@Mapper
20   -public interface AiChatModelMapper extends BaseMapperX<AiChatModelDO> {
21   -
22   - default AiChatModelDO selectFirstByStatus(Integer status) {
23   - return selectOne(new QueryWrapperX<AiChatModelDO>()
24   - .eq("status", status)
25   - .limitN(1)
26   - .orderByAsc("sort"));
27   - }
28   -
29   - default PageResult<AiChatModelDO> selectPage(AiChatModelPageReqVO reqVO) {
30   - return selectPage(reqVO, new LambdaQueryWrapperX<AiChatModelDO>()
31   - .likeIfPresent(AiChatModelDO::getName, reqVO.getName())
32   - .eqIfPresent(AiChatModelDO::getModel, reqVO.getModel())
33   - .eqIfPresent(AiChatModelDO::getPlatform, reqVO.getPlatform())
34   - .orderByAsc(AiChatModelDO::getSort));
35   - }
36   -
37   - default List<AiChatModelDO> selectList(Integer status) {
38   - return selectList(new LambdaQueryWrapperX<AiChatModelDO>()
39   - .eq(AiChatModelDO::getStatus, status)
40   - .orderByAsc(AiChatModelDO::getSort));
41   - }
42   -
43   -}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiToolMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.mysql.model;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * AI 工具 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface AiToolMapper extends BaseMapperX<AiToolDO> {
  19 +
  20 + default PageResult<AiToolDO> selectPage(AiToolPageReqVO reqVO) {
  21 + return selectPage(reqVO, new LambdaQueryWrapperX<AiToolDO>()
  22 + .likeIfPresent(AiToolDO::getName, reqVO.getName())
  23 + .eqIfPresent(AiToolDO::getDescription, reqVO.getDescription())
  24 + .eqIfPresent(AiToolDO::getStatus, reqVO.getStatus())
  25 + .betweenIfPresent(AiToolDO::getCreateTime, reqVO.getCreateTime())
  26 + .orderByDesc(AiToolDO::getId));
  27 + }
  28 +
  29 + default List<AiToolDO> selectListByStatus(Integer status) {
  30 + return selectList(new LambdaQueryWrapperX<AiToolDO>()
  31 + .eq(AiToolDO::getStatus, status)
  32 + .orderByDesc(AiToolDO::getId));
  33 + }
  34 +
  35 +}
0 36 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/framework/web/package-info.java
1 1 /**
2   - * crm 模块的 web 拓展封装
  2 + * ai 模块的 web 拓展封装
3 3 */
4   -package cn.iocoder.yudao.module.crm.framework.web;
  4 +package cn.iocoder.yudao.module.ai.framework.web;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java
... ... @@ -4,17 +4,18 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.lang.Assert;
5 5 import cn.hutool.core.util.ObjUtil;
6 6 import cn.hutool.core.util.ObjectUtil;
  7 +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum;
7 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 10 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
10 11 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
11 12 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
12 13 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
13   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  14 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
14 15 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
15 16 import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper;
16 17 import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
17   -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
  18 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
18 19 import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
19 20 import jakarta.annotation.Resource;
20 21 import lombok.extern.slf4j.Slf4j;
... ... @@ -44,7 +45,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
44 45 private AiChatConversationMapper chatConversationMapper;
45 46  
46 47 @Resource
47   - private AiChatModelService chatModalService;
  48 + private AiModelService modalService;
48 49 @Resource
49 50 private AiChatRoleService chatRoleService;
50 51 @Resource
... ... @@ -54,9 +55,9 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
54 55 public Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId) {
55 56 // 1.1 获得 AiChatRoleDO 聊天角色
56 57 AiChatRoleDO role = createReqVO.getRoleId() != null ? chatRoleService.validateChatRole(createReqVO.getRoleId()) : null;
57   - // 1.2 获得 AiChatModelDO 聊天模型
58   - AiChatModelDO model = role != null && role.getModelId() != null ? chatModalService.validateChatModel(role.getModelId())
59   - : chatModalService.getRequiredDefaultChatModel();
  58 + // 1.2 获得 AiModelDO 聊天模型
  59 + AiModelDO model = role != null && role.getModelId() != null ? modalService.validateModel(role.getModelId())
  60 + : modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());
60 61 Assert.notNull(model, "必须找到默认模型");
61 62 validateChatModel(model);
62 63  
... ... @@ -67,7 +68,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
67 68  
68 69 // 2. 创建 AiChatConversationDO 聊天对话
69 70 AiChatConversationDO conversation = new AiChatConversationDO().setUserId(userId).setPinned(false)
70   - .setModelId(model.getId()).setModel(model.getModel()).setKnowledgeId(createReqVO.getKnowledgeId())
  71 + .setModelId(model.getId()).setModel(model.getModel())
71 72 .setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts());
72 73 if (role != null) {
73 74 conversation.setTitle(role.getName()).setRoleId(role.getId()).setSystemMessage(role.getSystemMessage());
... ... @@ -86,9 +87,9 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
86 87 throw exception(CHAT_CONVERSATION_NOT_EXISTS);
87 88 }
88 89 // 1.2 校验模型是否存在(修改模型的情况)
89   - AiChatModelDO model = null;
  90 + AiModelDO model = null;
90 91 if (updateReqVO.getModelId() != null) {
91   - model = chatModalService.validateChatModel(updateReqVO.getModelId());
  92 + model = modalService.validateModel(updateReqVO.getModelId());
92 93 }
93 94  
94 95 // 1.3 校验知识库是否存在
... ... @@ -139,10 +140,11 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
139 140 chatConversationMapper.deleteById(id);
140 141 }
141 142  
142   - private void validateChatModel(AiChatModelDO model) {
  143 + private void validateChatModel(AiModelDO model) {
143 144 if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) {
144 145 return;
145 146 }
  147 + Assert.equals(model.getType(), AiModelTypeEnum.CHAT.getType(), "模型类型不正确:" + model);
146 148 throw exception(CHAT_CONVERSATION_MODEL_ERROR);
147 149 }
148 150  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
... ... @@ -10,19 +10,24 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
10 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
11 11 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
12 12 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
  13 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
13 14 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
14 15 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
15   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO;
16 16 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
17 17 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
18   -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
19   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  18 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
  19 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  20 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
  21 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
20 22 import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper;
21   -import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;
22 23 import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
  24 +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
23 25 import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
24   -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
25   -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
  26 +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
  27 +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
  28 +import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
  29 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
  30 +import cn.iocoder.yudao.module.ai.service.model.AiToolService;
26 31 import jakarta.annotation.Resource;
27 32 import lombok.extern.slf4j.Slf4j;
28 33 import org.springframework.ai.chat.messages.Message;
... ... @@ -34,18 +39,19 @@ import org.springframework.ai.chat.model.ChatResponse;
34 39 import org.springframework.ai.chat.model.StreamingChatModel;
35 40 import org.springframework.ai.chat.prompt.ChatOptions;
36 41 import org.springframework.ai.chat.prompt.Prompt;
37   -import org.springframework.ai.chat.prompt.PromptTemplate;
38 42 import org.springframework.stereotype.Service;
39 43 import org.springframework.transaction.annotation.Transactional;
40 44 import reactor.core.publisher.Flux;
41 45  
42 46 import java.time.LocalDateTime;
43 47 import java.util.*;
  48 +import java.util.stream.Collectors;
44 49  
45 50 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
46 51 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
47 52 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
48 53 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  54 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
49 55 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;
50 56 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_NOT_EXIST;
51 57  
... ... @@ -58,138 +64,199 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_N
58 64 @Slf4j
59 65 public class AiChatMessageServiceImpl implements AiChatMessageService {
60 66  
  67 + /**
  68 + * 知识库转 {@link UserMessage} 的内容模版
  69 + */
  70 + private static final String KNOWLEDGE_USER_MESSAGE_TEMPLATE = "使用 <Reference></Reference> 标记中的内容作为本次对话的参考:\n\n" +
  71 + "%s\n\n" + // 多个 <Reference></Reference> 的拼接
  72 + "回答要求:\n- 避免提及你是从 <Reference></Reference> 获取的知识。";
  73 +
61 74 @Resource
62 75 private AiChatMessageMapper chatMessageMapper;
63 76  
64 77 @Resource
65 78 private AiChatConversationService chatConversationService;
66 79 @Resource
67   - private AiChatModelService chatModalService;
  80 + private AiChatRoleService chatRoleService;
68 81 @Resource
69   - private AiApiKeyService apiKeyService;
  82 + private AiModelService modalService;
70 83 @Resource
71 84 private AiKnowledgeSegmentService knowledgeSegmentService;
  85 + @Resource
  86 + private AiKnowledgeDocumentService knowledgeDocumentService;
  87 + @Resource
  88 + private AiToolService toolService;
72 89  
73 90 @Transactional(rollbackFor = Exception.class)
74 91 public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) {
75 92 // 1.1 校验对话存在
76   - AiChatConversationDO conversation = chatConversationService.validateChatConversationExists(sendReqVO.getConversationId());
  93 + AiChatConversationDO conversation = chatConversationService
  94 + .validateChatConversationExists(sendReqVO.getConversationId());
77 95 if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
78 96 throw exception(CHAT_CONVERSATION_NOT_EXISTS);
79 97 }
80 98 List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());
81 99 // 1.2 校验模型
82   - AiChatModelDO model = chatModalService.validateChatModel(conversation.getModelId());
83   - ChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
  100 + AiModelDO model = modalService.validateModel(conversation.getModelId());
  101 + ChatModel chatModel = modalService.getChatModel(model.getId());
  102 +
  103 + // 2. 知识库找回
  104 + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(),
  105 + conversation);
84 106  
85   - // 2. 插入 user 发送消息
  107 + // 3. 插入 user 发送消息
86 108 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
87   - userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext());
  109 + userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(),
  110 + null);
88 111  
89 112 // 3.1 插入 assistant 接收消息
90 113 AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
91   - userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext());
92   -
93   - // 3.2 召回段落
94   - List<AiKnowledgeSegmentDO> segmentList = recallSegment(sendReqVO.getContent(), conversation.getKnowledgeId());
  114 + userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(),
  115 + knowledgeSegments);
95 116  
96   - // 3.3 创建 chat 需要的 Prompt
97   - Prompt prompt = buildPrompt(conversation, historyMessages, segmentList, model, sendReqVO);
  117 + // 3.2 创建 chat 需要的 Prompt
  118 + Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO);
98 119 ChatResponse chatResponse = chatModel.call(prompt);
99 120  
100   - // 3.4 段式返回
101   - String newContent = chatResponse.getResult().getOutput().getContent();
102   - chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId)).setContent(newContent));
103   - return new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
104   - .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent));
  121 + // 3.3 更新响应内容
  122 + String newContent = chatResponse.getResult().getOutput().getText();
  123 + chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(newContent));
  124 + // 3.4 响应结果
  125 + List<AiChatMessageRespVO.KnowledgeSegment> segments = BeanUtils.toBean(knowledgeSegments,
  126 + AiChatMessageRespVO.KnowledgeSegment.class,
  127 + segment -> {
  128 + AiKnowledgeDocumentDO document = knowledgeDocumentService
  129 + .getKnowledgeDocument(segment.getDocumentId());
  130 + segment.setDocumentName(document != null ? document.getName() : null);
  131 + });
  132 + return new AiChatMessageSendRespVO()
  133 + .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
  134 + .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
  135 + .setContent(newContent).setSegments(segments));
105 136 }
106 137  
107 138 @Override
108   - public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId) {
  139 + public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO,
  140 + Long userId) {
109 141 // 1.1 校验对话存在
110   - AiChatConversationDO conversation = chatConversationService.validateChatConversationExists(sendReqVO.getConversationId());
  142 + AiChatConversationDO conversation = chatConversationService
  143 + .validateChatConversationExists(sendReqVO.getConversationId());
111 144 if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
112 145 throw exception(CHAT_CONVERSATION_NOT_EXISTS);
113 146 }
114 147 List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());
115 148 // 1.2 校验模型
116   - AiChatModelDO model = chatModalService.validateChatModel(conversation.getModelId());
117   - StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
  149 + AiModelDO model = modalService.validateModel(conversation.getModelId());
  150 + StreamingChatModel chatModel = modalService.getChatModel(model.getId());
118 151  
119   - // 2. 插入 user 发送消息
  152 + // 2. 知识库找回
  153 + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(),
  154 + conversation);
  155 +
  156 + // 3. 插入 user 发送消息
120 157 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
121   - userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext());
  158 + userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(),
  159 + null);
122 160  
123   - // 3.1 插入 assistant 接收消息
  161 + // 4.1 插入 assistant 接收消息
124 162 AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
125   - userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext());
126   -
127   -
128   - // 3.2 召回段落
129   - List<AiKnowledgeSegmentDO> segmentList = recallSegment(sendReqVO.getContent(), conversation.getKnowledgeId());
  163 + userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(),
  164 + knowledgeSegments);
130 165  
131   - // 3.3 构建 Prompt,并进行调用
132   - Prompt prompt = buildPrompt(conversation, historyMessages, segmentList, model, sendReqVO);
  166 + // 4.2 构建 Prompt,并进行调用
  167 + Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO);
133 168 Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
134 169  
135   - // 3.4 流式返回
136   - // TODO 注意:Schedulers.immediate() 目的是,避免默认 Schedulers.parallel() 并发消费 chunk 导致 SSE 响应前端会乱序问题
  170 + // 4.3 流式返回
137 171 StringBuffer contentBuffer = new StringBuffer();
138 172 return streamResponse.map(chunk -> {
139   - String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null;
  173 + // 处理知识库的返回,只有首次才有
  174 + List<AiChatMessageRespVO.KnowledgeSegment> segments = null;
  175 + if (StrUtil.isEmpty(contentBuffer)) {
  176 + segments = BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class,
  177 + segment -> TenantUtils.executeIgnore(() -> {
  178 + AiKnowledgeDocumentDO document = knowledgeDocumentService
  179 + .getKnowledgeDocument(segment.getDocumentId());
  180 + segment.setDocumentName(document != null ? document.getName() : null);
  181 + }));
  182 + }
  183 + // 响应结果
  184 + String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
140 185 newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
141 186 contentBuffer.append(newContent);
142   - // 响应结果
143   - return success(new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
144   - .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent)));
  187 + return success(new AiChatMessageSendRespVO()
  188 + .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
  189 + .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
  190 + .setContent(newContent).setSegments(segments)));
145 191 }).doOnComplete(() -> {
146 192 // 忽略租户,因为 Flux 异步无法透传租户
147   - TenantUtils.executeIgnore(() ->
148   - chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId))
149   - .setContent(contentBuffer.toString())));
  193 + TenantUtils.executeIgnore(() -> chatMessageMapper.updateById(
  194 + new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString())));
150 195 }).doOnError(throwable -> {
151 196 log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable);
152 197 // 忽略租户,因为 Flux 异步无法透传租户
153   - TenantUtils.executeIgnore(() ->
154   - chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage())));
  198 + TenantUtils.executeIgnore(() -> chatMessageMapper.updateById(
  199 + new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage())));
155 200 }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR)));
156 201 }
157 202  
158   - private List<AiKnowledgeSegmentDO> recallSegment(String content, Long knowledgeId) {
159   - if (Objects.isNull(knowledgeId)) {
  203 + private List<AiKnowledgeSegmentSearchRespBO> recallKnowledgeSegment(String content,
  204 + AiChatConversationDO conversation) {
  205 + // 1. 查询聊天角色
  206 + if (conversation == null || conversation.getRoleId() == null) {
  207 + return Collections.emptyList();
  208 + }
  209 + AiChatRoleDO role = chatRoleService.getChatRole(conversation.getRoleId());
  210 + if (role == null || CollUtil.isEmpty(role.getKnowledgeIds())) {
160 211 return Collections.emptyList();
161 212 }
162   - return knowledgeSegmentService.similaritySearch(new AiKnowledgeSegmentSearchReqVO().setKnowledgeId(knowledgeId).setContent(content));
163   - }
164   -
165   - private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,List<AiKnowledgeSegmentDO> segmentList,
166   - AiChatModelDO model, AiChatMessageSendReqVO sendReqVO) {
167   - // 1. 构建 Prompt Message 列表
168   - List<Message> chatMessages = new ArrayList<>();
169 213  
170   - // 1.1 召回内容消息构建
171   - if (CollUtil.isNotEmpty(segmentList)) {
172   - PromptTemplate promptTemplate = new PromptTemplate(AiChatRoleEnum.AI_KNOWLEDGE_ROLE.getSystemMessage());
173   - StringBuilder infoBuilder = StrUtil.builder();
174   - segmentList.forEach(segment -> infoBuilder.append(System.lineSeparator()).append(segment.getContent()));
175   - Message message = promptTemplate.createMessage(Map.of("info", infoBuilder.toString()));
176   - chatMessages.add(message);
  214 + // 2. 遍历找回
  215 + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = new ArrayList<>();
  216 + for (Long knowledgeId : role.getKnowledgeIds()) {
  217 + knowledgeSegments.addAll(knowledgeSegmentService.searchKnowledgeSegment(new AiKnowledgeSegmentSearchReqBO()
  218 + .setKnowledgeId(knowledgeId).setContent(content)));
177 219 }
  220 + return knowledgeSegments;
  221 + }
178 222  
179   - // 1.2 system context 角色设定
  223 + private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,
  224 + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments,
  225 + AiModelDO model, AiChatMessageSendReqVO sendReqVO) {
  226 + List<Message> chatMessages = new ArrayList<>();
  227 + // 1.1 System Context 角色设定
180 228 if (StrUtil.isNotBlank(conversation.getSystemMessage())) {
181 229 chatMessages.add(new SystemMessage(conversation.getSystemMessage()));
182 230 }
183   - // 1.3 history message 历史消息
  231 +
  232 + // 1.2 历史 history message 历史消息
184 233 List<AiChatMessageDO> contextMessages = filterContextMessages(messages, conversation, sendReqVO);
185   - contextMessages.forEach(message -> chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent())));
186   - // 1.4 user message 新发送消息
  234 + contextMessages
  235 + .forEach(message -> chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent())));
  236 +
  237 + // 1.3 当前 user message 新发送消息
187 238 chatMessages.add(new UserMessage(sendReqVO.getContent()));
188 239  
189   - // 2. 构建 ChatOptions 对象
  240 + // 1.4 知识库,通过 UserMessage 实现
  241 + if (CollUtil.isNotEmpty(knowledgeSegments)) {
  242 + String reference = knowledgeSegments.stream()
  243 + .map(segment -> "<Reference>" + segment.getContent() + "</Reference>")
  244 + .collect(Collectors.joining("\n\n"));
  245 + chatMessages.add(new UserMessage(String.format(KNOWLEDGE_USER_MESSAGE_TEMPLATE, reference)));
  246 + }
  247 +
  248 + // 2.1 查询 tool 工具
  249 + Set<String> toolNames = null;
  250 + if (conversation.getRoleId() != null) {
  251 + AiChatRoleDO chatRole = chatRoleService.getChatRole(conversation.getRoleId());
  252 + if (chatRole != null && CollUtil.isNotEmpty(chatRole.getToolIds())) {
  253 + toolNames = convertSet(toolService.getToolList(chatRole.getToolIds()), AiToolDO::getName);
  254 + }
  255 + }
  256 + // 2.2 构建 ChatOptions 对象
190 257 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
191 258 ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(),
192   - conversation.getTemperature(), conversation.getMaxTokens());
  259 + conversation.getTemperature(), conversation.getMaxTokens(), toolNames);
193 260 return new Prompt(chatMessages, chatOptions);
194 261 }
195 262  
... ... @@ -204,8 +271,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
204 271 * @return 消息上下文
205 272 */
206 273 private List<AiChatMessageDO> filterContextMessages(List<AiChatMessageDO> messages,
207   - AiChatConversationDO conversation,
208   - AiChatMessageSendReqVO sendReqVO) {
  274 + AiChatConversationDO conversation,
  275 + AiChatMessageSendReqVO sendReqVO) {
209 276 if (conversation.getMaxContexts() == null || ObjUtil.notEqual(sendReqVO.getUseContext(), Boolean.TRUE)) {
210 277 return Collections.emptyList();
211 278 }
... ... @@ -216,7 +283,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
216 283 continue;
217 284 }
218 285 AiChatMessageDO userMessage = CollUtil.get(messages, i - 1);
219   - if (userMessage == null || ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId())
  286 + if (userMessage == null
  287 + || ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId())
220 288 || StrUtil.isEmpty(assistantMessage.getContent())) {
221 289 continue;
222 290 }
... ... @@ -233,11 +301,13 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
233 301 }
234 302  
235 303 private AiChatMessageDO createChatMessage(Long conversationId, Long replyId,
236   - AiChatModelDO model, Long userId, Long roleId,
237   - MessageType messageType, String content, Boolean useContext) {
  304 + AiModelDO model, Long userId, Long roleId,
  305 + MessageType messageType, String content, Boolean useContext,
  306 + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments) {
238 307 AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId)
239 308 .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId)
240   - .setType(messageType.getValue()).setContent(content).setUseContext(useContext);
  309 + .setType(messageType.getValue()).setContent(content).setUseContext(useContext)
  310 + .setSegmentIds(convertList(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getId));
241 311 message.setCreateTime(LocalDateTime.now());
242 312 chatMessageMapper.insert(message);
243 313 return message;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.service.image;
3 3 import cn.hutool.core.bean.BeanUtil;
4 4 import cn.hutool.core.codec.Base64;
5 5 import cn.hutool.core.collection.CollUtil;
  6 +import cn.hutool.core.lang.Assert;
6 7 import cn.hutool.core.map.MapUtil;
7 8 import cn.hutool.core.util.ObjUtil;
8 9 import cn.hutool.core.util.StrUtil;
... ... @@ -12,15 +13,19 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
12 13 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
13 14 import cn.iocoder.yudao.framework.common.pojo.PageResult;
14 15 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
15   -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.*;
  16 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
  17 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageReqVO;
  18 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePublicPageReqVO;
  19 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageUpdateReqVO;
16 20 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;
17 21 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;
18 22 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
  23 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
19 24 import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper;
20 25 import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
21   -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
  26 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
22 27 import cn.iocoder.yudao.module.infra.api.file.FileApi;
23   -import com.alibaba.cloud.ai.tongyi.image.TongYiImagesOptions;
  28 +import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;
24 29 import jakarta.annotation.Resource;
25 30 import lombok.extern.slf4j.Slf4j;
26 31 import org.springframework.ai.image.ImageModel;
... ... @@ -55,13 +60,13 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
55 60 public class AiImageServiceImpl implements AiImageService {
56 61  
57 62 @Resource
58   - private AiImageMapper imageMapper;
  63 + private AiModelService modelService;
59 64  
60 65 @Resource
61   - private FileApi fileApi;
  66 + private AiImageMapper imageMapper;
62 67  
63 68 @Resource
64   - private AiApiKeyService apiKeyService;
  69 + private FileApi fileApi;
65 70  
66 71 @Override
67 72 public PageResult<AiImageDO> getImagePageMy(Long userId, AiImagePageReqVO pageReqVO) {
... ... @@ -88,23 +93,31 @@ public class AiImageServiceImpl implements AiImageService {
88 93  
89 94 @Override
90 95 public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) {
91   - // 1. 保存数据库
92   - AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)
93   - .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
  96 + // 1. 校验模型
  97 + AiModelDO model = modelService.validateModel(drawReqVO.getModelId());
  98 +
  99 + // 2. 保存数据库
  100 + AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId)
  101 + .setPlatform(model.getPlatform()).setModelId(model.getId()).setModel(model.getModel())
  102 + .setPublicStatus(false).setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
94 103 imageMapper.insert(image);
95   - // 2. 异步绘制,后续前端通过返回的 id 进行轮询结果
96   - getSelf().executeDrawImage(image, drawReqVO);
  104 +
  105 + // 3. 异步绘制,后续前端通过返回的 id 进行轮询结果
  106 + getSelf().executeDrawImage(image, drawReqVO, model);
97 107 return image.getId();
98 108 }
99 109  
100 110 @Async
101   - public void executeDrawImage(AiImageDO image, AiImageDrawReqVO req) {
  111 + public void executeDrawImage(AiImageDO image, AiImageDrawReqVO reqVO, AiModelDO model) {
102 112 try {
103 113 // 1.1 构建请求
104   - ImageOptions request = buildImageOptions(req);
  114 + ImageOptions request = buildImageOptions(reqVO, model);
105 115 // 1.2 执行请求
106   - ImageModel imageModel = apiKeyService.getImageModel(AiPlatformEnum.validatePlatform(req.getPlatform()));
107   - ImageResponse response = imageModel.call(new ImagePrompt(req.getPrompt(), request));
  116 + ImageModel imageModel = modelService.getImageModel(model.getId());
  117 + ImageResponse response = imageModel.call(new ImagePrompt(reqVO.getPrompt(), request));
  118 + if (response.getResult() == null) {
  119 + throw new IllegalArgumentException("生成结果为空");
  120 + }
108 121  
109 122 // 2. 上传到文件服务
110 123 String b64Json = response.getResult().getOutput().getB64Json();
... ... @@ -116,49 +129,49 @@ public class AiImageServiceImpl implements AiImageService {
116 129 imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.SUCCESS.getStatus())
117 130 .setPicUrl(filePath).setFinishTime(LocalDateTime.now()));
118 131 } catch (Exception ex) {
119   - log.error("[doDall][image({}) 生成异常]", image, ex);
  132 + log.error("[executeDrawImage][image({}) 生成异常]", image, ex);
120 133 imageMapper.updateById(new AiImageDO().setId(image.getId())
121 134 .setStatus(AiImageStatusEnum.FAIL.getStatus())
122 135 .setErrorMessage(ex.getMessage()).setFinishTime(LocalDateTime.now()));
123 136 }
124 137 }
125 138  
126   - private static ImageOptions buildImageOptions(AiImageDrawReqVO draw) {
127   - if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) {
  139 + private static ImageOptions buildImageOptions(AiImageDrawReqVO draw, AiModelDO model) {
  140 + if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) {
128 141 // https://platform.openai.com/docs/api-reference/images/create
129   - return OpenAiImageOptions.builder().withModel(draw.getModel())
  142 + return OpenAiImageOptions.builder().withModel(model.getModel())
130 143 .withHeight(draw.getHeight()).withWidth(draw.getWidth())
131 144 .withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格
132 145 .withResponseFormat("b64_json")
133 146 .build();
134   - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) {
  147 + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) {
135 148 // https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage
136 149 // https://platform.stability.ai/docs/api-reference#tag/Text-to-Image/operation/textToImage
137   - return StabilityAiImageOptions.builder().withModel(draw.getModel())
138   - .withHeight(draw.getHeight()).withWidth(draw.getWidth())
139   - .withSeed(Long.valueOf(draw.getOptions().get("seed")))
140   - .withCfgScale(Float.valueOf(draw.getOptions().get("scale")))
141   - .withSteps(Integer.valueOf(draw.getOptions().get("steps")))
142   - .withSampler(String.valueOf(draw.getOptions().get("sampler")))
143   - .withStylePreset(String.valueOf(draw.getOptions().get("stylePreset")))
144   - .withClipGuidancePreset(String.valueOf(draw.getOptions().get("clipGuidancePreset")))
  150 + return StabilityAiImageOptions.builder().model(model.getModel())
  151 + .height(draw.getHeight()).width(draw.getWidth())
  152 + .seed(Long.valueOf(draw.getOptions().get("seed")))
  153 + .cfgScale(Float.valueOf(draw.getOptions().get("scale")))
  154 + .steps(Integer.valueOf(draw.getOptions().get("steps")))
  155 + .sampler(String.valueOf(draw.getOptions().get("sampler")))
  156 + .stylePreset(String.valueOf(draw.getOptions().get("stylePreset")))
  157 + .clipGuidancePreset(String.valueOf(draw.getOptions().get("clipGuidancePreset")))
145 158 .build();
146   - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) {
147   - return TongYiImagesOptions.builder()
148   - .withModel(draw.getModel()).withN(1)
  159 + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) {
  160 + return DashScopeImageOptions.builder()
  161 + .withModel(model.getModel()).withN(1)
149 162 .withHeight(draw.getHeight()).withWidth(draw.getWidth())
150 163 .build();
151   - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) {
  164 + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) {
152 165 return QianFanImageOptions.builder()
153   - .withModel(draw.getModel()).withN(1)
154   - .withHeight(draw.getHeight()).withWidth(draw.getWidth())
  166 + .model(model.getModel()).N(1)
  167 + .height(draw.getHeight()).width(draw.getWidth())
155 168 .build();
156   - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) {
  169 + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) {
157 170 return ZhiPuAiImageOptions.builder()
158   - .withModel(draw.getModel())
  171 + .model(model.getModel())
159 172 .build();
160 173 }
161   - throw new IllegalArgumentException("不支持的 AI 平台:" + draw.getPlatform());
  174 + throw new IllegalArgumentException("不支持的 AI 平台:" + model.getPlatform());
162 175 }
163 176  
164 177 @Override
... ... @@ -205,52 +218,56 @@ public class AiImageServiceImpl implements AiImageService {
205 218  
206 219 @Override
207 220 @Transactional(rollbackFor = Exception.class)
208   - public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO reqVO) {
209   - MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi();
210   - // 1. 保存数据库
211   - AiImageDO image = BeanUtils.toBean(reqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)
  221 + public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO drawReqVO) {
  222 + // 1. 校验模型
  223 + AiModelDO model = modelService.validateModel(drawReqVO.getModelId());
  224 + Assert.equals(model.getPlatform(), AiPlatformEnum.MIDJOURNEY.getPlatform(), "平台不匹配");
  225 + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(model.getId());
  226 +
  227 + // 2. 保存数据库
  228 + AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)
212 229 .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus())
213   - .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform());
  230 + .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()).setModelId(model.getId()).setModel(model.getName());
214 231 imageMapper.insert(image);
215 232  
216   - // 2. 调用 Midjourney Proxy 提交任务
217   - List<String> base64Array = StrUtil.isBlank(reqVO.getReferImageUrl()) ? null :
218   - Collections.singletonList("data:image/jpeg;base64,".concat(Base64.encode(HttpUtil.downloadBytes(reqVO.getReferImageUrl()))));
  233 + // 3. 调用 Midjourney Proxy 提交任务
  234 + List<String> base64Array = StrUtil.isBlank(drawReqVO.getReferImageUrl()) ? null :
  235 + Collections.singletonList("data:image/jpeg;base64,".concat(Base64.encode(HttpUtil.downloadBytes(drawReqVO.getReferImageUrl()))));
219 236 MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest(
220   - base64Array, reqVO.getPrompt(),null,
221   - MidjourneyApi.ImagineRequest.buildState(reqVO.getWidth(),
222   - reqVO.getHeight(), reqVO.getVersion(), reqVO.getModel()));
  237 + base64Array, drawReqVO.getPrompt(),null,
  238 + MidjourneyApi.ImagineRequest.buildState(drawReqVO.getWidth(),
  239 + drawReqVO.getHeight(), drawReqVO.getVersion(), model.getModel()));
223 240 MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest);
224 241  
225   - // 3. 情况一【失败】:抛出业务异常
  242 + // 4.1 情况一【失败】:抛出业务异常
226 243 if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) {
227 244 String description = imagineResponse.description().contains("quota_not_enough") ?
228 245 "账户余额不足" : imagineResponse.description();
229 246 throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description);
230 247 }
231 248  
232   - // 4. 情况二【成功】:更新 taskId 和参数
  249 + // 4.2 情况二【成功】:更新 taskId 和参数
233 250 imageMapper.updateById(new AiImageDO().setId(image.getId())
234   - .setTaskId(imagineResponse.result()).setOptions(BeanUtil.beanToMap(reqVO)));
  251 + .setTaskId(imagineResponse.result()).setOptions(BeanUtil.beanToMap(drawReqVO)));
235 252 return image.getId();
236 253 }
237 254  
238 255 @Override
239 256 public Integer midjourneySync() {
240   - MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi();
241 257 // 1.1 获取 Midjourney 平台,状态在 “进行中” 的 image
242   - List<AiImageDO> imageList = imageMapper.selectListByStatusAndPlatform(
  258 + List<AiImageDO> images = imageMapper.selectListByStatusAndPlatform(
243 259 AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform());
244   - if (CollUtil.isEmpty(imageList)) {
  260 + if (CollUtil.isEmpty(images)) {
245 261 return 0;
246 262 }
247 263 // 1.2 调用 Midjourney Proxy 获取任务进展
248   - List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(convertSet(imageList, AiImageDO::getTaskId));
  264 + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(images.get(0).getModelId());
  265 + List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(convertSet(images, AiImageDO::getTaskId));
249 266 Map<String, MidjourneyApi.Notify> taskMap = convertMap(taskList, MidjourneyApi.Notify::id);
250 267  
251 268 // 2. 逐个处理,更新进展
252 269 int count = 0;
253   - for (AiImageDO image : imageList) {
  270 + for (AiImageDO image : images) {
254 271 MidjourneyApi.Notify notify = taskMap.get(image.getTaskId());
255 272 if (notify == null) {
256 273 log.error("[midjourneySync][image({}) 查询不到进展]", image);
... ... @@ -308,12 +325,12 @@ public class AiImageServiceImpl implements AiImageService {
308 325  
309 326 @Override
310 327 public Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO) {
311   - MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi();
312 328 // 1.1 检查 image
313 329 AiImageDO image = validateImageExists(reqVO.getId());
314 330 if (ObjUtil.notEqual(userId, image.getUserId())) {
315 331 throw exception(IMAGE_NOT_EXISTS);
316 332 }
  333 + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(image.getModelId());
317 334 // 1.2 检查 customId
318 335 MidjourneyApi.Button button = CollUtil.findOne(image.getButtons(),
319 336 buttonX -> buttonX.customId().equals(reqVO.getCustomId()));
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java
1 1 package cn.iocoder.yudao.module.ai.service.knowledge;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO;
4 5 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
5 6 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO;
6 8 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
7 9 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
8 10  
  11 +import java.util.Collection;
  12 +import java.util.List;
  13 +import java.util.Map;
  14 +
  15 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  16 +
9 17 /**
10   - * AI 知识库-文档 Service 接口
  18 + * AI 知识库文档 Service 接口
11 19 *
12 20 * @author xiaoxin
13 21 */
14 22 public interface AiKnowledgeDocumentService {
15 23  
16 24 /**
17   - * 创建文档
  25 + * 创建文档(单个)
18 26 *
19 27 * @param createReqVO 文档创建 Request VO
20 28 * @return 文档编号
21 29 */
22 30 Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO);
23 31  
  32 + /**
  33 + * 创建文档(多个)
  34 + *
  35 + * @param createListReqVO 批量创建 Request VO
  36 + * @return 文档编号列表
  37 + */
  38 + List<Long> createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO);
24 39  
25 40 /**
26 41 * 获取文档分页
... ... @@ -31,9 +46,73 @@ public interface AiKnowledgeDocumentService {
31 46 PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO);
32 47  
33 48 /**
  49 + * 获取文档详情
  50 + *
  51 + * @param id 文档编号
  52 + * @return 文档详情
  53 + */
  54 + AiKnowledgeDocumentDO getKnowledgeDocument(Long id);
  55 +
  56 + /**
34 57 * 更新文档
35 58 *
36 59 * @param reqVO 更新信息
37 60 */
38 61 void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO);
  62 +
  63 + /**
  64 + * 更新文档状态
  65 + *
  66 + * @param reqVO 更新状态信息
  67 + */
  68 + void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO);
  69 +
  70 + /**
  71 + * 更新文档检索次数(增加 +1)
  72 + *
  73 + * @param ids 文档编号列表
  74 + */
  75 + void updateKnowledgeDocumentRetrievalCountIncr(Collection<Long> ids);
  76 +
  77 + /**
  78 + * 删除文档
  79 + *
  80 + * @param id 文档编号
  81 + */
  82 + void deleteKnowledgeDocument(Long id);
  83 +
  84 + /**
  85 + * 校验文档是否存在
  86 + *
  87 + * @param id 文档编号
  88 + * @return 文档信息
  89 + */
  90 + AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id);
  91 +
  92 + /**
  93 + * 读取 URL 内容
  94 + *
  95 + * @param url URL
  96 + * @return 内容
  97 + */
  98 + String readUrl(String url);
  99 +
  100 + /**
  101 + * 获取文档列表
  102 + *
  103 + * @param ids 文档编号列表
  104 + * @return 文档列表
  105 + */
  106 + List<AiKnowledgeDocumentDO> getKnowledgeDocumentList(Collection<Long> ids);
  107 +
  108 + /**
  109 + * 获取文档 Map
  110 + *
  111 + * @param ids 文档编号列表
  112 + * @return 文档 Map
  113 + */
  114 + default Map<Long, AiKnowledgeDocumentDO> getKnowledgeDocumentMap(Collection<Long> ids) {
  115 + return convertMap(getKnowledgeDocumentList(ids), AiKnowledgeDocumentDO::getId);
  116 + }
  117 +
39 118 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
1 1 package cn.iocoder.yudao.module.ai.service.knowledge;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
  5 +import cn.hutool.core.util.StrUtil;
4 6 import cn.hutool.http.HttpUtil;
5 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  10 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO;
9 11 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
10 12 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
  13 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO;
11 14 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
12 15 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
13   -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
14 16 import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper;
15   -import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;
16   -import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum;
17 17 import jakarta.annotation.Resource;
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.springframework.ai.document.Document;
20 20 import org.springframework.ai.reader.tika.TikaDocumentReader;
21 21 import org.springframework.ai.tokenizer.TokenCountEstimator;
22   -import org.springframework.ai.transformer.splitter.TokenTextSplitter;
23   -import org.springframework.ai.vectorstore.VectorStore;
  22 +import org.springframework.context.annotation.Lazy;
24 23 import org.springframework.core.io.ByteArrayResource;
25 24 import org.springframework.stereotype.Service;
26 25 import org.springframework.transaction.annotation.Transactional;
27 26  
  27 +import java.util.ArrayList;
  28 +import java.util.Collection;
  29 +import java.util.Collections;
28 30 import java.util.List;
29 31  
30 32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
31   -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_DOCUMENT_NOT_EXISTS;
  33 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  34 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
32 35  
33 36 /**
34 37 * AI 知识库文档 Service 实现类
... ... @@ -40,91 +43,172 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_DOCU
40 43 public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentService {
41 44  
42 45 @Resource
43   - private AiKnowledgeDocumentMapper documentMapper;
44   - @Resource
45   - private AiKnowledgeSegmentMapper segmentMapper;
  46 + private AiKnowledgeDocumentMapper knowledgeDocumentMapper;
46 47  
47 48 @Resource
48 49 private TokenCountEstimator tokenCountEstimator;
  50 +
  51 + @Resource
  52 + private AiKnowledgeSegmentService knowledgeSegmentService;
49 53 @Resource
  54 + @Lazy // 延迟加载,避免循环依赖
50 55 private AiKnowledgeService knowledgeService;
51 56  
52 57 @Override
53   - @Transactional(rollbackFor = Exception.class)
54 58 public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) {
55   - // 0. 校验并获取向量存储实例
56   - VectorStore vectorStore = knowledgeService.getVectorStoreById(createReqVO.getKnowledgeId());
  59 + // 1. 校验参数
  60 + knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId());
57 61  
58   - // 1.1 下载文档
59   - TikaDocumentReader loader = new TikaDocumentReader(downloadFile(createReqVO.getUrl()));
60   - List<Document> documents = loader.get();
61   - Document document = CollUtil.getFirst(documents);
62   - // 1.2 文档记录入库
63   - String content = document.getContent();
  62 + // 2. 下载文档
  63 + String content = readUrl(createReqVO.getUrl());
  64 +
  65 + // 3. 文档记录入库
64 66 AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class)
65   - .setTokens(tokenCountEstimator.estimate(content)).setWordCount(content.length())
66   - .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus());
67   - documentMapper.insert(documentDO);
68   - Long documentId = documentDO.getId();
69   - if (CollUtil.isEmpty(documents)) {
70   - return documentId;
  67 + .setContent(content).setContentLength(content.length()).setTokens(tokenCountEstimator.estimate(content))
  68 + .setStatus(CommonStatusEnum.ENABLE.getStatus());
  69 + knowledgeDocumentMapper.insert(documentDO);
  70 +
  71 + // 4. 文档切片入库(异步)
  72 + knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(documentDO.getId(), content);
  73 + return documentDO.getId();
  74 + }
  75 +
  76 + @Override
  77 + public List<Long> createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO) {
  78 + // 1. 校验参数
  79 + knowledgeService.validateKnowledgeExists(createListReqVO.getKnowledgeId());
  80 +
  81 + // 2. 下载文档
  82 + List<String> contents = convertList(createListReqVO.getList(), document -> readUrl(document.getUrl()));
  83 +
  84 + // 3. 文档记录入库
  85 + List<AiKnowledgeDocumentDO> documentDOs = new ArrayList<>(createListReqVO.getList().size());
  86 + for (int i = 0; i < createListReqVO.getList().size(); i++) {
  87 + AiKnowledgeDocumentCreateListReqVO.Document documentVO = createListReqVO.getList().get(i);
  88 + String content = contents.get(i);
  89 + documentDOs.add(BeanUtils.toBean(documentVO, AiKnowledgeDocumentDO.class)
  90 + .setKnowledgeId(createListReqVO.getKnowledgeId())
  91 + .setContent(content).setContentLength(content.length())
  92 + .setTokens(tokenCountEstimator.estimate(content))
  93 + .setSegmentMaxTokens(createListReqVO.getSegmentMaxTokens())
  94 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
71 95 }
  96 + knowledgeDocumentMapper.insertBatch(documentDOs);
72 97  
73   - // 2 构造文本分段器
74   - TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(createReqVO.getDefaultSegmentTokens(), createReqVO.getMinSegmentWordCount(), createReqVO.getMinChunkLengthToEmbed(),
75   - createReqVO.getMaxNumSegments(), createReqVO.getKeepSeparator());
76   - // 2.1 文档分段
77   - List<Document> segments = tokenTextSplitter.apply(documents);
78   - // 2.2 分段内容入库
79   - List<AiKnowledgeSegmentDO> segmentDOList = CollectionUtils.convertList(segments,
80   - segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId)
81   - .setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId())
82   - .setTokens(tokenCountEstimator.estimate(segment.getContent())).setWordCount(segment.getContent().length())
83   - .setStatus(CommonStatusEnum.ENABLE.getStatus()));
84   - segmentMapper.insertBatch(segmentDOList);
85   -
86   - // 3. 向量化并存储
87   - segments.forEach(segment -> segment.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, createReqVO.getKnowledgeId()));
88   - vectorStore.add(segments);
89   - return documentId;
  98 + // 4. 批量创建文档切片(异步)
  99 + documentDOs.forEach(documentDO -> knowledgeSegmentService
  100 + .createKnowledgeSegmentBySplitContentAsync(documentDO.getId(), documentDO.getContent()));
  101 + return convertList(documentDOs, AiKnowledgeDocumentDO::getId);
90 102 }
91 103  
92 104 @Override
93 105 public PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO) {
94   - return documentMapper.selectPage(pageReqVO);
  106 + return knowledgeDocumentMapper.selectPage(pageReqVO);
  107 + }
  108 +
  109 + @Override
  110 + public AiKnowledgeDocumentDO getKnowledgeDocument(Long id) {
  111 + return knowledgeDocumentMapper.selectById(id);
95 112 }
96 113  
97 114 @Override
98 115 public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) {
99 116 // 1. 校验文档是否存在
100   - validateKnowledgeDocumentExists(reqVO.getId());
  117 + AiKnowledgeDocumentDO oldDocument = validateKnowledgeDocumentExists(reqVO.getId());
  118 +
101 119 // 2. 更新文档
102 120 AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class);
103   - documentMapper.updateById(document);
  121 + knowledgeDocumentMapper.updateById(document);
  122 +
  123 + // 3. 如果处于开启状态,并且最大 tokens 发生变化,则 segment 需要重新索引
  124 + if (CommonStatusEnum.isEnable(oldDocument.getStatus())
  125 + && reqVO.getSegmentMaxTokens() != null
  126 + && ObjUtil.notEqual(reqVO.getSegmentMaxTokens(), oldDocument.getSegmentMaxTokens())) {
  127 + // 删除旧的文档切片
  128 + knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(reqVO.getId());
  129 + // 重新创建文档切片
  130 + knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(reqVO.getId(), oldDocument.getContent());
  131 + }
104 132 }
105 133  
106   - /**
107   - * 校验文档是否存在
108   - *
109   - * @param id 文档编号
110   - * @return 文档信息
111   - */
112   - private AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id) {
113   - AiKnowledgeDocumentDO knowledgeDocument = documentMapper.selectById(id);
  134 + @Override
  135 + public void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO) {
  136 + // 1. 校验存在
  137 + AiKnowledgeDocumentDO document = validateKnowledgeDocumentExists(reqVO.getId());
  138 +
  139 + // 2. 更新状态
  140 + knowledgeDocumentMapper.updateById(new AiKnowledgeDocumentDO()
  141 + .setId(reqVO.getId()).setStatus(reqVO.getStatus()));
  142 +
  143 + // 3. 处理文档切片
  144 + if (CommonStatusEnum.isEnable(reqVO.getStatus())) {
  145 + knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(reqVO.getId(), document.getContent());
  146 + } else {
  147 + knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(reqVO.getId());
  148 + }
  149 + }
  150 +
  151 + @Override
  152 + @Transactional(rollbackFor = Exception.class)
  153 + public void deleteKnowledgeDocument(Long id) {
  154 + // 1. 校验存在
  155 + validateKnowledgeDocumentExists(id);
  156 +
  157 + // 2. 删除
  158 + knowledgeDocumentMapper.deleteById(id);
  159 +
  160 + // 3. 删除对应的段落
  161 + knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(id);
  162 + }
  163 +
  164 + @Override
  165 + public void updateKnowledgeDocumentRetrievalCountIncr(Collection<Long> ids) {
  166 + if (CollUtil.isEmpty(ids)) {
  167 + return;
  168 + }
  169 + knowledgeDocumentMapper.updateRetrievalCountIncr(ids);
  170 + }
  171 +
  172 + @Override
  173 + public AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id) {
  174 + AiKnowledgeDocumentDO knowledgeDocument = knowledgeDocumentMapper.selectById(id);
114 175 if (knowledgeDocument == null) {
115 176 throw exception(KNOWLEDGE_DOCUMENT_NOT_EXISTS);
116 177 }
117 178 return knowledgeDocument;
118 179 }
119 180  
120   - private org.springframework.core.io.Resource downloadFile(String url) {
  181 + @Override
  182 + public String readUrl(String url) {
  183 + // 下载文件
  184 + ByteArrayResource resource;
121 185 try {
122 186 byte[] bytes = HttpUtil.downloadBytes(url);
123   - return new ByteArrayResource(bytes);
  187 + if (bytes.length == 0) {
  188 + throw exception(KNOWLEDGE_DOCUMENT_FILE_EMPTY);
  189 + }
  190 + resource = new ByteArrayResource(bytes);
124 191 } catch (Exception e) {
125   - log.error("[downloadFile][url({}) 下载失败]", url, e);
126   - throw new RuntimeException(e);
  192 + log.error("[readUrl][url({}) 读取失败]", url, e);
  193 + throw exception(KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL);
  194 + }
  195 +
  196 + // 读取文件
  197 + TikaDocumentReader loader = new TikaDocumentReader(resource);
  198 + List<Document> documents = loader.get();
  199 + Document document = CollUtil.getFirst(documents);
  200 + if (document == null || StrUtil.isEmpty(document.getText())) {
  201 + throw exception(KNOWLEDGE_DOCUMENT_FILE_READ_FAIL);
  202 + }
  203 + return document.getText();
  204 + }
  205 +
  206 + @Override
  207 + public List<AiKnowledgeDocumentDO> getKnowledgeDocumentList(Collection<Long> ids) {
  208 + if (CollUtil.isEmpty(ids)) {
  209 + return Collections.emptyList();
127 210 }
  211 + return knowledgeDocumentMapper.selectBatchIds(ids);
128 212 }
129 213  
130 214 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
... ... @@ -2,12 +2,19 @@ package cn.iocoder.yudao.module.ai.service.knowledge;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
5   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO;
6   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSaveReqVO;
7 7 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
8 8 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
  9 +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
  10 +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
  11 +import org.springframework.scheduling.annotation.Async;
9 12  
  13 +import java.util.Collection;
10 14 import java.util.List;
  15 +import java.util.Map;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
11 18  
12 19 /**
13 20 * AI 知识库段落 Service 接口
... ... @@ -17,6 +24,32 @@ import java.util.List;
17 24 public interface AiKnowledgeSegmentService {
18 25  
19 26 /**
  27 + * 获取知识库段落详情
  28 + *
  29 + * @param id 段落编号
  30 + * @return 段落详情
  31 + */
  32 + AiKnowledgeSegmentDO getKnowledgeSegment(Long id);
  33 +
  34 + /**
  35 + * 获取知识库段落列表
  36 + *
  37 + * @param ids 段落编号列表
  38 + * @return 段落列表
  39 + */
  40 + List<AiKnowledgeSegmentDO> getKnowledgeSegmentList(Collection<Long> ids);
  41 +
  42 + /**
  43 + * 获取知识库段落 Map
  44 + *
  45 + * @param ids 段落编号列表
  46 + * @return 段落 Map
  47 + */
  48 + default Map<Long, AiKnowledgeSegmentDO> getKnowledgeSegmentMap(Collection<Long> ids) {
  49 + return convertMap(getKnowledgeSegmentList(ids), AiKnowledgeSegmentDO::getId);
  50 + }
  51 +
  52 + /**
20 53 * 获取段落分页
21 54 *
22 55 * @param pageReqVO 分页查询
... ... @@ -25,11 +58,38 @@ public interface AiKnowledgeSegmentService {
25 58 PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO);
26 59  
27 60 /**
  61 + * 基于 content 内容,切片创建多个段落
  62 + *
  63 + * @param documentId 知识库文档编号
  64 + * @param content 文档内容
  65 + */
  66 + void createKnowledgeSegmentBySplitContent(Long documentId, String content);
  67 +
  68 + /**
  69 + * 【异步】基于 content 内容,切片创建多个段落
  70 + *
  71 + * @param documentId 知识库文档编号
  72 + * @param content 文档内容
  73 + */
  74 + @Async
  75 + default void createKnowledgeSegmentBySplitContentAsync(Long documentId, String content) {
  76 + createKnowledgeSegmentBySplitContent(documentId, content);
  77 + }
  78 +
  79 + /**
  80 + * 创建知识库段落
  81 + *
  82 + * @param createReqVO 创建信息
  83 + * @return 段落编号
  84 + */
  85 + Long createKnowledgeSegment(AiKnowledgeSegmentSaveReqVO createReqVO);
  86 +
  87 + /**
28 88 * 更新段落的内容
29 89 *
30 90 * @param reqVO 更新内容
31 91 */
32   - void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO);
  92 + void updateKnowledgeSegment(AiKnowledgeSegmentSaveReqVO reqVO);
33 93  
34 94 /**
35 95 * 更新段落的状态
... ... @@ -39,11 +99,52 @@ public interface AiKnowledgeSegmentService {
39 99 void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
40 100  
41 101 /**
42   - * 召回段落
  102 + * 重新索引知识库下的所有文档段落
  103 + *
  104 + * @param knowledgeId 知识库编号
  105 + */
  106 + void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId);
  107 +
  108 + /**
  109 + * 【异步】重新索引知识库下的所有文档段落
  110 + *
  111 + * @param knowledgeId 知识库编号
  112 + */
  113 + @Async
  114 + default void reindexByKnowledgeIdAsync(Long knowledgeId) {
  115 + reindexKnowledgeSegmentByKnowledgeId(knowledgeId);
  116 + }
  117 +
  118 + /**
  119 + * 根据文档编号删除段落
  120 + *
  121 + * @param documentId 文档编号
  122 + */
  123 + void deleteKnowledgeSegmentByDocumentId(Long documentId);
  124 +
  125 + /**
  126 + * 搜索知识库段落,并返回结果
  127 + *
  128 + * @param reqBO 搜索请求信息
  129 + * @return 搜索结果段落列表
  130 + */
  131 + List<AiKnowledgeSegmentSearchRespBO> searchKnowledgeSegment(AiKnowledgeSegmentSearchReqBO reqBO);
  132 +
  133 + /**
  134 + * 根据 URL 内容,切片创建多个段落
  135 + *
  136 + * @param url URL 地址
  137 + * @param segmentMaxTokens 段落最大 Token 数
  138 + * @return 切片后的段落列表
  139 + */
  140 + List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens);
  141 +
  142 + /**
  143 + * 获取文档处理进度(多个)
43 144 *
44   - * @param reqVO 召回请求信息
45   - * @return 召回的段落
  145 + * @param documentIds 文档编号列表
  146 + * @return 文档处理列表
46 147 */
47   - List<AiKnowledgeSegmentDO> similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO);
  148 + List<AiKnowledgeSegmentProcessRespVO> getKnowledgeSegmentProcessList(List<Long> documentIds);
48 149  
49 150 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
... ... @@ -2,31 +2,39 @@ package cn.iocoder.yudao.module.ai.service.knowledge;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.collection.ListUtil;
  5 +import cn.hutool.core.util.ObjUtil;
  6 +import cn.hutool.core.util.StrUtil;
5 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 10 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
9   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO;
10   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO;
  11 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;
  12 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSaveReqVO;
11 13 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
12 14 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
  15 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
13 16 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
14   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
15 17 import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;
16   -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
17   -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
  18 +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
  19 +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
  20 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
18 21 import jakarta.annotation.Resource;
19 22 import lombok.extern.slf4j.Slf4j;
20 23 import org.springframework.ai.document.Document;
  24 +import org.springframework.ai.tokenizer.TokenCountEstimator;
  25 +import org.springframework.ai.transformer.splitter.TextSplitter;
  26 +import org.springframework.ai.transformer.splitter.TokenTextSplitter;
21 27 import org.springframework.ai.vectorstore.SearchRequest;
22 28 import org.springframework.ai.vectorstore.VectorStore;
23 29 import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
  30 +import org.springframework.context.annotation.Lazy;
24 31 import org.springframework.stereotype.Service;
25 32  
26   -import java.util.List;
27   -import java.util.Objects;
  33 +import java.util.*;
28 34  
29 35 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  36 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  37 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG;
30 38 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS;
31 39  
32 40 /**
... ... @@ -38,15 +46,28 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGM
38 46 @Slf4j
39 47 public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService {
40 48  
  49 + private static final String VECTOR_STORE_METADATA_KNOWLEDGE_ID = "knowledgeId";
  50 + private static final String VECTOR_STORE_METADATA_DOCUMENT_ID = "documentId";
  51 + private static final String VECTOR_STORE_METADATA_SEGMENT_ID = "segmentId";
  52 +
  53 + private static final Map<String, Class<?>> VECTOR_STORE_METADATA_TYPES = Map.of(
  54 + VECTOR_STORE_METADATA_KNOWLEDGE_ID, String.class,
  55 + VECTOR_STORE_METADATA_DOCUMENT_ID, String.class,
  56 + VECTOR_STORE_METADATA_SEGMENT_ID, String.class);
  57 +
41 58 @Resource
42 59 private AiKnowledgeSegmentMapper segmentMapper;
43 60  
44 61 @Resource
45 62 private AiKnowledgeService knowledgeService;
46 63 @Resource
47   - private AiChatModelService chatModelService;
  64 + @Lazy // 延迟加载,避免循环依赖
  65 + private AiKnowledgeDocumentService knowledgeDocumentService;
48 66 @Resource
49   - private AiApiKeyService apiKeyService;
  67 + private AiModelService modelService;
  68 +
  69 + @Resource
  70 + private TokenCountEstimator tokenCountEstimator;
50 71  
51 72 @Override
52 73 public PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) {
... ... @@ -54,67 +75,198 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
54 75 }
55 76  
56 77 @Override
57   - public void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO) {
  78 + public void createKnowledgeSegmentBySplitContent(Long documentId, String content) {
  79 + // 1. 校验
  80 + AiKnowledgeDocumentDO documentDO = knowledgeDocumentService.validateKnowledgeDocumentExists(documentId);
  81 + AiKnowledgeDO knowledgeDO = knowledgeService.validateKnowledgeExists(documentDO.getKnowledgeId());
  82 + VectorStore vectorStore = getVectorStoreById(knowledgeDO);
  83 +
  84 + // 2. 文档切片
  85 + List<Document> documentSegments = splitContentByToken(content, documentDO.getSegmentMaxTokens());
  86 +
  87 + // 3.1 存储切片
  88 + List<AiKnowledgeSegmentDO> segmentDOs = convertList(documentSegments, segment -> {
  89 + if (StrUtil.isEmpty(segment.getText())) {
  90 + return null;
  91 + }
  92 + return new AiKnowledgeSegmentDO().setKnowledgeId(documentDO.getKnowledgeId()).setDocumentId(documentId)
  93 + .setContent(segment.getText()).setContentLength(segment.getText().length())
  94 + .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY)
  95 + .setTokens(tokenCountEstimator.estimate(segment.getText()))
  96 + .setStatus(CommonStatusEnum.ENABLE.getStatus());
  97 + });
  98 + segmentMapper.insertBatch(segmentDOs);
  99 + // 3.2 切片向量化
  100 + for (int i = 0; i < documentSegments.size(); i++) {
  101 + Document segment = documentSegments.get(i);
  102 + AiKnowledgeSegmentDO segmentDO = segmentDOs.get(i);
  103 + writeVectorStore(vectorStore, segmentDO, segment);
  104 + }
  105 + }
  106 +
  107 + @Override
  108 + public void updateKnowledgeSegment(AiKnowledgeSegmentSaveReqVO reqVO) {
58 109 // 1. 校验
59   - AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId());
  110 + AiKnowledgeSegmentDO oldSegment = validateKnowledgeSegmentExists(reqVO.getId());
  111 +
  112 + // 2. 删除向量
  113 + VectorStore vectorStore = getVectorStoreById(oldSegment.getKnowledgeId());
  114 + deleteVectorStore(vectorStore, oldSegment);
  115 +
  116 + // 3.1 更新切片
  117 + AiKnowledgeSegmentDO newSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class);
  118 + segmentMapper.updateById(newSegment);
  119 + // 3.2 重新向量化,必须开启状态
  120 + if (CommonStatusEnum.isEnable(oldSegment.getStatus())) {
  121 + newSegment.setKnowledgeId(oldSegment.getKnowledgeId()).setDocumentId(oldSegment.getDocumentId());
  122 + writeVectorStore(vectorStore, newSegment, new Document(newSegment.getContent()));
  123 + }
  124 + }
  125 +
  126 + @Override
  127 + public void deleteKnowledgeSegmentByDocumentId(Long documentId) {
  128 + // 1. 查询需要删除的段落
  129 + List<AiKnowledgeSegmentDO> segments = segmentMapper.selectListByDocumentId(documentId);
  130 + if (CollUtil.isEmpty(segments)) {
  131 + return;
  132 + }
60 133  
61   - // 2.1 获取知识库向量实例
62   - VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId());
63   - // 2.2 删除原向量
64   - vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId()));
65   - // 2.3 重新向量化
66   - Document document = new Document(reqVO.getContent());
67   - document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId());
68   - vectorStore.add(List.of(document));
  134 + // 2. 批量删除段落记录
  135 + segmentMapper.deleteByIds(convertList(segments, AiKnowledgeSegmentDO::getId));
69 136  
70   - // 3. 更新段落内容
71   - AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class);
72   - knowledgeSegment.setVectorId(document.getId());
73   - segmentMapper.updateById(knowledgeSegment);
  137 + // 3. 删除向量存储中的段落
  138 + VectorStore vectorStore = getVectorStoreById(segments.get(0).getKnowledgeId());
  139 + vectorStore.delete(convertList(segments, AiKnowledgeSegmentDO::getVectorId));
74 140 }
75 141  
76 142 @Override
77 143 public void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
78   - // 0 校验
79   - AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId());
80   - // 1 获取知识库向量实例
81   - VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId());
82   - AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class);
83   -
84   - if (Objects.equals(reqVO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
85   - // 2.1 启用重新向量化
86   - Document document = new Document(oldKnowledgeSegment.getContent());
87   - document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId());
88   - vectorStore.add(List.of(document));
89   - knowledgeSegment.setVectorId(document.getId());
  144 + // 1. 校验
  145 + AiKnowledgeSegmentDO segment = validateKnowledgeSegmentExists(reqVO.getId());
  146 +
  147 + // 2. 获取知识库向量实例
  148 + VectorStore vectorStore = getVectorStoreById(segment.getKnowledgeId());
  149 +
  150 + // 3. 更新状态
  151 + segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(reqVO.getId()).setStatus(reqVO.getStatus()));
  152 +
  153 + // 4. 更新向量
  154 + if (CommonStatusEnum.isEnable(reqVO.getStatus())) {
  155 + writeVectorStore(vectorStore, segment, new Document(segment.getContent()));
90 156 } else {
91   - // 2.2 禁用删除向量
92   - vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId()));
93   - knowledgeSegment.setVectorId("");
  157 + deleteVectorStore(vectorStore, segment);
  158 + }
  159 + }
  160 +
  161 + @Override
  162 + public void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId) {
  163 + // 1.1 校验知识库存在
  164 + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(knowledgeId);
  165 + // 1.2 获取知识库向量实例
  166 + VectorStore vectorStore = getVectorStoreById(knowledge);
  167 +
  168 + // 2.1 查询知识库下的所有启用状态的段落
  169 + List<AiKnowledgeSegmentDO> segments = segmentMapper.selectListByKnowledgeIdAndStatus(
  170 + knowledgeId, CommonStatusEnum.ENABLE.getStatus());
  171 + if (CollUtil.isEmpty(segments)) {
  172 + return;
94 173 }
95   - // 3 更新段落状态
96   - segmentMapper.updateById(knowledgeSegment);
  174 + // 2.2 遍历所有段落,重新索引
  175 + for (AiKnowledgeSegmentDO segment : segments) {
  176 + // 删除旧的向量
  177 + deleteVectorStore(vectorStore, segment);
  178 + // 重新创建向量
  179 + writeVectorStore(vectorStore, segment, new Document(segment.getContent()));
  180 + }
  181 + log.info("[reindexKnowledgeSegmentByKnowledgeId][知识库({}) 重新索引完成,共处理 {} 个段落]",
  182 + knowledgeId, segments.size());
  183 + }
  184 +
  185 + private void writeVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO, Document segment) {
  186 + // 1. 向量存储
  187 + // 为什么要 toString 呢?因为部分 VectorStore 实现,不支持 Long 类型,例如说 QdrantVectorStore
  188 + segment.getMetadata().put(VECTOR_STORE_METADATA_KNOWLEDGE_ID, segmentDO.getKnowledgeId().toString());
  189 + segment.getMetadata().put(VECTOR_STORE_METADATA_DOCUMENT_ID, segmentDO.getDocumentId().toString());
  190 + segment.getMetadata().put(VECTOR_STORE_METADATA_SEGMENT_ID, segmentDO.getId().toString());
  191 + vectorStore.add(List.of(segment));
  192 +
  193 + // 2. 更新向量 ID
  194 + segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(segmentDO.getId()).setVectorId(segment.getId()));
  195 + }
  196 +
  197 + private void deleteVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO) {
  198 + // 1. 更新向量 ID
  199 + if (StrUtil.isEmpty(segmentDO.getVectorId())) {
  200 + return;
  201 + }
  202 + segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(segmentDO.getId())
  203 + .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY));
  204 +
  205 + // 2. 删除向量
  206 + vectorStore.delete(List.of(segmentDO.getVectorId()));
97 207 }
98 208  
99 209 @Override
100   - public List<AiKnowledgeSegmentDO> similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO) {
  210 + public List<AiKnowledgeSegmentSearchRespBO> searchKnowledgeSegment(AiKnowledgeSegmentSearchReqBO reqBO) {
101 211 // 1. 校验
102   - AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqVO.getKnowledgeId());
103   - AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
104   -
105   - // 2. 获取向量存储实例
106   - VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId());
107   -
108   - // 3.1 向量检索
109   - List<Document> documentList = vectorStore.similaritySearch(SearchRequest.query(reqVO.getContent())
110   - .withTopK(knowledge.getTopK())
111   - .withSimilarityThreshold(knowledge.getSimilarityThreshold())
112   - .withFilterExpression(new FilterExpressionBuilder().eq(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, reqVO.getKnowledgeId()).build()));
113   - if (CollUtil.isEmpty(documentList)) {
  212 + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqBO.getKnowledgeId());
  213 +
  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 段落召回
  229 + List<AiKnowledgeSegmentDO> segments = segmentMapper
  230 + .selectListByVectorIds(convertList(documents, Document::getId));
  231 + if (CollUtil.isEmpty(segments)) {
114 232 return ListUtil.empty();
115 233 }
116   - // 3.2 段落召回
117   - return segmentMapper.selectListByVectorIds(CollUtil.getFieldValues(documentList, "id", String.class));
  234 +
  235 + // 3. 增加召回次数
  236 + segmentMapper.updateRetrievalCountIncrByIds(convertList(segments, AiKnowledgeSegmentDO::getId));
  237 +
  238 + // 4. 构建结果
  239 + List<AiKnowledgeSegmentSearchRespBO> result = convertList(segments, segment -> {
  240 + Document document = CollUtil.findOne(documents, // 找到对应的文档
  241 + doc -> Objects.equals(doc.getId(), segment.getVectorId()));
  242 + if (document == null) {
  243 + return null;
  244 + }
  245 + return BeanUtils.toBean(segment, AiKnowledgeSegmentSearchRespBO.class)
  246 + .setScore(document.getScore());
  247 + });
  248 + result.sort((o1, o2) -> Double.compare(o2.getScore(), o1.getScore())); // 按照分数降序排序
  249 + return result;
  250 + }
  251 +
  252 + @Override
  253 + public List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens) {
  254 + // 1. 读取 URL 内容
  255 + String content = knowledgeDocumentService.readUrl(url);
  256 +
  257 + // 2. 文档切片
  258 + List<Document> documentSegments = splitContentByToken(content, segmentMaxTokens);
  259 +
  260 + // 3. 转换为段落对象
  261 + return convertList(documentSegments, segment -> {
  262 + if (StrUtil.isEmpty(segment.getText())) {
  263 + return null;
  264 + }
  265 + return new AiKnowledgeSegmentDO()
  266 + .setContent(segment.getText())
  267 + .setContentLength(segment.getText().length())
  268 + .setTokens(tokenCountEstimator.estimate(segment.getText()));
  269 + });
118 270 }
119 271  
120 272 /**
... ... @@ -131,4 +283,75 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
131 283 return knowledgeSegment;
132 284 }
133 285  
  286 + private VectorStore getVectorStoreById(AiKnowledgeDO knowledge) {
  287 + return modelService.getOrCreateVectorStore(knowledge.getEmbeddingModelId(), VECTOR_STORE_METADATA_TYPES);
  288 + }
  289 +
  290 + private VectorStore getVectorStoreById(Long knowledgeId) {
  291 + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(knowledgeId);
  292 + return getVectorStoreById(knowledge);
  293 + }
  294 +
  295 + private static List<Document> splitContentByToken(String content, Integer segmentMaxTokens) {
  296 + TextSplitter textSplitter = buildTokenTextSplitter(segmentMaxTokens);
  297 + return textSplitter.apply(Collections.singletonList(new Document(content)));
  298 + }
  299 +
  300 + private static TextSplitter buildTokenTextSplitter(Integer segmentMaxTokens) {
  301 + return TokenTextSplitter.builder()
  302 + .withChunkSize(segmentMaxTokens)
  303 + .withMinChunkSizeChars(Integer.MAX_VALUE) // 忽略字符的截断
  304 + .withMinChunkLengthToEmbed(1) // 允许的最小有效分段长度
  305 + .withMaxNumChunks(Integer.MAX_VALUE)
  306 + .withKeepSeparator(true) // 保留分隔符
  307 + .build();
  308 + }
  309 +
  310 + @Override
  311 + public List<AiKnowledgeSegmentProcessRespVO> getKnowledgeSegmentProcessList(List<Long> documentIds) {
  312 + if (CollUtil.isEmpty(documentIds)) {
  313 + return Collections.emptyList();
  314 + }
  315 + return segmentMapper.selectProcessList(documentIds);
  316 + }
  317 +
  318 + @Override
  319 + public Long createKnowledgeSegment(AiKnowledgeSegmentSaveReqVO createReqVO) {
  320 + // 1.1 校验文档是否存在
  321 + AiKnowledgeDocumentDO document = knowledgeDocumentService
  322 + .validateKnowledgeDocumentExists(createReqVO.getDocumentId());
  323 + // 1.2 获取知识库信息
  324 + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(document.getKnowledgeId());
  325 + // 1.3 校验 token 熟练
  326 + Integer tokens = tokenCountEstimator.estimate(createReqVO.getContent());
  327 + if (tokens > document.getSegmentMaxTokens()) {
  328 + throw exception(KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG, tokens, document.getSegmentMaxTokens());
  329 + }
  330 +
  331 + // 2. 保存段落
  332 + AiKnowledgeSegmentDO segment = BeanUtils.toBean(createReqVO, AiKnowledgeSegmentDO.class)
  333 + .setKnowledgeId(knowledge.getId()).setDocumentId(document.getId())
  334 + .setContentLength(createReqVO.getContent().length()).setTokens(tokens)
  335 + .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY)
  336 + .setRetrievalCount(0).setStatus(CommonStatusEnum.ENABLE.getStatus());
  337 + segmentMapper.insert(segment);
  338 +
  339 + // 3. 向量化
  340 + writeVectorStore(getVectorStoreById(knowledge), segment, new Document(segment.getContent()));
  341 + return segment.getId();
  342 + }
  343 +
  344 + @Override
  345 + public AiKnowledgeSegmentDO getKnowledgeSegment(Long id) {
  346 + return segmentMapper.selectById(id);
  347 + }
  348 +
  349 + @Override
  350 + public List<AiKnowledgeSegmentDO> getKnowledgeSegmentList(Collection<Long> ids) {
  351 + if (CollUtil.isEmpty(ids)) {
  352 + return Collections.emptyList();
  353 + }
  354 + return segmentMapper.selectBatchIds(ids);
  355 + }
  356 +
134 357 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
1 1 package cn.iocoder.yudao.module.ai.service.knowledge;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO;
5 4 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
6   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
7 6 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
8   -import org.springframework.ai.vectorstore.VectorStore;
  7 +
  8 +import java.util.List;
9 9  
10 10 /**
11 11 * AI 知识库-基础信息 Service 接口
... ... @@ -18,18 +18,24 @@ public interface AiKnowledgeService {
18 18 * 创建知识库
19 19 *
20 20 * @param createReqVO 创建信息
21   - * @param userId 用户编号
22 21 * @return 编号
23 22 */
24   - Long createKnowledge(AiKnowledgeCreateReqVO createReqVO, Long userId);
  23 + Long createKnowledge(AiKnowledgeSaveReqVO createReqVO);
25 24  
26 25 /**
27 26 * 更新知识库
28 27 *
29 28 * @param updateReqVO 更新信息
30   - * @param userId 用户编号
31 29 */
32   - void updateKnowledge(AiKnowledgeUpdateReqVO updateReqVO, Long userId);
  30 + void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO);
  31 +
  32 + /**
  33 + * 获得知识库
  34 + *
  35 + * @param id 编号
  36 + * @return 知识库
  37 + */
  38 + AiKnowledgeDO getKnowledge(Long id);
33 39  
34 40 /**
35 41 * 校验知识库是否存在
... ... @@ -41,18 +47,17 @@ public interface AiKnowledgeService {
41 47 /**
42 48 * 获得知识库分页
43 49 *
44   - * @param userId 用户编号
45 50 * @param pageReqVO 分页查询
46 51 * @return 知识库分页
47 52 */
48   - PageResult<AiKnowledgeDO> getKnowledgePage(Long userId, AiKnowledgePageReqVO pageReqVO);
  53 + PageResult<AiKnowledgeDO> getKnowledgePage(AiKnowledgePageReqVO pageReqVO);
49 54  
50 55 /**
51   - * 根据知识库编号获取向量存储实例
  56 + * 获得指定状态的知识库列表
52 57 *
53   - * @param id 知识库编号
54   - * @return 向量存储实例
  58 + * @param status 状态
  59 + * @return 知识库列表
55 60 */
56   - VectorStore getVectorStoreById(Long id);
  61 + List<AiKnowledgeDO> getKnowledgeSimpleListByStatus(Integer status);
57 62  
58 63 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
... ... @@ -4,19 +4,19 @@ import cn.hutool.core.util.ObjUtil;
4 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO;
8 7 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
9   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
10 9 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
11   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  10 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
  11 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
12 12 import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper;
13   -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
14   -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
  13 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
15 14 import jakarta.annotation.Resource;
16 15 import lombok.extern.slf4j.Slf4j;
17   -import org.springframework.ai.vectorstore.VectorStore;
18 16 import org.springframework.stereotype.Service;
19 17  
  18 +import java.util.List;
  19 +
20 20 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
21 21 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_EXISTS;
22 22  
... ... @@ -33,36 +33,43 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
33 33 private AiKnowledgeMapper knowledgeMapper;
34 34  
35 35 @Resource
36   - private AiChatModelService chatModelService;
  36 + private AiModelService modelService;
37 37 @Resource
38   - private AiApiKeyService apiKeyService;
  38 + private AiKnowledgeSegmentService knowledgeSegmentService;
39 39  
40 40 @Override
41   - public Long createKnowledge(AiKnowledgeCreateReqVO createReqVO, Long userId) {
  41 + public Long createKnowledge(AiKnowledgeSaveReqVO createReqVO) {
42 42 // 1. 校验模型配置
43   - AiChatModelDO model = chatModelService.validateChatModel(createReqVO.getModelId());
  43 + AiModelDO model = modelService.validateModel(createReqVO.getEmbeddingModelId());
44 44  
45 45 // 2. 插入知识库
46   - AiKnowledgeDO knowledgeBase = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class)
47   - .setModel(model.getModel()).setUserId(userId).setStatus(CommonStatusEnum.ENABLE.getStatus());
48   - knowledgeMapper.insert(knowledgeBase);
49   - return knowledgeBase.getId();
  46 + AiKnowledgeDO knowledge = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class)
  47 + .setEmbeddingModel(model.getModel());
  48 + knowledgeMapper.insert(knowledge);
  49 + return knowledge.getId();
50 50 }
51 51  
52 52 @Override
53   - public void updateKnowledge(AiKnowledgeUpdateReqVO updateReqVO, Long userId) {
  53 + public void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO) {
54 54 // 1.1 校验知识库存在
55   - AiKnowledgeDO knowledgeBaseDO = validateKnowledgeExists(updateReqVO.getId());
56   - if (ObjUtil.notEqual(knowledgeBaseDO.getUserId(), userId)) {
57   - throw exception(KNOWLEDGE_NOT_EXISTS);
58   - }
  55 + AiKnowledgeDO oldKnowledge = validateKnowledgeExists(updateReqVO.getId());
59 56 // 1.2 校验模型配置
60   - AiChatModelDO model = chatModelService.validateChatModel(updateReqVO.getModelId());
  57 + AiModelDO model = modelService.validateModel(updateReqVO.getEmbeddingModelId());
61 58  
62 59 // 2. 更新知识库
63   - AiKnowledgeDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class);
64   - updateDO.setModel(model.getModel());
65   - knowledgeMapper.updateById(updateDO);
  60 + AiKnowledgeDO updateObj = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class)
  61 + .setEmbeddingModel(model.getModel());
  62 + knowledgeMapper.updateById(updateObj);
  63 +
  64 + // 3. 如果模型变化,需要 reindex 所有的文档
  65 + if (ObjUtil.notEqual(oldKnowledge.getEmbeddingModelId(), updateReqVO.getEmbeddingModelId())) {
  66 + knowledgeSegmentService.reindexByKnowledgeIdAsync(updateReqVO.getId());
  67 + }
  68 + }
  69 +
  70 + @Override
  71 + public AiKnowledgeDO getKnowledge(Long id) {
  72 + return knowledgeMapper.selectById(id);
66 73 }
67 74  
68 75 @Override
... ... @@ -75,16 +82,13 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
75 82 }
76 83  
77 84 @Override
78   - public PageResult<AiKnowledgeDO> getKnowledgePage(Long userId, AiKnowledgePageReqVO pageReqVO) {
79   - return knowledgeMapper.selectPage(userId, pageReqVO);
  85 + public PageResult<AiKnowledgeDO> getKnowledgePage(AiKnowledgePageReqVO pageReqVO) {
  86 + return knowledgeMapper.selectPage(pageReqVO);
80 87 }
81 88  
82 89 @Override
83   - public VectorStore getVectorStoreById(Long id) {
84   - AiKnowledgeDO knowledge = validateKnowledgeExists(id);
85   - AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
86   - // 创建或获取 VectorStore 对象
87   - return apiKeyService.getOrCreateVectorStore(model.getKeyId());
  90 + public List<AiKnowledgeDO> getKnowledgeSimpleListByStatus(Integer status) {
  91 + return knowledgeMapper.selectListByStatus(status);
88 92 }
89 93  
90 94 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.knowledge.bo;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import javax.validation.constraints.NotNull;
  6 +
  7 +import jakarta.validation.constraints.NotEmpty;
  8 +
  9 +/**
  10 + * AI 知识库段落搜索 Request BO
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Data
  15 +public class AiKnowledgeSegmentSearchReqBO {
  16 +
  17 + /**
  18 + * 知识库编号
  19 + */
  20 + @NotNull(message = "知识库编号不能为空")
  21 + private Long knowledgeId;
  22 +
  23 + /**
  24 + * 内容
  25 + */
  26 + @NotEmpty(message = "内容不能为空")
  27 + private String content;
  28 +
  29 + /**
  30 + * 最大返回数量
  31 + */
  32 + private Integer topK;
  33 +
  34 + /**
  35 + * 相似度阈值
  36 + */
  37 + private Double similarityThreshold;
  38 +
  39 +}
0 40 \ No newline at end of file
... ...