Commit 90fa7be66001068f558835ca86fd82a1aeb39a62

Authored by 芋道源码
Committed by Gitee
2 parents 2f49ad31 68ed8cd6

!1006 AI 大模型的草稿提交(code review 代码)

Merge pull request !1006 from 芋道源码/master-jdk21-ai
Showing 172 changed files with 13626 additions and 333 deletions

Too many changes.

To preserve performance only 100 of 172 files are displayed.

.DS_Store 0 → 100644
No preview for this file type
... ... @@ -23,6 +23,7 @@
23 23 <!-- <module>yudao-module-mall</module>-->
24 24 <!-- <module>yudao-module-crm</module>-->
25 25 <!-- <module>yudao-module-erp</module>-->
  26 + <module>yudao-module-ai</module>
26 27 </modules>
27 28  
28 29 <name>${project.artifactId}</name>
... ... @@ -147,6 +148,23 @@
147 148 <name>aliyun</name>
148 149 <url>https://maven.aliyun.com/repository/public</url>
149 150 </repository>
  151 +
  152 + <repository>
  153 + <id>spring-milestones</id>
  154 + <name>Spring Milestones</name>
  155 + <url>https://repo.spring.io/milestone</url>
  156 + <snapshots>
  157 + <enabled>false</enabled>
  158 + </snapshots>
  159 + </repository>
  160 + <repository>
  161 + <id>spring-snapshots</id>
  162 + <name>Spring Snapshots</name>
  163 + <url>https://repo.spring.io/snapshot</url>
  164 + <releases>
  165 + <enabled>false</enabled>
  166 + </releases>
  167 + </repository>
150 168 </repositories>
151 169  
152 170 </project>
... ...
sql/mysql/ruoyi-vue-pro.sql
... ... @@ -11,308 +11,13 @@
11 11 Target Server Version : 80200 (8.2.0)
12 12 File Encoding : 65001
13 13  
14   - Date: 30/04/2024 09:54:18
  14 + Date: 07/07/2024 18:29:17
15 15 */
16 16  
17 17 SET NAMES utf8mb4;
18 18 SET FOREIGN_KEY_CHECKS = 0;
19 19  
20 20 -- ----------------------------
21   --- Table structure for QRTZ_BLOB_TRIGGERS
22   --- ----------------------------
23   -DROP TABLE IF EXISTS `QRTZ_BLOB_TRIGGERS`;
24   -CREATE TABLE `QRTZ_BLOB_TRIGGERS` (
25   - `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
26   - `TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
27   - `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
28   - `BLOB_DATA` blob NULL,
29   - PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,
30   - INDEX `SCHED_NAME`(`SCHED_NAME` ASC, `TRIGGER_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE,
31   - CONSTRAINT `qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
32   -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
33   -
34   --- ----------------------------
35   --- Records of QRTZ_BLOB_TRIGGERS
36   --- ----------------------------
37   -BEGIN;
38   -COMMIT;
39   -
40   --- ----------------------------
41   --- Table structure for QRTZ_CALENDARS
42   --- ----------------------------
43   -DROP TABLE IF EXISTS `QRTZ_CALENDARS`;
44   -CREATE TABLE `QRTZ_CALENDARS` (
45   - `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
46   - `CALENDAR_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
47   - `CALENDAR` blob NOT NULL,
48   - PRIMARY KEY (`SCHED_NAME`, `CALENDAR_NAME`) USING BTREE
49   -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
50   -
51   --- ----------------------------
52   --- Records of QRTZ_CALENDARS
53   --- ----------------------------
54   -BEGIN;
55   -COMMIT;
56   -
57   --- ----------------------------
58   --- Table structure for QRTZ_CRON_TRIGGERS
59   --- ----------------------------
60   -DROP TABLE IF EXISTS `QRTZ_CRON_TRIGGERS`;
61   -CREATE TABLE `QRTZ_CRON_TRIGGERS` (
62   - `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
63   - `TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
64   - `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
65   - `CRON_EXPRESSION` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
66   - `TIME_ZONE_ID` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
67   - PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,
68   - CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
69   -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
70   -
71   --- ----------------------------
72   --- Records of QRTZ_CRON_TRIGGERS
73   --- ----------------------------
74   -BEGIN;
75   -INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'accessLogCleanJob', 'DEFAULT', '0 0 0 * * ?', 'Asia/Shanghai');
76   -INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'brokerageRecordUnfreezeJob', 'DEFAULT', '0 * * * * ?', 'Asia/Shanghai');
77   -INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'errorLogCleanJob', 'DEFAULT', '0 0 0 * * ?', 'Asia/Shanghai');
78   -INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'jobLogCleanJob', 'DEFAULT', '0 0 0 * * ?', 'Asia/Shanghai');
79   -INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', '* * * * * ?', 'Asia/Shanghai');
80   -INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai');
81   -INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai');
82   -INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payRefundSyncJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai');
83   -INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'tradeOrderAutoCancelJob', 'DEFAULT', '0 * * * * ?', 'Asia/Shanghai');
84   -INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'tradeOrderAutoCommentJob', 'DEFAULT', '0 * * * * ?', 'Asia/Shanghai');
85   -INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'tradeOrderAutoReceiveJob', 'DEFAULT', '0 * * * * ?', 'Asia/Shanghai');
86   -COMMIT;
87   -
88   --- ----------------------------
89   --- Table structure for QRTZ_FIRED_TRIGGERS
90   --- ----------------------------
91   -DROP TABLE IF EXISTS `QRTZ_FIRED_TRIGGERS`;
92   -CREATE TABLE `QRTZ_FIRED_TRIGGERS` (
93   - `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
94   - `ENTRY_ID` varchar(95) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
95   - `TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
96   - `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
97   - `INSTANCE_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
98   - `FIRED_TIME` bigint NOT NULL,
99   - `SCHED_TIME` bigint NOT NULL,
100   - `PRIORITY` int NOT NULL,
101   - `STATE` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
102   - `JOB_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
103   - `JOB_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
104   - `IS_NONCONCURRENT` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
105   - `REQUESTS_RECOVERY` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
106   - PRIMARY KEY (`SCHED_NAME`, `ENTRY_ID`) USING BTREE,
107   - INDEX `IDX_QRTZ_FT_TRIG_INST_NAME`(`SCHED_NAME` ASC, `INSTANCE_NAME` ASC) USING BTREE,
108   - INDEX `IDX_QRTZ_FT_INST_JOB_REQ_RCVRY`(`SCHED_NAME` ASC, `INSTANCE_NAME` ASC, `REQUESTS_RECOVERY` ASC) USING BTREE,
109   - INDEX `IDX_QRTZ_FT_J_G`(`SCHED_NAME` ASC, `JOB_NAME` ASC, `JOB_GROUP` ASC) USING BTREE,
110   - INDEX `IDX_QRTZ_FT_JG`(`SCHED_NAME` ASC, `JOB_GROUP` ASC) USING BTREE,
111   - INDEX `IDX_QRTZ_FT_T_G`(`SCHED_NAME` ASC, `TRIGGER_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE,
112   - INDEX `IDX_QRTZ_FT_TG`(`SCHED_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE
113   -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
114   -
115   --- ----------------------------
116   --- Records of QRTZ_FIRED_TRIGGERS
117   --- ----------------------------
118   -BEGIN;
119   -COMMIT;
120   -
121   --- ----------------------------
122   --- Table structure for QRTZ_JOB_DETAILS
123   --- ----------------------------
124   -DROP TABLE IF EXISTS `QRTZ_JOB_DETAILS`;
125   -CREATE TABLE `QRTZ_JOB_DETAILS` (
126   - `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
127   - `JOB_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
128   - `JOB_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
129   - `DESCRIPTION` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
130   - `JOB_CLASS_NAME` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
131   - `IS_DURABLE` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
132   - `IS_NONCONCURRENT` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
133   - `IS_UPDATE_DATA` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
134   - `REQUESTS_RECOVERY` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
135   - `JOB_DATA` blob NULL,
136   - PRIMARY KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) USING BTREE,
137   - INDEX `IDX_QRTZ_J_REQ_RECOVERY`(`SCHED_NAME` ASC, `REQUESTS_RECOVERY` ASC) USING BTREE,
138   - INDEX `IDX_QRTZ_J_GRP`(`SCHED_NAME` ASC, `JOB_GROUP` ASC) USING BTREE
139   -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
140   -
141   --- ----------------------------
142   --- Records of QRTZ_JOB_DETAILS
143   --- ----------------------------
144   -BEGIN;
145   -INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'accessLogCleanJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000197400104A4F425F48414E444C45525F4E414D457400116163636573734C6F67436C65616E4A6F627800);
146   -INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'brokerageRecordUnfreezeJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000187400104A4F425F48414E444C45525F4E414D4574001A62726F6B65726167655265636F7264556E667265657A654A6F627800);
147   -INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'errorLogCleanJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B0200007870000000000000001A7400104A4F425F48414E444C45525F4E414D457400106572726F724C6F67436C65616E4A6F627800);
148   -INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'jobLogCleanJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B0200007870000000000000001B7400104A4F425F48414E444C45525F4E414D4574000E6A6F624C6F67436C65616E4A6F627800);
149   -INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000057400104A4F425F48414E444C45525F4E414D4574000C7061794E6F746966794A6F627800);
150   -INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000127400104A4F425F48414E444C45525F4E414D457400117061794F726465724578706972654A6F627800);
151   -INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000117400104A4F425F48414E444C45525F4E414D4574000F7061794F7264657253796E634A6F627800);
152   -INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payRefundSyncJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000137400104A4F425F48414E444C45525F4E414D45740010706179526566756E6453796E634A6F627800);
153   -INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'tradeOrderAutoCancelJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000157400104A4F425F48414E444C45525F4E414D4574001774726164654F726465724175746F43616E63656C4A6F627800);
154   -INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'tradeOrderAutoCommentJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000177400104A4F425F48414E444C45525F4E414D4574001874726164654F726465724175746F436F6D6D656E744A6F627800);
155   -INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'tradeOrderAutoReceiveJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000167400104A4F425F48414E444C45525F4E414D4574001874726164654F726465724175746F526563656976654A6F627800);
156   -COMMIT;
157   -
158   --- ----------------------------
159   --- Table structure for QRTZ_LOCKS
160   --- ----------------------------
161   -DROP TABLE IF EXISTS `QRTZ_LOCKS`;
162   -CREATE TABLE `QRTZ_LOCKS` (
163   - `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
164   - `LOCK_NAME` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
165   - PRIMARY KEY (`SCHED_NAME`, `LOCK_NAME`) USING BTREE
166   -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
167   -
168   --- ----------------------------
169   --- Records of QRTZ_LOCKS
170   --- ----------------------------
171   -BEGIN;
172   -INSERT INTO `QRTZ_LOCKS` (`SCHED_NAME`, `LOCK_NAME`) VALUES ('schedulerName', 'STATE_ACCESS');
173   -INSERT INTO `QRTZ_LOCKS` (`SCHED_NAME`, `LOCK_NAME`) VALUES ('schedulerName', 'TRIGGER_ACCESS');
174   -COMMIT;
175   -
176   --- ----------------------------
177   --- Table structure for QRTZ_PAUSED_TRIGGER_GRPS
178   --- ----------------------------
179   -DROP TABLE IF EXISTS `QRTZ_PAUSED_TRIGGER_GRPS`;
180   -CREATE TABLE `QRTZ_PAUSED_TRIGGER_GRPS` (
181   - `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
182   - `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
183   - PRIMARY KEY (`SCHED_NAME`, `TRIGGER_GROUP`) USING BTREE
184   -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
185   -
186   --- ----------------------------
187   --- Records of QRTZ_PAUSED_TRIGGER_GRPS
188   --- ----------------------------
189   -BEGIN;
190   -COMMIT;
191   -
192   --- ----------------------------
193   --- Table structure for QRTZ_SCHEDULER_STATE
194   --- ----------------------------
195   -DROP TABLE IF EXISTS `QRTZ_SCHEDULER_STATE`;
196   -CREATE TABLE `QRTZ_SCHEDULER_STATE` (
197   - `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
198   - `INSTANCE_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
199   - `LAST_CHECKIN_TIME` bigint NOT NULL,
200   - `CHECKIN_INTERVAL` bigint NOT NULL,
201   - PRIMARY KEY (`SCHED_NAME`, `INSTANCE_NAME`) USING BTREE
202   -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
203   -
204   --- ----------------------------
205   --- Records of QRTZ_SCHEDULER_STATE
206   --- ----------------------------
207   -BEGIN;
208   -INSERT INTO `QRTZ_SCHEDULER_STATE` (`SCHED_NAME`, `INSTANCE_NAME`, `LAST_CHECKIN_TIME`, `CHECKIN_INTERVAL`) VALUES ('schedulerName', 'MacBook-Pro.local1713489703551', 1713742509534, 15000);
209   -COMMIT;
210   -
211   --- ----------------------------
212   --- Table structure for QRTZ_SIMPLE_TRIGGERS
213   --- ----------------------------
214   -DROP TABLE IF EXISTS `QRTZ_SIMPLE_TRIGGERS`;
215   -CREATE TABLE `QRTZ_SIMPLE_TRIGGERS` (
216   - `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
217   - `TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
218   - `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
219   - `REPEAT_COUNT` bigint NOT NULL,
220   - `REPEAT_INTERVAL` bigint NOT NULL,
221   - `TIMES_TRIGGERED` bigint NOT NULL,
222   - PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,
223   - CONSTRAINT `qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
224   -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
225   -
226   --- ----------------------------
227   --- Records of QRTZ_SIMPLE_TRIGGERS
228   --- ----------------------------
229   -BEGIN;
230   -COMMIT;
231   -
232   --- ----------------------------
233   --- Table structure for QRTZ_SIMPROP_TRIGGERS
234   --- ----------------------------
235   -DROP TABLE IF EXISTS `QRTZ_SIMPROP_TRIGGERS`;
236   -CREATE TABLE `QRTZ_SIMPROP_TRIGGERS` (
237   - `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
238   - `TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
239   - `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
240   - `STR_PROP_1` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
241   - `STR_PROP_2` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
242   - `STR_PROP_3` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
243   - `INT_PROP_1` int NULL DEFAULT NULL,
244   - `INT_PROP_2` int NULL DEFAULT NULL,
245   - `LONG_PROP_1` bigint NULL DEFAULT NULL,
246   - `LONG_PROP_2` bigint NULL DEFAULT NULL,
247   - `DEC_PROP_1` decimal(13, 4) NULL DEFAULT NULL,
248   - `DEC_PROP_2` decimal(13, 4) NULL DEFAULT NULL,
249   - `BOOL_PROP_1` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
250   - `BOOL_PROP_2` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
251   - PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,
252   - CONSTRAINT `qrtz_simprop_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
253   -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
254   -
255   --- ----------------------------
256   --- Records of QRTZ_SIMPROP_TRIGGERS
257   --- ----------------------------
258   -BEGIN;
259   -COMMIT;
260   -
261   --- ----------------------------
262   --- Table structure for QRTZ_TRIGGERS
263   --- ----------------------------
264   -DROP TABLE IF EXISTS `QRTZ_TRIGGERS`;
265   -CREATE TABLE `QRTZ_TRIGGERS` (
266   - `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
267   - `TRIGGER_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
268   - `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
269   - `JOB_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
270   - `JOB_GROUP` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
271   - `DESCRIPTION` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
272   - `NEXT_FIRE_TIME` bigint NULL DEFAULT NULL,
273   - `PREV_FIRE_TIME` bigint NULL DEFAULT NULL,
274   - `PRIORITY` int NULL DEFAULT NULL,
275   - `TRIGGER_STATE` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
276   - `TRIGGER_TYPE` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
277   - `START_TIME` bigint NOT NULL,
278   - `END_TIME` bigint NULL DEFAULT NULL,
279   - `CALENDAR_NAME` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
280   - `MISFIRE_INSTR` smallint NULL DEFAULT NULL,
281   - `JOB_DATA` blob NULL,
282   - PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,
283   - INDEX `IDX_QRTZ_T_J`(`SCHED_NAME` ASC, `JOB_NAME` ASC, `JOB_GROUP` ASC) USING BTREE,
284   - INDEX `IDX_QRTZ_T_JG`(`SCHED_NAME` ASC, `JOB_GROUP` ASC) USING BTREE,
285   - INDEX `IDX_QRTZ_T_C`(`SCHED_NAME` ASC, `CALENDAR_NAME` ASC) USING BTREE,
286   - INDEX `IDX_QRTZ_T_G`(`SCHED_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE,
287   - INDEX `IDX_QRTZ_T_STATE`(`SCHED_NAME` ASC, `TRIGGER_STATE` ASC) USING BTREE,
288   - INDEX `IDX_QRTZ_T_N_STATE`(`SCHED_NAME` ASC, `TRIGGER_NAME` ASC, `TRIGGER_GROUP` ASC, `TRIGGER_STATE` ASC) USING BTREE,
289   - INDEX `IDX_QRTZ_T_N_G_STATE`(`SCHED_NAME` ASC, `TRIGGER_GROUP` ASC, `TRIGGER_STATE` ASC) USING BTREE,
290   - INDEX `IDX_QRTZ_T_NEXT_FIRE_TIME`(`SCHED_NAME` ASC, `NEXT_FIRE_TIME` ASC) USING BTREE,
291   - INDEX `IDX_QRTZ_T_NFT_ST`(`SCHED_NAME` ASC, `TRIGGER_STATE` ASC, `NEXT_FIRE_TIME` ASC) USING BTREE,
292   - INDEX `IDX_QRTZ_T_NFT_MISFIRE`(`SCHED_NAME` ASC, `MISFIRE_INSTR` ASC, `NEXT_FIRE_TIME` ASC) USING BTREE,
293   - INDEX `IDX_QRTZ_T_NFT_ST_MISFIRE`(`SCHED_NAME` ASC, `MISFIRE_INSTR` ASC, `NEXT_FIRE_TIME` ASC, `TRIGGER_STATE` ASC) USING BTREE,
294   - INDEX `IDX_QRTZ_T_NFT_ST_MISFIRE_GRP`(`SCHED_NAME` ASC, `MISFIRE_INSTR` ASC, `NEXT_FIRE_TIME` ASC, `TRIGGER_GROUP` ASC, `TRIGGER_STATE` ASC) USING BTREE,
295   - CONSTRAINT `qrtz_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) REFERENCES `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
296   -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
297   -
298   --- ----------------------------
299   --- Records of QRTZ_TRIGGERS
300   --- ----------------------------
301   -BEGIN;
302   -INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'accessLogCleanJob', 'DEFAULT', 'accessLogCleanJob', 'DEFAULT', NULL, 1696348800000, -1, 5, 'PAUSED', 'CRON', 1696301981000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);
303   -INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'brokerageRecordUnfreezeJob', 'DEFAULT', 'brokerageRecordUnfreezeJob', 'DEFAULT', NULL, 1695909720000, -1, 5, 'PAUSED', 'CRON', 1695909706000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);
304   -INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'errorLogCleanJob', 'DEFAULT', 'errorLogCleanJob', 'DEFAULT', NULL, 1696348800000, -1, 5, 'PAUSED', 'CRON', 1696302043000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);
305   -INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'jobLogCleanJob', 'DEFAULT', 'jobLogCleanJob', 'DEFAULT', NULL, 1696348800000, -1, 5, 'PAUSED', 'CRON', 1696302092000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);
306   -INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', 'payNotifyJob', 'DEFAULT', NULL, 1688907102000, 1688907101000, 5, 'PAUSED', 'CRON', 1635294882000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
307   -INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', 'payOrderExpireJob', 'DEFAULT', NULL, 1690011600000, -1, 5, 'PAUSED', 'CRON', 1690011553000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
308   -INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', 'payOrderSyncJob', 'DEFAULT', NULL, 1690011600000, 1690011540000, 5, 'PAUSED', 'CRON', 1690007785000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
309   -INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payRefundSyncJob', 'DEFAULT', 'payRefundSyncJob', 'DEFAULT', NULL, 1690117560000, 1690117500000, 5, 'PAUSED', 'CRON', 1690117424000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
310   -INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'tradeOrderAutoCancelJob', 'DEFAULT', 'tradeOrderAutoCancelJob', 'DEFAULT', NULL, 1695727440000, 1695727380000, 5, 'PAUSED', 'CRON', 1695656605000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);
311   -INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'tradeOrderAutoCommentJob', 'DEFAULT', 'tradeOrderAutoCommentJob', 'DEFAULT', NULL, 1695783840000, 1695783780000, 5, 'PAUSED', 'CRON', 1695742709000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);
312   -INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'tradeOrderAutoReceiveJob', 'DEFAULT', 'tradeOrderAutoReceiveJob', 'DEFAULT', NULL, 1695742740000, 1695742680000, 5, 'PAUSED', 'CRON', 1695727433000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E547371007E000A000000037800);
313   -COMMIT;
314   -
315   --- ----------------------------
316 21 -- Table structure for infra_api_access_log
317 22 -- ----------------------------
318 23 DROP TABLE IF EXISTS `infra_api_access_log`;
... ... @@ -344,7 +49,7 @@ CREATE TABLE `infra_api_access_log` (
344 49 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
345 50 PRIMARY KEY (`id`) USING BTREE,
346 51 INDEX `idx_create_time`(`create_time` ASC) USING BTREE
347   -) ENGINE = InnoDB AUTO_INCREMENT = 35934 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表';
  52 +) ENGINE = InnoDB AUTO_INCREMENT = 35940 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表';
348 53  
349 54 -- ----------------------------
350 55 -- Records of infra_api_access_log
... ... @@ -358,24 +63,24 @@ COMMIT;
358 63 DROP TABLE IF EXISTS `infra_api_error_log`;
359 64 CREATE TABLE `infra_api_error_log` (
360 65 `id` int NOT NULL AUTO_INCREMENT COMMENT '编号',
361   - `trace_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '链路追踪编号\n *\n * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。',
  66 + `trace_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '链路追踪编号',
362 67 `user_id` int NOT NULL DEFAULT 0 COMMENT '用户编号',
363 68 `user_type` tinyint NOT NULL DEFAULT 0 COMMENT '用户类型',
364   - `application_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用名\n *\n * 目前读取 spring.application.name',
  69 + `application_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用名',
365 70 `request_method` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求方法名',
366 71 `request_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求地址',
367 72 `request_params` varchar(8000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求参数',
368 73 `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户 IP',
369 74 `user_agent` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '浏览器 UA',
370 75 `exception_time` datetime NOT NULL COMMENT '异常发生时间',
371   - `exception_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '异常名\n *\n * {@link Throwable#getClass()} 的类全名',
372   - `exception_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常导致的消息\n *\n * {@link cn.iocoder.common.framework.util.ExceptionUtil#getMessage(Throwable)}',
373   - `exception_root_cause_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常导致的根消息\n *\n * {@link cn.iocoder.common.framework.util.ExceptionUtil#getRootCauseMessage(Throwable)}',
374   - `exception_stack_trace` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常的栈轨迹\n *\n * {@link cn.iocoder.common.framework.util.ExceptionUtil#getServiceException(Exception)}',
375   - `exception_class_name` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常发生的类全名\n *\n * {@link StackTraceElement#getClassName()}',
376   - `exception_file_name` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常发生的类文件\n *\n * {@link StackTraceElement#getFileName()}',
377   - `exception_method_name` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常发生的方法名\n *\n * {@link StackTraceElement#getMethodName()}',
378   - `exception_line_number` int NOT NULL COMMENT '异常发生的方法所在行\n *\n * {@link StackTraceElement#getLineNumber()}',
  76 + `exception_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '异常名',
  77 + `exception_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常导致的消息',
  78 + `exception_root_cause_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常导致的根消息',
  79 + `exception_stack_trace` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常的栈轨迹',
  80 + `exception_class_name` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常发生的类全名',
  81 + `exception_file_name` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常发生的类文件',
  82 + `exception_method_name` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常发生的方法名',
  83 + `exception_line_number` int NOT NULL COMMENT '异常发生的方法所在行',
379 84 `process_status` tinyint NOT NULL COMMENT '处理状态',
380 85 `process_time` datetime NULL DEFAULT NULL COMMENT '处理时间',
381 86 `process_user_id` int NULL DEFAULT 0 COMMENT '处理用户编号',
... ... @@ -386,7 +91,7 @@ CREATE TABLE `infra_api_error_log` (
386 91 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
387 92 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
388 93 PRIMARY KEY (`id`) USING BTREE
389   -) ENGINE = InnoDB AUTO_INCREMENT = 16530 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
  94 +) ENGINE = InnoDB AUTO_INCREMENT = 17761 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
390 95  
391 96 -- ----------------------------
392 97 -- Records of infra_api_error_log
... ... @@ -423,7 +128,7 @@ CREATE TABLE `infra_codegen_column` (
423 128 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
424 129 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
425 130 PRIMARY KEY (`id`) USING BTREE
426   -) ENGINE = InnoDB AUTO_INCREMENT = 2305 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
  131 +) ENGINE = InnoDB AUTO_INCREMENT = 2440 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
427 132  
428 133 -- ----------------------------
429 134 -- Records of infra_codegen_column
... ... @@ -461,7 +166,7 @@ CREATE TABLE `infra_codegen_table` (
461 166 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
462 167 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
463 168 PRIMARY KEY (`id`) USING BTREE
464   -) ENGINE = InnoDB AUTO_INCREMENT = 176 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
  169 +) ENGINE = InnoDB AUTO_INCREMENT = 184 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
465 170  
466 171 -- ----------------------------
467 172 -- Records of infra_codegen_table
... ... @@ -545,7 +250,7 @@ CREATE TABLE `infra_file` (
545 250 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
546 251 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
547 252 PRIMARY KEY (`id`) USING BTREE
548   -) ENGINE = InnoDB AUTO_INCREMENT = 1307 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
  253 +) ENGINE = InnoDB AUTO_INCREMENT = 1397 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
549 254  
550 255 -- ----------------------------
551 256 -- Records of infra_file
... ... @@ -691,7 +396,7 @@ CREATE TABLE `system_dept` (
691 396 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
692 397 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
693 398 PRIMARY KEY (`id`) USING BTREE
694   -) ENGINE = InnoDB AUTO_INCREMENT = 114 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '部门表';
  399 +) ENGINE = InnoDB AUTO_INCREMENT = 115 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '部门表';
695 400  
696 401 -- ----------------------------
697 402 -- Records of system_dept
... ... @@ -733,7 +438,7 @@ CREATE TABLE `system_dict_data` (
733 438 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
734 439 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
735 440 PRIMARY KEY (`id`) USING BTREE
736   -) ENGINE = InnoDB AUTO_INCREMENT = 1537 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
  441 +) ENGINE = InnoDB AUTO_INCREMENT = 1555 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
737 442  
738 443 -- ----------------------------
739 444 -- Records of system_dict_data
... ... @@ -1104,6 +809,24 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
1104 809 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1534, 1, '赢单', '1', 'crm_business_end_status_type', 0, 'success', '', '', '1', '2024-04-13 23:26:57', '1', '2024-04-13 23:26:57', b'0');
1105 810 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1535, 2, '输单', '2', 'crm_business_end_status_type', 0, 'primary', '', '', '1', '2024-04-13 23:27:31', '1', '2024-04-13 23:27:31', b'0');
1106 811 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1536, 3, '无效', '3', 'crm_business_end_status_type', 0, 'info', '', '', '1', '2024-04-13 23:27:59', '1', '2024-04-13 23:27:59', b'0');
  812 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1537, 1, 'OpenAI', 'OpenAI', 'ai_platform', 0, '', '', '', '1', '2024-05-09 22:33:47', '1', '2024-05-09 22:58:46', b'0');
  813 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1538, 2, 'Ollama', 'Ollama', 'ai_platform', 0, '', '', '', '1', '2024-05-17 23:02:55', '1', '2024-05-17 23:02:55', b'0');
  814 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1539, 3, '文心一言', 'YiYan', 'ai_platform', 0, '', '', '', '1', '2024-05-18 09:24:20', '1', '2024-05-18 09:29:01', b'0');
  815 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1540, 4, '讯飞星火', 'XingHuo', 'ai_platform', 0, '', '', '', '1', '2024-05-18 10:08:56', '1', '2024-05-18 10:08:56', b'0');
  816 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1541, 5, '通义千问', 'TongYi', 'ai_platform', 0, '', '', '', '1', '2024-05-18 10:32:29', '1', '2024-07-06 15:42:29', b'0');
  817 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1542, 6, 'StableDiffusion', 'StableDiffusion', 'ai_platform', 0, '', '', '', '1', '2024-06-01 15:09:31', '1', '2024-06-01 15:10:25', b'0');
  818 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1543, 10, '进行中', '10', 'ai_image_status', 0, 'primary', '', '', '1', '2024-06-26 20:51:41', '1', '2024-06-26 20:52:48', b'0');
  819 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1544, 20, '已完成', '20', 'ai_image_status', 0, 'success', '', '', '1', '2024-06-26 20:52:07', '1', '2024-06-26 20:52:41', b'0');
  820 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1545, 30, '已失败', '30', 'ai_image_status', 0, 'warning', '', '', '1', '2024-06-26 20:52:25', '1', '2024-06-26 20:52:35', b'0');
  821 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1546, 7, 'Midjourney', 'Midjourney', 'ai_platform', 0, '', '', '', '1', '2024-06-26 22:14:46', '1', '2024-06-26 22:14:46', b'0');
  822 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1547, 10, '进行中', '10', 'ai_music_status', 0, 'primary', '', '', '1', '2024-06-27 22:45:22', '1', '2024-06-28 00:56:17', b'0');
  823 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1548, 20, '已完成', '20', 'ai_music_status', 0, 'success', '', '', '1', '2024-06-27 22:45:33', '1', '2024-06-28 00:56:18', b'0');
  824 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1549, 30, '已失败', '30', 'ai_music_status', 0, 'danger', '', '', '1', '2024-06-27 22:45:44', '1', '2024-06-28 00:56:19', b'0');
  825 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1550, 1, '歌词模式', '1', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:31', '1', '2024-06-28 01:22:25', b'0');
  826 +INSERT 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');
  827 +INSERT 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');
  828 +INSERT 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');
  829 +INSERT 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');
1107 830 COMMIT;
1108 831  
1109 832 -- ----------------------------
... ... @@ -1122,9 +845,8 @@ CREATE TABLE `system_dict_type` (
1122 845 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
1123 846 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
1124 847 `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
1125   - PRIMARY KEY (`id`) USING BTREE,
1126   - UNIQUE INDEX `dict_type`(`type` ASC) USING BTREE
1127   -) ENGINE = InnoDB AUTO_INCREMENT = 620 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
  848 + PRIMARY KEY (`id`) USING BTREE
  849 +) ENGINE = InnoDB AUTO_INCREMENT = 624 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
1128 850  
1129 851 -- ----------------------------
1130 852 -- Records of system_dict_type
... ... @@ -1213,6 +935,10 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat
1213 935 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (615, 'BPM 监听器值类型', 'bpm_process_listener_value_type', 0, '', '1', '2024-03-23 13:00:31', '1', '2024-03-23 13:00:31', b'0', '1970-01-01 00:00:00');
1214 936 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (616, '时间间隔', 'date_interval', 0, '', '1', '2024-03-29 22:50:09', '1', '2024-03-29 22:50:09', b'0', '1970-01-01 00:00:00');
1215 937 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (619, 'CRM 商机结束状态类型', 'crm_business_end_status_type', 0, '', '1', '2024-04-13 23:23:00', '1', '2024-04-13 23:23:00', b'0', '1970-01-01 00:00:00');
  938 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (620, 'AI 模型平台', 'ai_platform', 0, '', '1', '2024-05-09 22:27:38', '1', '2024-05-09 22:27:38', b'0', '1970-01-01 00:00:00');
  939 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (621, 'AI 绘画状态', 'ai_image_status', 0, '', '1', '2024-06-26 20:51:23', '1', '2024-06-26 20:51:23', b'0', '1970-01-01 00:00:00');
  940 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (622, 'AI 音乐状态', 'ai_music_status', 0, '', '1', '2024-06-27 22:45:07', '1', '2024-06-28 00:56:27', b'0', '1970-01-01 00:00:00');
  941 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (623, 'AI 音乐生成模式', 'ai_generate_mode', 0, '', '1', '2024-06-27 22:46:21', '1', '2024-06-28 01:22:29', b'0', '1970-01-01 00:00:00');
1216 942 COMMIT;
1217 943  
1218 944 -- ----------------------------
... ... @@ -1236,7 +962,7 @@ CREATE TABLE `system_login_log` (
1236 962 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
1237 963 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
1238 964 PRIMARY KEY (`id`) USING BTREE
1239   -) ENGINE = InnoDB AUTO_INCREMENT = 3104 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
  965 +) ENGINE = InnoDB AUTO_INCREMENT = 3177 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
1240 966  
1241 967 -- ----------------------------
1242 968 -- Records of system_login_log
... ... @@ -1367,17 +1093,17 @@ CREATE TABLE `system_menu` (
1367 1093 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
1368 1094 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
1369 1095 PRIMARY KEY (`id`) USING BTREE
1370   -) ENGINE = InnoDB AUTO_INCREMENT = 2758 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
  1096 +) ENGINE = InnoDB AUTO_INCREMENT = 2792 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
1371 1097  
1372 1098 -- ----------------------------
1373 1099 -- Records of system_menu
1374 1100 -- ----------------------------
1375 1101 BEGIN;
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 (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-02-29 01:04:23', b'0');
  1102 +INSERT INTO `system_menu` (`id`, `name`, `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');
1377 1103 INSERT INTO `system_menu` (`id`, `name`, `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');
1378 1104 INSERT INTO `system_menu` (`id`, `name`, `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');
1379 1105 INSERT INTO `system_menu` (`id`, `name`, `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');
1380   -INSERT INTO `system_menu` (`id`, `name`, `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-02-29 01:03:28', b'0');
  1106 +INSERT INTO `system_menu` (`id`, `name`, `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');
1381 1107 INSERT INTO `system_menu` (`id`, `name`, `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');
1382 1108 INSERT INTO `system_menu` (`id`, `name`, `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');
1383 1109 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (104, '岗位管理', '', 2, 5, 1, 'post', 'fa:address-book-o', 'system/post/index', 'SystemPost', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:06:39', b'0');
... ... @@ -1902,7 +1628,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
1902 1628 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2445, '装修页面更新', 'promotion:diy-page:update', 3, 3, 2442, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', b'0');
1903 1629 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2446, '装修页面删除', 'promotion:diy-page:delete', 3, 4, 2442, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:26', '', '2023-10-29 14:19:26', b'0');
1904 1630 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2447, '三方登录', '', 1, 10, 1, 'social', 'fa:rocket', '', '', 0, b'1', b'1', b'1', '1', '2023-11-04 12:12:01', '1', '2024-02-29 01:14:05', b'0');
1905   -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2448, '三方应用', '', 2, 1, 2447, 'client', 'ep:set-up', 'views/system/social/client/index.vue', 'SocialClient', 0, b'1', b'1', b'1', '1', '2023-11-04 12:17:19', '1', '2023-11-04 12:17:19', b'0');
  1631 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2448, '三方应用', '', 2, 1, 2447, 'client', 'ep:set-up', 'system/social/client/index.vue', 'SocialClient', 0, b'1', b'1', b'1', '1', '2023-11-04 12:17:19', '1', '2024-05-04 19:09:54', b'0');
1906 1632 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2449, '三方应用查询', 'system:social-client:query', 3, 1, 2448, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-11-04 12:43:12', '1', '2023-11-04 12:43:33', b'0');
1907 1633 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2450, '三方应用创建', 'system:social-client:create', 3, 2, 2448, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-11-04 12:43:58', '1', '2023-11-04 12:43:58', b'0');
1908 1634 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2451, '三方应用更新', 'system:social-client:update', 3, 3, 2448, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-11-04 12:44:27', '1', '2023-11-04 12:44:27', b'0');
... ... @@ -2118,7 +1844,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
2118 1844 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2709, '客户公海配置查询', 'crm:customer-pool-config:query', 3, 2, 2516, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-02-24 16:45:19', '1', '2024-02-24 16:45:28', b'0');
2119 1845 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2710, '合同配置更新', 'crm:contract-config:update', 3, 1, 2708, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-02-24 16:45:56', '1', '2024-02-24 16:45:56', b'0');
2120 1846 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2711, '合同配置查询', 'crm:contract-config:query', 3, 2, 2708, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-02-24 16:46:16', '1', '2024-02-24 16:46:16', b'0');
2121   -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2712, '客户分析', 'crm:statistics-customer:query', 2, 0, 2560, 'customer', 'ep:avatar', 'views/crm/statistics/customer/index.vue', 'CrmStatisticsCustomer', 0, b'1', b'1', b'1', '1', '2024-03-09 16:43:56', '1', '2024-04-24 19:42:52', b'0');
  1847 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2712, '客户分析', 'crm:statistics-customer:query', 2, 0, 2560, 'customer', 'ep:avatar', 'crm/statistics/customer/index.vue', 'CrmStatisticsCustomer', 0, b'1', b'1', b'1', '1', '2024-03-09 16:43:56', '1', '2024-05-04 20:38:50', b'0');
2122 1848 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2713, '抄送我的', 'bpm:process-instance-cc:query', 2, 30, 1200, 'copy', 'ep:copy-document', 'bpm/task/copy/index', 'BpmProcessInstanceCopy', 0, b'1', b'1', b'1', '1', '2024-03-17 21:50:23', '1', '2024-04-24 19:55:12', b'0');
2123 1849 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2714, '流程分类', '', 2, 3, 1186, 'category', 'fa:object-ungroup', 'bpm/category/index', 'BpmCategory', 0, b'1', b'1', b'1', '', '2024-03-08 02:00:51', '1', '2024-03-21 23:51:18', b'0');
2124 1850 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2715, '分类查询', 'bpm:category:query', 3, 1, 2714, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-03-08 02:00:51', '1', '2024-03-19 14:36:25', b'0');
... ... @@ -2163,6 +1889,38 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
2163 1889 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2755, '删除项目', 'report:go-view-project:delete', 3, 2, 2153, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 20:01:37', '1', '2024-04-24 20:01:37', b'0');
2164 1890 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2756, '会员等级记录查询', 'member:level-record:query', 3, 10, 2325, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 20:02:32', '1', '2024-04-24 20:02:32', b'0');
2165 1891 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2757, '会员经验记录查询', 'member:experience-record:query', 3, 11, 2325, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 20:02:51', '1', '2024-04-24 20:02:51', b'0');
  1892 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2758, 'AI 大模型', '', 1, 400, 0, '/ai', 'fa:apple', '', '', 0, b'1', b'1', b'1', '1', '2024-05-07 15:07:56', '1', '2024-05-25 12:36:12', b'0');
  1893 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2759, 'AI 对话', '', 2, 1, 2758, 'chat', 'ep:message', 'ai/chat/index/index.vue', 'AiChat', 0, b'1', b'1', b'1', '1', '2024-05-07 15:09:14', '1', '2024-07-07 17:15:36', b'0');
  1894 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2760, '控制台', '', 1, 100, 2758, 'console', 'ep:setting', '', '', 0, b'1', b'1', b'1', '1', '2024-05-09 22:39:09', '1', '2024-05-24 23:34:21', b'0');
  1895 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2761, 'API 密钥', '', 2, 0, 2760, 'api-key', 'ep:key', 'ai/model/apiKey/index.vue', 'AiApiKey', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-10 22:44:08', b'0');
  1896 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2762, 'API 密钥查询', 'ai:api-key:query', 3, 1, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:32', b'0');
  1897 +INSERT INTO `system_menu` (`id`, `name`, `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');
  1898 +INSERT INTO `system_menu` (`id`, `name`, `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');
  1899 +INSERT INTO `system_menu` (`id`, `name`, `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');
  1900 +INSERT INTO `system_menu` (`id`, `name`, `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');
  1901 +INSERT INTO `system_menu` (`id`, `name`, `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');
  1902 +INSERT INTO `system_menu` (`id`, `name`, `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');
  1903 +INSERT INTO `system_menu` (`id`, `name`, `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');
  1904 +INSERT INTO `system_menu` (`id`, `name`, `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');
  1905 +INSERT INTO `system_menu` (`id`, `name`, `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');
  1906 +INSERT INTO `system_menu` (`id`, `name`, `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');
  1907 +INSERT INTO `system_menu` (`id`, `name`, `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');
  1908 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2776, '聊天角色更新', 'ai:chat-role:update', 3, 3, 2773, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', b'0');
  1909 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2777, '聊天角色删除', 'ai:chat-role:delete', 3, 4, 2773, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-05-13 21:43:38', '1', '2024-05-13 21:43:38', b'0');
  1910 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2778, '聊天管理', '', 2, 10, 2760, 'chat-conversation', 'ep:chat-square', 'ai/chat/manager/index.vue', 'AiChatManager', 0, b'1', b'1', b'1', '', '2024-05-24 15:39:18', '1', '2024-06-26 21:36:56', b'0');
  1911 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2779, '会话查询', 'ai:chat-conversation:query', 3, 1, 2778, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-24 15:39:18', '1', '2024-05-25 08:38:30', b'0');
  1912 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2780, '会话删除', 'ai:chat-conversation:delete', 3, 2, 2778, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-24 15:39:18', '1', '2024-05-25 08:38:40', b'0');
  1913 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2781, '消息查询', 'ai:chat-message:query', 3, 11, 2778, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-05-25 08:38:56', '1', '2024-05-25 08:38:56', b'0');
  1914 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2782, '消息删除', 'ai:chat-message:delete', 3, 12, 2778, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-05-25 08:39:10', '1', '2024-05-25 08:39:10', b'0');
  1915 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2783, 'AI 绘画', '', 2, 2, 2758, 'image', 'ep:picture-rounded', 'ai/image/index/index.vue', 'AiImage', 0, b'1', b'1', b'1', '1', '2024-05-26 11:45:17', '1', '2024-07-07 17:18:59', b'0');
  1916 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2784, '绘画管理', '', 2, 11, 2760, 'image', 'fa:file-image-o', 'ai/image/manager/index.vue', 'AiImageManager', 0, b'1', b'1', b'1', '', '2024-06-26 13:32:31', '1', '2024-06-26 21:37:13', b'0');
  1917 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2785, '绘画查询', 'ai:image:query', 3, 1, 2784, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-06-26 13:32:31', '1', '2024-06-26 22:21:57', b'0');
  1918 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2786, '绘画删除', 'ai:image:delete', 3, 4, 2784, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-06-26 13:32:31', '1', '2024-06-26 22:22:08', b'0');
  1919 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2787, '会话更新公开状态', 'ai:image:update-public-status', 3, 2, 2784, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-06-26 22:47:56', '1', '2024-06-26 22:47:56', b'0');
  1920 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2788, '音乐管理', '', 2, 12, 2760, 'music', 'fa:music', 'ai/music/manager/index.vue', 'AiMusicManager', 0, b'1', b'1', b'1', '', '2024-06-27 15:03:33', '1', '2024-06-27 23:04:19', b'0');
  1921 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2789, '音乐查询', 'ai:music:query', 3, 1, 2788, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', b'0');
  1922 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2790, '音乐更新', 'ai:music:update', 3, 3, 2788, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', b'0');
  1923 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2791, '音乐删除', 'ai:music:delete', 3, 4, 2788, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', b'0');
2166 1924 COMMIT;
2167 1925  
2168 1926 -- ----------------------------
... ... @@ -2284,7 +2042,7 @@ CREATE TABLE `system_oauth2_access_token` (
2284 2042 PRIMARY KEY (`id`) USING BTREE,
2285 2043 INDEX `idx_access_token`(`access_token` ASC) USING BTREE,
2286 2044 INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE
2287   -) ENGINE = InnoDB AUTO_INCREMENT = 6620 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
  2045 +) ENGINE = InnoDB AUTO_INCREMENT = 7699 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
2288 2046  
2289 2047 -- ----------------------------
2290 2048 -- Records of system_oauth2_access_token
... ... @@ -2406,7 +2164,7 @@ CREATE TABLE `system_oauth2_refresh_token` (
2406 2164 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
2407 2165 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
2408 2166 PRIMARY KEY (`id`) USING BTREE
2409   -) ENGINE = InnoDB AUTO_INCREMENT = 1483 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
  2167 +) ENGINE = InnoDB AUTO_INCREMENT = 1539 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
2410 2168  
2411 2169 -- ----------------------------
2412 2170 -- Records of system_oauth2_refresh_token
... ... @@ -3410,7 +3168,7 @@ CREATE TABLE `system_sms_code` (
3410 3168 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
3411 3169 PRIMARY KEY (`id`) USING BTREE,
3412 3170 INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'
3413   -) ENGINE = InnoDB AUTO_INCREMENT = 614 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
  3171 +) ENGINE = InnoDB AUTO_INCREMENT = 615 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
3414 3172  
3415 3173 -- ----------------------------
3416 3174 -- Records of system_sms_code
... ... @@ -3451,7 +3209,7 @@ CREATE TABLE `system_sms_log` (
3451 3209 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
3452 3210 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
3453 3211 PRIMARY KEY (`id`) USING BTREE
3454   -) ENGINE = InnoDB AUTO_INCREMENT = 962 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
  3212 +) ENGINE = InnoDB AUTO_INCREMENT = 972 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
3455 3213  
3456 3214 -- ----------------------------
3457 3215 -- Records of system_sms_log
... ... @@ -3616,8 +3374,8 @@ CREATE TABLE `system_tenant` (
3616 3374 -- ----------------------------
3617 3375 BEGIN;
3618 3376 INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '芋道源码', NULL, '芋艿', '17321315478', 0, 'www.iocoder.cn', 0, '2099-02-19 17:14:16', 9999, '1', '2021-01-05 17:03:47', '1', '2023-11-06 11:41:41', b'0');
3619   -INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, '小租户', 110, '小王2', '15601691300', 0, 'zsxq.iocoder.cn', 111, '2024-03-11 00:00:00', 20, '1', '2022-02-22 00:56:14', '1', '2023-11-06 11:41:47', b'0');
3620   -INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'test.iocoder.cn', 111, '2022-04-30 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2023-11-06 11:41:53', b'0');
  3377 +INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, '小租户', 110, '小王2', '15601691300', 0, 'zsxq.iocoder.cn', 111, '2024-03-11 00:00:00', 20, '1', '2022-02-22 00:56:14', '1', '2024-05-04 22:37:03', b'0');
  3378 +INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'test.iocoder.cn', 111, '2022-04-29 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2024-05-04 22:36:09', b'0');
3621 3379 COMMIT;
3622 3380  
3623 3381 -- ----------------------------
... ... @@ -3739,18 +3497,17 @@ CREATE TABLE `system_users` (
3739 3497 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
3740 3498 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
3741 3499 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
3742   - PRIMARY KEY (`id`) USING BTREE,
3743   - UNIQUE INDEX `idx_username`(`username` ASC, `update_time` ASC, `tenant_id` ASC) USING BTREE
  3500 + PRIMARY KEY (`id`) USING BTREE
3744 3501 ) ENGINE = InnoDB AUTO_INCREMENT = 139 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户信息表';
3745 3502  
3746 3503 -- ----------------------------
3747 3504 -- Records of system_users
3748 3505 -- ----------------------------
3749 3506 BEGIN;
3750   -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]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/96c787a2ce88bf6d0ce3cd8b6cf5314e80e7703cd41bf4af8cd2e2909dbd6b6d.png', 0, '0:0:0:0:0:0:0:1', '2024-04-29 21:50:32', 'admin', '2021-01-05 17:03:47', NULL, '2024-04-29 21:50:32', b'0', 1);
  3507 +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]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/96c787a2ce88bf6d0ce3cd8b6cf5314e80e7703cd41bf4af8cd2e2909dbd6b6d.png', 0, '0:0:0:0:0:0:0:1', '2024-07-07 17:15:51', 'admin', '2021-01-05 17:03:47', NULL, '2024-07-07 17:15:51', b'0', 1);
3751 3508 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$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '127.0.0.1', '2022-07-09 23:03:33', '', '2021-01-07 09:07:17', NULL, '2022-07-09 23:03:33', b'0', 1);
3752 3509 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$10$YMpimV4T6BtDhIaA8jSW.u8UTGBeGhc/qwXP4oxoMr4mOw9.qttt6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '0:0:0:0:0:0:0:1', '2024-03-18 21:09:04', '', '2021-01-13 23:50:35', NULL, '2024-03-18 21:09:04', b'0', 1);
3753   -INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$KhExCYl7lx6eWWZYKsibKOZ8IBJRyuNuCcEOLQ11RYhJKgHmlSwK.', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-03-26 07:11:35', '', '2021-01-21 02:13:53', NULL, '2024-03-26 07:11:35', b'0', 1);
  3510 +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$KhExCYl7lx6eWWZYKsibKOZ8IBJRyuNuCcEOLQ11RYhJKgHmlSwK.', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-06-17 14:23:23', '', '2021-01-21 02:13:53', NULL, '2024-06-17 14:23:23', b'0', 1);
3754 3511 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);
3755 3512 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);
3756 3513 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
... ... @@ -77,6 +77,7 @@
77 77 <jimureport.version>1.6.6-beta2</jimureport.version>
78 78 <xercesImpl.version>2.12.2</xercesImpl.version>
79 79 <weixin-java.version>4.6.0</weixin-java.version>
  80 + <spring-ai-bom.version>0.8.0</spring-ai-bom.version>
80 81 </properties>
81 82  
82 83 <dependencyManagement>
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/ServiceErrorCodeRange.java
... ... @@ -43,4 +43,6 @@ public class ServiceErrorCodeRange {
43 43  
44 44 // 模块 crm 错误码区间 [1-020-000-000 ~ 1-021-000-000)
45 45  
  46 + // 模块 ai 错误码区间 [1-022-000-000 ~ 1-023-000-000)
  47 +
46 48 }
... ...
yudao-module-ai/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <groupId>cn.iocoder.boot</groupId>
  7 + <artifactId>yudao</artifactId>
  8 + <version>${revision}</version>
  9 + </parent>
  10 + <modelVersion>4.0.0</modelVersion>
  11 + <modules>
  12 + <module>yudao-module-ai-api</module>
  13 + <module>yudao-module-ai-biz</module>
  14 + <module>yudao-spring-boot-starter-ai</module>
  15 + </modules>
  16 + <packaging>pom</packaging>
  17 + <artifactId>yudao-module-ai</artifactId>
  18 +
  19 + <name>${project.artifactId}</name>
  20 + <description>
  21 + ai 模块下,接入 LLM 大模型,支持聊天、绘图、音乐、写作、思维脑图等功能。
  22 + 目前已接入各种模型,不限于:
  23 + 国内:通义千问、文心一言、讯飞星火、智谱 GLM、DeepSeek
  24 + 国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno
  25 + </description>
  26 +
  27 +</project>
0 28 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-api/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <groupId>cn.iocoder.boot</groupId>
  7 + <artifactId>yudao-module-ai</artifactId>
  8 + <version>${revision}</version>
  9 + </parent>
  10 + <modelVersion>4.0.0</modelVersion>
  11 + <artifactId>yudao-module-ai-api</artifactId>
  12 + <packaging>jar</packaging>
  13 +
  14 + <name>${project.artifactId}</name>
  15 + <description>
  16 + ai 模块 API,暴露给其它模块调用
  17 + </description>
  18 +
  19 + <dependencies>
  20 + <dependency>
  21 + <groupId>cn.iocoder.boot</groupId>
  22 + <artifactId>yudao-common</artifactId>
  23 + </dependency>
  24 +
  25 + <!-- 参数校验 -->
  26 + <dependency>
  27 + <groupId>org.springframework.boot</groupId>
  28 + <artifactId>spring-boot-starter-validation</artifactId>
  29 + <optional>true</optional>
  30 + </dependency>
  31 + </dependencies>
  32 +</project>
0 33 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/api/package-info.java 0 → 100644
  1 +/**
  2 + * 占位,没有特别的作用
  3 + */
  4 +package cn.iocoder.yudao.module.ai.api;
0 5 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.enums;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * AI 内置聊天角色的枚举
  11 + *
  12 + * @author xiaoxin
  13 + */
  14 +@AllArgsConstructor
  15 +@Getter
  16 +public enum AiChatRoleEnum implements IntArrayValuable {
  17 +
  18 + AI_WRITE_ROLE(1, "写作助手", """
  19 + 你是一位出色的写作助手,能够帮助用户生成创意和灵感,并在用户提供场景和提示词时生成对应的回复。你的任务包括:
  20 + 1. 撰写建议:根据用户提供的主题或问题,提供详细的写作建议、情节发展方向、角色设定以及背景描写,确保内容结构清晰、有逻辑。
  21 + 2. 回复生成:根据用户提供的场景和提示词,生成合适的对话或文字回复,确保语气和风格符合场景需求。
  22 + 除此之外不需要除了正文内容外的其他回复,如标题、开头、任何解释性语句或道歉。
  23 + """),
  24 +
  25 + AI_MIND_MAP_ROLE(2, "脑图助手", """
  26 + 你是一位非常优秀的思维导图助手,你会把用户的所有提问都总结成思维导图,然后以 Markdown 格式输出。markdown 只需要输出一级标题,二级标题,三级标题,四级标题,最多输出四级,除此之外不要输出任何其他 markdown 标记。下面是一个合格的例子:
  27 + # Geek-AI 助手
  28 + ## 完整的开源系统
  29 + ### 前端开源
  30 + ### 后端开源
  31 + ## 支持各种大模型
  32 + ### OpenAI
  33 + ### Azure
  34 + ### 文心一言
  35 + ### 通义千问
  36 + ## 集成多种收费方式
  37 + ### 支付宝
  38 + ### 微信
  39 + 除此之外不要任何解释性语句。
  40 + """);
  41 +
  42 + // TODO @xin:这个 role 是不是删除掉好点哈。= = 目前主要是没做角色枚举。这里多了 role 反倒容易误解哈
  43 + /**
  44 + * 角色
  45 + */
  46 + private final Integer role;
  47 + /**
  48 + * 角色名
  49 + */
  50 + private final String name;
  51 +
  52 + /**
  53 + * 角色设定
  54 + */
  55 + private final String systemMessage;
  56 +
  57 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiChatRoleEnum::getRole).toArray();
  58 +
  59 + @Override
  60 + public int[] array() {
  61 + return ARRAYS;
  62 + }
  63 +
  64 +}
... ...
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/DictTypeConstants.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.enums;
  2 +
  3 +/**
  4 + * AI 字典类型的枚举类
  5 + *
  6 + * @author xiaoxin
  7 + */
  8 +public interface DictTypeConstants {
  9 +
  10 + // ========== AI Write ==========
  11 + String AI_WRITE_FORMAT = "ai_write_format"; // 写作格式
  12 + String AI_WRITE_LENGTH = "ai_write_length"; // 写作长度
  13 + String AI_WRITE_LANGUAGE = "ai_write_language"; // 写作语言
  14 + String AI_WRITE_TONE = "ai_write_tone"; // 写作语气
  15 +
  16 +}
... ...
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.enums;
  2 +
  3 +import cn.iocoder.yudao.framework.common.exception.ErrorCode;
  4 +
  5 +/**
  6 + * AI 错误码枚举类
  7 + *
  8 + * ai 系统,使用 1-040-000-000 段
  9 + */
  10 +public interface ErrorCodeConstants {
  11 +
  12 + // ========== API 密钥 1-040-000-000 ==========
  13 + ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "API 密钥不存在");
  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 +
  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, "操作失败,找不到默认聊天模型");
  23 +
  24 + // ========== API 聊天模型 1-040-002-000 ==========
  25 + ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "聊天角色不存在");
  26 + ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "聊天角色({})已禁用!");
  27 +
  28 + // ========== API 聊天会话 1-040-003-000 ==========
  29 +
  30 + ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "对话不存在!");
  31 + ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "操作失败,该聊天模型的配置不完整");
  32 +
  33 + // ========== API 聊天消息 1-040-004-000 ==========
  34 +
  35 + ErrorCode CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "消息不存在!");
  36 + ErrorCode CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "对话生成异常!");
  37 +
  38 + // ========== 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, "图片绘画失败! {}");
  44 +
  45 + // ========== API 音乐 1-040-006-000 ==========
  46 + ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_022_006_000, "音乐不存在!");
  47 +
  48 +
  49 + // ========== API 写作 1-022-007-000 ==========
  50 + ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_022_007_000, "作文不存在!");
  51 + ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_022_07_001, "写作生成异常!");
  52 +
  53 +}
... ...
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/image/AiImageStatusEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.enums.image;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * AI 绘画状态的枚举
  8 + *
  9 + * @author fansili
  10 + */
  11 +@AllArgsConstructor
  12 +@Getter
  13 +public enum AiImageStatusEnum {
  14 +
  15 + IN_PROGRESS(10, "进行中"),
  16 + SUCCESS(20, "已完成"),
  17 + FAIL(30, "已失败");
  18 +
  19 + /**
  20 + * 状态
  21 + */
  22 + private final Integer status;
  23 + /**
  24 + * 状态名
  25 + */
  26 + private final String name;
  27 +
  28 + public static AiImageStatusEnum valueOfStatus(Integer status) {
  29 + for (AiImageStatusEnum statusEnum : AiImageStatusEnum.values()) {
  30 + if (statusEnum.getStatus().equals(status)) {
  31 + return statusEnum;
  32 + }
  33 + }
  34 + throw new IllegalArgumentException("未知会话状态: " + status);
  35 + }
  36 +
  37 +}
... ...
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicGenerateModeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.enums.music;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * AI 音乐生成模式的枚举
  11 + *
  12 + * @author xiaoxin
  13 + */
  14 +@AllArgsConstructor
  15 +@Getter
  16 +public enum AiMusicGenerateModeEnum implements IntArrayValuable {
  17 +
  18 + DESCRIPTION(1, "描述模式"),
  19 + LYRIC(2, "歌词模式");
  20 +
  21 + /**
  22 + * 模式
  23 + */
  24 + private final Integer mode;
  25 + /**
  26 + * 模式名
  27 + */
  28 + private final String name;
  29 +
  30 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiMusicGenerateModeEnum::getMode).toArray();
  31 +
  32 + @Override
  33 + public int[] array() {
  34 + return ARRAYS;
  35 + }
  36 +
  37 +}
... ...
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicStatusEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.enums.music;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * AI 音乐状态的枚举
  11 + *
  12 + * @author xiaoxin
  13 + */
  14 +@AllArgsConstructor
  15 +@Getter
  16 +public enum AiMusicStatusEnum implements IntArrayValuable {
  17 +
  18 + IN_PROGRESS(10, "进行中"),
  19 + SUCCESS(20, "已完成"),
  20 + FAIL(30, "已失败");
  21 +
  22 + /**
  23 + * 状态
  24 + */
  25 + private final Integer status;
  26 +
  27 + /**
  28 + * 状态名
  29 + */
  30 + private final String name;
  31 +
  32 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiMusicStatusEnum::getStatus).toArray();
  33 +
  34 + @Override
  35 + public int[] array() {
  36 + return ARRAYS;
  37 + }
  38 +
  39 +}
... ...
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.enums.write;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * AI 写作类型的枚举
  11 + *
  12 + * @author xiaoxin
  13 + */
  14 +@AllArgsConstructor
  15 +@Getter
  16 +public enum AiWriteTypeEnum implements IntArrayValuable {
  17 +
  18 + WRITING(1, "撰写", "请撰写一篇关于 [{}] 的文章。文章的内容格式:{},语气:{},语言:{},长度:{}。请确保涵盖主要内容,不需要除了正文内容外的其他回复,如标题、额外的解释或道歉。"),
  19 + REPLY(2, "回复", "请针对如下内容:[{}] 做个回复。回复内容参考:[{}], 回复格式:{},语气:{},语言:{},长度:{}。不需要除了正文内容外的其他回复,如标题、开头、额外的解释或道歉。");
  20 +
  21 + /**
  22 + * 类型
  23 + */
  24 + private final Integer type;
  25 + /**
  26 + * 类型名
  27 + */
  28 + private final String name;
  29 +
  30 + /**
  31 + * 模版
  32 + */
  33 + private final String prompt;
  34 +
  35 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiWriteTypeEnum::getType).toArray();
  36 +
  37 + @Override
  38 + public int[] array() {
  39 + return ARRAYS;
  40 + }
  41 +
  42 +}
... ...
yudao-module-ai/yudao-module-ai-biz/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <groupId>cn.iocoder.boot</groupId>
  7 + <artifactId>yudao-module-ai</artifactId>
  8 + <version>${revision}</version>
  9 + </parent>
  10 + <modelVersion>4.0.0</modelVersion>
  11 + <artifactId>yudao-module-ai-biz</artifactId>
  12 +
  13 + <name>${project.artifactId}</name>
  14 + <description>
  15 + ai 模块下,接入 LLM 大模型,支持聊天、绘图、音乐、写作、思维脑图等功能。
  16 + 目前已接入各种模型,不限于:
  17 + 国内:通义千问、文心一言、讯飞星火、智谱 GLM、DeepSeek
  18 + 国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno
  19 + </description>
  20 +
  21 + <dependencies>
  22 + <dependency>
  23 + <groupId>cn.iocoder.boot</groupId>
  24 + <artifactId>yudao-module-ai-api</artifactId>
  25 + <version>${revision}</version>
  26 + </dependency>
  27 +
  28 + <!-- 业务组件 -->
  29 + <dependency>
  30 + <groupId>cn.iocoder.boot</groupId>
  31 + <artifactId>yudao-spring-boot-starter-ai</artifactId>
  32 + <version>${revision}</version>
  33 + </dependency>
  34 +
  35 + <dependency>
  36 + <groupId>cn.iocoder.boot</groupId>
  37 + <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
  38 + </dependency>
  39 +
  40 + <!-- Web 相关 -->
  41 + <dependency>
  42 + <groupId>cn.iocoder.boot</groupId>
  43 + <artifactId>yudao-spring-boot-starter-security</artifactId>
  44 + </dependency>
  45 +
  46 + <!-- DB 相关 -->
  47 + <dependency>
  48 + <groupId>cn.iocoder.boot</groupId>
  49 + <artifactId>yudao-spring-boot-starter-mybatis</artifactId>
  50 + </dependency>
  51 +
  52 + <!-- Job 相关 -->
  53 + <dependency>
  54 + <groupId>cn.iocoder.boot</groupId>
  55 + <artifactId>yudao-spring-boot-starter-job</artifactId>
  56 + </dependency>
  57 +
  58 + <!-- Test 测试相关 -->
  59 + <dependency>
  60 + <groupId>cn.iocoder.boot</groupId>
  61 + <artifactId>yudao-spring-boot-starter-test</artifactId>
  62 + </dependency>
  63 + </dependencies>
  64 +</project>
0 65 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.chat;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
  5 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
  10 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
  11 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
  12 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
  13 +import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService;
  14 +import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService;
  15 +import io.swagger.v3.oas.annotations.Operation;
  16 +import io.swagger.v3.oas.annotations.Parameter;
  17 +import io.swagger.v3.oas.annotations.tags.Tag;
  18 +import jakarta.annotation.Resource;
  19 +import jakarta.validation.Valid;
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.validation.annotation.Validated;
  22 +import org.springframework.web.bind.annotation.*;
  23 +
  24 +import java.util.List;
  25 +import java.util.Map;
  26 +
  27 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  28 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  29 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  30 +
  31 +@Tag(name = "管理后台 - AI 聊天对话")
  32 +@RestController
  33 +@RequestMapping("/ai/chat/conversation")
  34 +@Validated
  35 +public class AiChatConversationController {
  36 +
  37 + @Resource
  38 + private AiChatConversationService chatConversationService;
  39 + @Resource
  40 + private AiChatMessageService chatMessageService;
  41 +
  42 + @PostMapping("/create-my")
  43 + @Operation(summary = "创建【我的】聊天对话")
  44 + public CommonResult<Long> createChatConversationMy(@RequestBody @Valid AiChatConversationCreateMyReqVO createReqVO) {
  45 + return success(chatConversationService.createChatConversationMy(createReqVO, getLoginUserId()));
  46 + }
  47 +
  48 + @PutMapping("/update-my")
  49 + @Operation(summary = "更新【我的】聊天对话")
  50 + public CommonResult<Boolean> updateChatConversationMy(@RequestBody @Valid AiChatConversationUpdateMyReqVO updateReqVO) {
  51 + chatConversationService.updateChatConversationMy(updateReqVO, getLoginUserId());
  52 + return success(true);
  53 + }
  54 +
  55 + @GetMapping("/my-list")
  56 + @Operation(summary = "获得【我的】聊天对话列表")
  57 + public CommonResult<List<AiChatConversationRespVO>> getChatConversationMyList() {
  58 + List<AiChatConversationDO> list = chatConversationService.getChatConversationListByUserId(getLoginUserId());
  59 + return success(BeanUtils.toBean(list, AiChatConversationRespVO.class));
  60 + }
  61 +
  62 + @GetMapping("/get-my")
  63 + @Operation(summary = "获得【我的】聊天对话")
  64 + @Parameter(name = "id", required = true, description = "对话编号", example = "1024")
  65 + public CommonResult<AiChatConversationRespVO> getChatConversationMy(@RequestParam("id") Long id) {
  66 + AiChatConversationDO conversation = chatConversationService.getChatConversation(id);
  67 + if (conversation != null && ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
  68 + conversation = null;
  69 + }
  70 + return success(BeanUtils.toBean(conversation, AiChatConversationRespVO.class));
  71 + }
  72 +
  73 + @DeleteMapping("/delete-my")
  74 + @Operation(summary = "删除聊天对话")
  75 + @Parameter(name = "id", required = true, description = "对话编号", example = "1024")
  76 + public CommonResult<Boolean> deleteChatConversationMy(@RequestParam("id") Long id) {
  77 + chatConversationService.deleteChatConversationMy(id, getLoginUserId());
  78 + return success(true);
  79 + }
  80 +
  81 + @DeleteMapping("/delete-by-unpinned")
  82 + @Operation(summary = "删除未置顶的聊天对话")
  83 + public CommonResult<Boolean> deleteChatConversationMyByUnpinned() {
  84 + chatConversationService.deleteChatConversationMyByUnpinned(getLoginUserId());
  85 + return success(true);
  86 + }
  87 +
  88 + // ========== 对话管理 ==========
  89 +
  90 + @GetMapping("/page")
  91 + @Operation(summary = "获得对话分页", description = "用于【对话管理】菜单")
  92 + @PreAuthorize("@ss.hasPermission('ai:chat-conversation:query')")
  93 + public CommonResult<PageResult<AiChatConversationRespVO>> getChatConversationPage(AiChatConversationPageReqVO pageReqVO) {
  94 + PageResult<AiChatConversationDO> pageResult = chatConversationService.getChatConversationPage(pageReqVO);
  95 + if (CollUtil.isEmpty(pageResult.getList())) {
  96 + return success(PageResult.empty());
  97 + }
  98 + // 拼接关联数据
  99 + Map<Long, Integer> messageCountMap = chatMessageService.getChatMessageCountMap(
  100 + convertList(pageResult.getList(), AiChatConversationDO::getId));
  101 + return success(BeanUtils.toBean(pageResult, AiChatConversationRespVO.class,
  102 + conversation -> conversation.setMessageCount(messageCountMap.getOrDefault(conversation.getId(), 0))));
  103 + }
  104 +
  105 + @Operation(summary = "管理员删除对话")
  106 + @DeleteMapping("/delete-by-admin")
  107 + @Parameter(name = "id", required = true, description = "对话编号", example = "1024")
  108 + @PreAuthorize("@ss.hasPermission('ai:chat-conversation:delete')")
  109 + public CommonResult<Boolean> deleteChatConversationByAdmin(@RequestParam("id") Long id) {
  110 + chatConversationService.deleteChatConversationByAdmin(id);
  111 + return success(true);
  112 + }
  113 +
  114 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http 0 → 100644
  1 +### 发送消息(段式)
  2 +POST {{baseUrl}}/ai/chat/message/send
  3 +Content-Type: application/json
  4 +Authorization: {{token}}
  5 +tenant-id: {{adminTenentId}}
  6 +
  7 +{
  8 + "conversationId": "1781604279872581724",
  9 + "content": "你是 OpenAI 么?"
  10 +}
  11 +
  12 +### 发送消息(流式)
  13 +POST {{baseUrl}}/ai/chat/message/send-stream
  14 +Content-Type: application/json
  15 +Authorization: {{token}}
  16 +tenant-id: {{adminTenentId}}
  17 +
  18 +{
  19 + "conversationId": "1781604279872581724",
  20 + "content": "1+1=?"
  21 +}
  22 +
  23 +### 获得指定对话的消息列表
  24 +GET {{baseUrl}}/ai/chat/message/list-by-conversation-id?conversationId=1781604279872581649
  25 +Authorization: {{token}}
  26 +
  27 +### 删除消息
  28 +DELETE {{baseUrl}}/ai/chat/message/delete?id=50
  29 +Authorization: {{token}}
0 30 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.chat;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
  5 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  8 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
  10 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
  11 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
  12 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
  13 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
  14 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
  15 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  16 +import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService;
  17 +import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService;
  18 +import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
  19 +import io.swagger.v3.oas.annotations.Operation;
  20 +import io.swagger.v3.oas.annotations.Parameter;
  21 +import io.swagger.v3.oas.annotations.tags.Tag;
  22 +import jakarta.annotation.Resource;
  23 +import jakarta.annotation.security.PermitAll;
  24 +import jakarta.validation.Valid;
  25 +import lombok.extern.slf4j.Slf4j;
  26 +import org.springframework.http.MediaType;
  27 +import org.springframework.security.access.prepost.PreAuthorize;
  28 +import org.springframework.web.bind.annotation.*;
  29 +import reactor.core.publisher.Flux;
  30 +
  31 +import java.util.Collections;
  32 +import java.util.List;
  33 +import java.util.Map;
  34 +
  35 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  36 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  37 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  38 +
  39 +@Tag(name = "管理后台 - 聊天消息")
  40 +@RestController
  41 +@RequestMapping("/ai/chat/message")
  42 +@Slf4j
  43 +public class AiChatMessageController {
  44 +
  45 + @Resource
  46 + private AiChatMessageService chatMessageService;
  47 + @Resource
  48 + private AiChatConversationService chatConversationService;
  49 + @Resource
  50 + private AiChatRoleService chatRoleService;
  51 +
  52 + @Operation(summary = "发送消息(段式)", description = "一次性返回,响应较慢")
  53 + @PostMapping("/send")
  54 + public CommonResult<AiChatMessageSendRespVO> sendMessage(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {
  55 + return success(chatMessageService.sendMessage(sendReqVO, getLoginUserId()));
  56 + }
  57 +
  58 + @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快")
  59 + @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
  60 + @PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题
  61 + public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {
  62 + return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId());
  63 + }
  64 +
  65 + @Operation(summary = "获得指定对话的消息列表")
  66 + @GetMapping("/list-by-conversation-id")
  67 + @Parameter(name = "conversationId", required = true, description = "对话编号", example = "1024")
  68 + public CommonResult<List<AiChatMessageRespVO>> getChatMessageListByConversationId(
  69 + @RequestParam("conversationId") Long conversationId) {
  70 + AiChatConversationDO conversation = chatConversationService.getChatConversation(conversationId);
  71 + if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
  72 + return success(Collections.emptyList());
  73 + }
  74 + List<AiChatMessageDO> messageList = chatMessageService.getChatMessageListByConversationId(conversationId);
  75 + return success(BeanUtils.toBean(messageList, AiChatMessageRespVO.class));
  76 + }
  77 +
  78 + @Operation(summary = "删除消息")
  79 + @DeleteMapping("/delete")
  80 + @Parameter(name = "id", required = true, description = "消息编号", example = "1024")
  81 + public CommonResult<Boolean> deleteChatMessage(@RequestParam("id") Long id) {
  82 + chatMessageService.deleteChatMessage(id, getLoginUserId());
  83 + return success(true);
  84 + }
  85 +
  86 + @Operation(summary = "删除指定对话的消息")
  87 + @DeleteMapping("/delete-by-conversation-id")
  88 + @Parameter(name = "conversationId", required = true, description = "对话编号", example = "1024")
  89 + public CommonResult<Boolean> deleteChatMessageByConversationId(@RequestParam("conversationId") Long conversationId) {
  90 + chatMessageService.deleteChatMessageByConversationId(conversationId, getLoginUserId());
  91 + return success(true);
  92 + }
  93 +
  94 + // ========== 对话管理 ==========
  95 +
  96 + @GetMapping("/page")
  97 + @Operation(summary = "获得消息分页", description = "用于【对话管理】菜单")
  98 + @PreAuthorize("@ss.hasPermission('ai:chat-conversation:query')")
  99 + public CommonResult<PageResult<AiChatMessageRespVO>> getChatMessagePage(AiChatMessagePageReqVO pageReqVO) {
  100 + PageResult<AiChatMessageDO> pageResult = chatMessageService.getChatMessagePage(pageReqVO);
  101 + if (CollUtil.isEmpty(pageResult.getList())) {
  102 + return success(PageResult.empty());
  103 + }
  104 + // 拼接数据
  105 + Map<Long, AiChatRoleDO> roleMap = chatRoleService.getChatRoleMap(
  106 + convertSet(pageResult.getList(), AiChatMessageDO::getRoleId));
  107 + return success(BeanUtils.toBean(pageResult, AiChatMessageRespVO.class,
  108 + respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(), role -> respVO.setRoleName(role.getName()))));
  109 + }
  110 +
  111 + @Operation(summary = "管理员删除消息")
  112 + @DeleteMapping("/delete-by-admin")
  113 + @Parameter(name = "id", required = true, description = "消息编号", example = "1024")
  114 + @PreAuthorize("@ss.hasPermission('ai:chat-message:delete')")
  115 + public CommonResult<Boolean> deleteChatMessageByAdmin(@RequestParam("id") Long id) {
  116 + chatMessageService.deleteChatMessageByAdmin(id);
  117 + return success(true);
  118 + }
  119 +
  120 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateMyReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - AI 聊天对话创建【我的】 Request VO")
  7 +@Data
  8 +public class AiChatConversationCreateMyReqVO {
  9 +
  10 + @Schema(description = "聊天角色编号", example = "666")
  11 + private Long roleId;
  12 +
  13 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@Schema(description = "管理后台 - AI 聊天对话的分页 Request VO")
  13 +@Data
  14 +public class AiChatConversationPageReqVO extends PageParam {
  15 +
  16 + @Schema(description = "用户编号", example = "1024")
  17 + private Long userId;
  18 +
  19 + @Schema(description = "对话标题", example = "你好")
  20 + private String title;
  21 +
  22 + @Schema(description = "创建时间")
  23 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  24 + private LocalDateTime[] createTime;
  25 +
  26 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;
  2 +
  3 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  4 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  5 +import com.fhs.core.trans.anno.Trans;
  6 +import com.fhs.core.trans.constant.TransType;
  7 +import com.fhs.core.trans.vo.VO;
  8 +import io.swagger.v3.oas.annotations.media.Schema;
  9 +import lombok.Data;
  10 +
  11 +import java.time.LocalDateTime;
  12 +
  13 +@Schema(description = "管理后台 - AI 聊天对话 Response VO")
  14 +@Data
  15 +public class AiChatConversationRespVO implements VO {
  16 +
  17 + @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  18 + private Long id;
  19 +
  20 + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  21 + private Long userId;
  22 +
  23 + @Schema(description = "对话标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是一个标题")
  24 + private String title;
  25 +
  26 + @Schema(description = "是否置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  27 + private Boolean pinned;
  28 +
  29 + @Schema(description = "角色编号", example = "1")
  30 + @Trans(type = TransType.SIMPLE, target = AiChatRoleDO.class, fields = {"name", "avatar"}, refs = {"roleName", "roleAvatar"})
  31 + private Long roleId;
  32 +
  33 + @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  34 + @Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = "name", ref = "modelName")
  35 + private Long modelId;
  36 +
  37 + @Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922")
  38 + private String model;
  39 +
  40 + @Schema(description = "模型名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
  41 + private String modelName;
  42 +
  43 + @Schema(description = "角色设定", example = "一个快乐的程序员")
  44 + private String systemMessage;
  45 +
  46 + @Schema(description = "温度参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.8")
  47 + private Double temperature;
  48 +
  49 + @Schema(description = "单条回复的最大 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
  50 + private Integer maxTokens;
  51 +
  52 + @Schema(description = "上下文的最大 Message 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  53 + private Integer maxContexts;
  54 +
  55 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  56 + private LocalDateTime createTime;
  57 +
  58 + // ========== 关联 role 信息 ==========
  59 +
  60 + @Schema(description = "角色头像", example = "https://www.iocoder.cn/1.png")
  61 + private String roleAvatar;
  62 +
  63 + @Schema(description = "角色名字", example = "小黄")
  64 + private String roleName;
  65 +
  66 + // ========== 仅在【对话管理】时加载 ==========
  67 +
  68 + @Schema(description = "消息数量", example = "20")
  69 + private Integer messageCount;
  70 +
  71 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - AI 聊天对话更新【我的】 Request VO")
  8 +@Data
  9 +public class AiChatConversationUpdateMyReqVO {
  10 +
  11 + @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  12 + @NotNull(message = "对话编号不能为空")
  13 + private Long id;
  14 +
  15 + @Schema(description = "对话标题", example = "我是一个标题")
  16 + private String title;
  17 +
  18 + @Schema(description = "是否置顶", example = "true")
  19 + private Boolean pinned;
  20 +
  21 + @Schema(description = "模型编号", example = "1")
  22 + private Long modelId;
  23 +
  24 + @Schema(description = "角色设定", example = "一个快乐的程序员")
  25 + private String systemMessage;
  26 +
  27 + @Schema(description = "温度参数", example = "0.8")
  28 + private Double temperature;
  29 +
  30 + @Schema(description = "单条回复的最大 Token 数量", example = "4096")
  31 + private Integer maxTokens;
  32 +
  33 + @Schema(description = "上下文的最大 Message 数量", example = "10")
  34 + private Integer maxContexts;
  35 +
  36 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessagePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@Schema(description = "管理后台 - AI 聊天消息的分页 Request VO")
  13 +@Data
  14 +public class AiChatMessagePageReqVO extends PageParam {
  15 +
  16 + @Schema(description = "对话编号", example = "2048")
  17 + private Long conversationId;
  18 +
  19 + @Schema(description = "用户编号", example = "1024")
  20 + private Long userId;
  21 +
  22 + @Schema(description = "消息内容", example = "你好")
  23 + private String content;
  24 +
  25 + @Schema(description = "创建时间")
  26 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  27 + private LocalDateTime[] createTime;
  28 +
  29 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - AI 聊天消息 Response VO")
  9 +@Data
  10 +public class AiChatMessageRespVO {
  11 +
  12 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  16 + private Long conversationId;
  17 +
  18 + @Schema(description = "回复消息编号", example = "1024")
  19 + private Long replyId;
  20 +
  21 + @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "role")
  22 + private String type; // 参见 MessageType 枚举类
  23 +
  24 + @Schema(description = "用户编号", example = "4096")
  25 + private Long userId;
  26 +
  27 + @Schema(description = "角色编号", example = "888")
  28 + private Long roleId;
  29 +
  30 + @Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "gpt-3.5-turbo")
  31 + private String model;
  32 +
  33 + @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123")
  34 + private Long modelId;
  35 +
  36 + @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊")
  37 + private String content;
  38 +
  39 + @Schema(description = "是否携带上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  40 + private Boolean useContext;
  41 +
  42 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51")
  43 + private LocalDateTime createTime;
  44 +
  45 + // ========== 仅在【对话管理】时加载 ==========
  46 +
  47 + @Schema(description = "角色名字", example = "小黄")
  48 + private String roleName;
  49 +
  50 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import jakarta.validation.constraints.Size;
  7 +import lombok.Data;
  8 +import lombok.experimental.Accessors;
  9 +
  10 +@Schema(description = "管理后台 - AI 聊天消息发送 Request VO")
  11 +@Data
  12 +public class AiChatMessageSendReqVO {
  13 +
  14 + @Schema(description = "聊天对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  15 + @NotNull(message = "聊天对话编号不能为空")
  16 + private Long conversationId;
  17 +
  18 + @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "帮我写个 Java 算法")
  19 + @NotEmpty(message = "聊天内容不能为空")
  20 + private String content;
  21 +
  22 + @Schema(description = "是否携带上下文", example = "true")
  23 + private Boolean useContext;
  24 +
  25 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - AI 聊天消息发送 Response VO")
  9 +@Data
  10 +public class AiChatMessageSendRespVO {
  11 +
  12 + @Schema(description = "发送消息", requiredMode = Schema.RequiredMode.REQUIRED)
  13 + private Message send;
  14 +
  15 + @Schema(description = "接收消息", requiredMode = Schema.RequiredMode.REQUIRED)
  16 + private Message receive;
  17 +
  18 + @Schema(description = "消息")
  19 + @Data
  20 + public static class Message {
  21 +
  22 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  23 + private Long id;
  24 +
  25 + @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "role")
  26 + private String type; // 参见 MessageType 枚举类
  27 +
  28 + @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊")
  29 + private String content;
  30 +
  31 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  32 + private LocalDateTime createTime;
  33 +
  34 + }
  35 +
  36 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.http 0 → 100644
  1 +### 生成图片:OpenAI(DALL)
  2 +POST {{baseUrl}}/ai/image/draw
  3 +Content-Type: application/json
  4 +Authorization: {{token}}
  5 +
  6 +{
  7 + "platform": "OpenAI",
  8 + "prompt": "可爱的小喵星人",
  9 + "model": "dall-e-3",
  10 + "height": "1024",
  11 + "width": "1024",
  12 + "options": {
  13 + "style": "vivid"
  14 + }
  15 +}
  16 +
  17 +### 生成图片:StableDiffusion
  18 +POST {{baseUrl}}/ai/image/draw
  19 +Content-Type: application/json
  20 +Authorization: {{token}}
  21 +
  22 +{
  23 + "platform": "StableDiffusion",
  24 + "prompt": "中国长城",
  25 + "model": "stable-diffusion-v1-6",
  26 + "height": "1024",
  27 + "width": "1024",
  28 + "style": "vivid"
  29 +}
  30 +
  31 +### 生成图片:生成图片(Midjourney)
  32 +POST {{baseUrl}}/ai/image/midjourney/imagine
  33 +Content-Type: application/json
  34 +Authorization: {{token}}
  35 +
  36 +{
  37 + "prompt": "中国旗袍",
  38 + "model": "midjourney",
  39 + "width": "1",
  40 + "height": "1",
  41 + "version": "6.0"
  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/image/AiImageController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.image;
  2 +
  3 +import cn.hutool.core.util.ObjUtil;
  4 +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
  5 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
  10 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageReqVO;
  11 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageRespVO;
  12 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageUpdateReqVO;
  13 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;
  14 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;
  15 +import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
  16 +import cn.iocoder.yudao.module.ai.service.image.AiImageService;
  17 +import io.swagger.v3.oas.annotations.Operation;
  18 +import io.swagger.v3.oas.annotations.Parameter;
  19 +import io.swagger.v3.oas.annotations.tags.Tag;
  20 +import jakarta.annotation.Resource;
  21 +import jakarta.annotation.security.PermitAll;
  22 +import jakarta.validation.Valid;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.springframework.security.access.prepost.PreAuthorize;
  25 +import org.springframework.validation.annotation.Validated;
  26 +import org.springframework.web.bind.annotation.*;
  27 +
  28 +import java.util.List;
  29 +
  30 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  31 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  32 +
  33 +@Tag(name = "管理后台 - AI 绘画")
  34 +@RestController
  35 +@RequestMapping("/ai/image")
  36 +@Slf4j
  37 +public class AiImageController {
  38 +
  39 + @Resource
  40 + private AiImageService imageService;
  41 +
  42 + @GetMapping("/my-page")
  43 + @Operation(summary = "获取【我的】绘图分页")
  44 + public CommonResult<PageResult<AiImageRespVO>> getImagePageMy(@Validated PageParam pageReqVO) {
  45 + PageResult<AiImageDO> pageResult = imageService.getImagePageMy(getLoginUserId(), pageReqVO);
  46 + return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));
  47 + }
  48 +
  49 + @GetMapping("/get-my")
  50 + @Operation(summary = "获取【我的】绘图记录")
  51 + @Parameter(name = "id", required = true, description = "绘画编号", example = "1024")
  52 + public CommonResult<AiImageRespVO> getImageMy(@RequestParam("id") Long id) {
  53 + AiImageDO image = imageService.getImage(id);
  54 + if (image == null || ObjUtil.notEqual(getLoginUserId(), image.getUserId())) {
  55 + return success(null);
  56 + }
  57 + return success(BeanUtils.toBean(image, AiImageRespVO.class));
  58 + }
  59 +
  60 + @GetMapping("/my-list-by-ids")
  61 + @Operation(summary = "获取【我的】绘图记录列表")
  62 + @Parameter(name = "ids", required = true, description = "绘画编号数组", example = "1024,2048")
  63 + public CommonResult<List<AiImageRespVO>> getImageListMyByIds(@RequestParam("ids") List<Long> ids) {
  64 + List<AiImageDO> imageList = imageService.getImageList(ids);
  65 + imageList.removeIf(item -> !ObjUtil.equal(getLoginUserId(), item.getUserId()));
  66 + return success(BeanUtils.toBean(imageList, AiImageRespVO.class));
  67 + }
  68 +
  69 + @Operation(summary = "生成图片")
  70 + @PostMapping("/draw")
  71 + public CommonResult<Long> drawImage(@Valid @RequestBody AiImageDrawReqVO drawReqVO) {
  72 + return success(imageService.drawImage(getLoginUserId(), drawReqVO));
  73 + }
  74 +
  75 + @Operation(summary = "删除【我的】绘画记录")
  76 + @DeleteMapping("/delete-my")
  77 + @Parameter(name = "id", required = true, description = "绘画编号", example = "1024")
  78 + public CommonResult<Boolean> deleteImageMy(@RequestParam("id") Long id) {
  79 + imageService.deleteImageMy(id, getLoginUserId());
  80 + return success(true);
  81 + }
  82 +
  83 + // ================ midjourney 专属 ================
  84 +
  85 + @Operation(summary = "【Midjourney】生成图片")
  86 + @PostMapping("/midjourney/imagine")
  87 + public CommonResult<Long> midjourneyImagine(@Valid @RequestBody AiMidjourneyImagineReqVO reqVO) {
  88 + Long imageId = imageService.midjourneyImagine(getLoginUserId(), reqVO);
  89 + return success(imageId);
  90 + }
  91 +
  92 + @Operation(summary = "【Midjourney】通知图片进展", description = "由 Midjourney Proxy 回调")
  93 + @PostMapping("/midjourney/notify") // 必须是 POST 方法,否则会报错
  94 + @PermitAll
  95 + public CommonResult<Boolean> midjourneyNotify(@Valid @RequestBody MidjourneyApi.Notify notify) {
  96 + imageService.midjourneyNotify(notify);
  97 + return success(true);
  98 + }
  99 +
  100 + @Operation(summary = "【Midjourney】Action 操作(二次生成图片)", description = "例如说:放大、缩小、U1、U2 等")
  101 + @PostMapping("/midjourney/action")
  102 + public CommonResult<Long> midjourneyAction(@Valid @RequestBody AiMidjourneyActionReqVO reqVO) {
  103 + Long imageId = imageService.midjourneyAction(getLoginUserId(), reqVO);
  104 + return success(imageId);
  105 + }
  106 +
  107 + // ================ 绘图管理 ================
  108 +
  109 + @GetMapping("/page")
  110 + @Operation(summary = "获得绘画分页")
  111 + @PreAuthorize("@ss.hasPermission('ai:image:query')")
  112 + public CommonResult<PageResult<AiImageRespVO>> getImagePage(@Valid AiImagePageReqVO pageReqVO) {
  113 + PageResult<AiImageDO> pageResult = imageService.getImagePage(pageReqVO);
  114 + return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));
  115 + }
  116 +
  117 + @PutMapping("/update")
  118 + @Operation(summary = "更新绘画")
  119 + @PreAuthorize("@ss.hasPermission('ai:image:update')")
  120 + public CommonResult<Boolean> updateImage(@Valid @RequestBody AiImageUpdateReqVO updateReqVO) {
  121 + imageService.updateImage(updateReqVO);
  122 + return success(true);
  123 + }
  124 +
  125 + @DeleteMapping("/delete")
  126 + @Operation(summary = "删除绘画")
  127 + @Parameter(name = "id", description = "编号", required = true)
  128 + @PreAuthorize("@ss.hasPermission('ai:image:delete')")
  129 + public CommonResult<Boolean> deleteImage(@RequestParam("id") Long id) {
  130 + imageService.deleteImage(id);
  131 + return success(true);
  132 + }
  133 +
  134 +}
0 135 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import jakarta.validation.constraints.Size;
  7 +import lombok.Data;
  8 +import org.springframework.ai.openai.OpenAiImageOptions;
  9 +import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
  10 +
  11 +import java.util.Map;
  12 +
  13 +@Schema(description = "管理后台 - AI 绘画 Request VO")
  14 +@Data
  15 +public class AiImageDrawReqVO {
  16 +
  17 + @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
  18 + private String platform; // 参见 AiPlatformEnum 枚举
  19 +
  20 + @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城")
  21 + @NotEmpty(message = "提示词不能为空")
  22 + @Size(max = 1200, message = "提示词最大 1200")
  23 + private String prompt;
  24 +
  25 + @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "stable-diffusion-v1-6")
  26 + @NotEmpty(message = "模型不能为空")
  27 + private String model;
  28 +
  29 + /**
  30 + * 1. dall-e-2 模型:256x256、512x512、1024x1024
  31 + * 2. dall-e-3 模型:1024x1024, 1792x1024, 或 1024x1792
  32 + */
  33 + @Schema(description = "图片高度")
  34 + @NotNull(message = "图片高度不能为空")
  35 + private Integer height;
  36 +
  37 + @Schema(description = "图片宽度")
  38 + @NotNull(message = "图片宽度不能为空")
  39 + private Integer width;
  40 +
  41 + // ========== 各平台绘画的拓展参数 ==========
  42 +
  43 + /**
  44 + * 绘制参数,不同 platform 的不同参数
  45 + *
  46 + * 1. {@link OpenAiImageOptions}
  47 + * 2. {@link StabilityAiImageOptions}
  48 + */
  49 + @Schema(description = "绘制参数")
  50 + private Map<String, String> options;
  51 +
  52 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  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;
  13 +
  14 +@Schema(description = "管理后台 - AI 绘画分页 Request VO")
  15 +@Data
  16 +public class AiImagePageReqVO extends PageParam {
  17 +
  18 + @Schema(description = "用户编号", example = "28987")
  19 + private Long userId;
  20 +
  21 + @Schema(description = "平台", example = "OpenAI")
  22 + private String platform;
  23 +
  24 + @Schema(description = "绘画状态", example = "1")
  25 + private Integer status;
  26 +
  27 + @Schema(description = "是否发布", example = "1")
  28 + private Boolean publicStatus;
  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/image/vo/AiImageRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +import java.time.LocalDateTime;
  8 +import java.util.List;
  9 +import java.util.Map;
  10 +
  11 +@Schema(description = "管理后台 - AI 绘画 Response VO")
  12 +@Data
  13 +public class AiImageRespVO {
  14 +
  15 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  16 + private Long id;
  17 +
  18 + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  19 + private Long userId;
  20 +
  21 + @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
  22 + private String platform; // 参见 AiPlatformEnum 枚举
  23 +
  24 + @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "stable-diffusion-v1-6")
  25 + private String model;
  26 +
  27 + @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "南极的小企鹅")
  28 + private String prompt;
  29 +
  30 + @Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  31 + private Integer width;
  32 +
  33 + @Schema(description = "图片高度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  34 + private Integer height;
  35 +
  36 + @Schema(description = "绘画状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  37 + private Integer status;
  38 +
  39 + @Schema(description = "是否发布", requiredMode = Schema.RequiredMode.REQUIRED, example = "public")
  40 + private Boolean publicStatus;
  41 +
  42 + @Schema(description = "图片地址", example = "https://www.iocoder.cn/1.png")
  43 + private String picUrl;
  44 +
  45 + @Schema(description = "绘画错误信息", example = "图片错误信息")
  46 + private String errorMessage;
  47 +
  48 + @Schema(description = "绘制参数")
  49 + private Map<String, String> options;
  50 +
  51 + @Schema(description = "mj buttons 按钮")
  52 + private List<MidjourneyApi.Button> buttons;
  53 +
  54 + @Schema(description = "完成时间")
  55 + private LocalDateTime finishTime;
  56 +
  57 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  58 + private LocalDateTime createTime;
  59 +
  60 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageUpdateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - AI 绘画修改 Request VO")
  8 +@Data
  9 +public class AiImageUpdateReqVO {
  10 +
  11 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
  12 + @NotNull(message = "编号不能为空")
  13 + private Long id;
  14 +
  15 + @Schema(description = "是否发布", example = "true")
  16 + private Boolean publicStatus;
  17 +
  18 +}
0 19 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyActionReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +@Schema(description = "管理后台 - AI 绘图操作(Midjourney) Request VO")
  9 +@Data
  10 +public class AiMidjourneyActionReqVO {
  11 +
  12 + @Schema(description = "图片编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  13 + @NotNull(message = "图片编号不能为空")
  14 + private Long id;
  15 +
  16 + @Schema(description = "操作按钮编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "MJ::JOB::variation::4::06aa3e66-0e97-49cc-8201-e0295d883de4")
  17 + @NotEmpty(message = "操作按钮编号不能为空")
  18 + private String customId;
  19 +
  20 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +@Schema(description = "管理后台 - AI 绘画生成(Midjourney) Request VO")
  9 +@Data
  10 +public class AiMidjourneyImagineReqVO {
  11 +
  12 + @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "中国神龙")
  13 + @NotEmpty(message = "提示词不能为空!")
  14 + private String prompt;
  15 +
  16 + @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "midjourney")
  17 + @NotEmpty(message = "模型不能为空")
  18 + private String model; // 参考 MidjourneyApi.ModelEnum
  19 +
  20 + @Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  21 + @NotNull(message = "图片宽度不能为空")
  22 + private Integer width;
  23 +
  24 + @Schema(description = "图片高度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  25 + @NotNull(message = "图片高度不能为空")
  26 + private Integer height;
  27 +
  28 + @Schema(description = "版本号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6.0")
  29 + @NotEmpty(message = "版本号不能为空")
  30 + private String version;
  31 +
  32 + @Schema(description = "参考图", example = "https://www.iocoder.cn/x.png")
  33 + private String referImageUrl;
  34 +
  35 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/AiMindMapController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.mindmap;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;
  5 +import cn.iocoder.yudao.module.ai.service.mindmap.AiMindMapService;
  6 +import io.swagger.v3.oas.annotations.Operation;
  7 +import io.swagger.v3.oas.annotations.tags.Tag;
  8 +import jakarta.annotation.Resource;
  9 +import jakarta.annotation.security.PermitAll;
  10 +import jakarta.validation.Valid;
  11 +import org.springframework.http.MediaType;
  12 +import org.springframework.web.bind.annotation.PostMapping;
  13 +import org.springframework.web.bind.annotation.RequestBody;
  14 +import org.springframework.web.bind.annotation.RequestMapping;
  15 +import org.springframework.web.bind.annotation.RestController;
  16 +import reactor.core.publisher.Flux;
  17 +
  18 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  19 +
  20 +@Tag(name = "管理后台 - AI 思维导图")
  21 +@RestController
  22 +@RequestMapping("/ai/mind-map")
  23 +public class AiMindMapController {
  24 +
  25 + @Resource
  26 + private AiMindMapService mindMapService;
  27 +
  28 + @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
  29 + @Operation(summary = "脑图生成(流式)", description = "流式返回,响应较快")
  30 + @PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题
  31 + public Flux<CommonResult<String>> generateMindMap(@RequestBody @Valid AiMindMapGenerateReqVO generateReqVO) {
  32 + return mindMapService.generateMindMap(generateReqVO, getLoginUserId());
  33 + }
  34 +
  35 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapGenerateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotBlank;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - AI 思维导图生成 Request VO")
  8 +@Data
  9 +public class AiMindMapGenerateReqVO {
  10 +
  11 + @Schema(description = "思维导图内容提示", example = "Java 学习路线")
  12 + @NotBlank(message = "思维导图内容提示不能为空")
  13 + private String prompt;
  14 +
  15 +}
0 16 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java 0 → 100644
  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.apikey.AiApiKeyPageReqVO;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyRespVO;
  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;
  10 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
  11 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  12 +import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
  13 +import io.swagger.v3.oas.annotations.Operation;
  14 +import io.swagger.v3.oas.annotations.Parameter;
  15 +import io.swagger.v3.oas.annotations.tags.Tag;
  16 +import jakarta.annotation.Resource;
  17 +import jakarta.validation.Valid;
  18 +import org.springframework.security.access.prepost.PreAuthorize;
  19 +import org.springframework.validation.annotation.Validated;
  20 +import org.springframework.web.bind.annotation.*;
  21 +
  22 +import java.util.List;
  23 +
  24 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  25 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  26 +
  27 +@Tag(name = "管理后台 - AI API 密钥")
  28 +@RestController
  29 +@RequestMapping("/ai/api-key")
  30 +@Validated
  31 +public class AiApiKeyController {
  32 +
  33 + @Resource
  34 + private AiApiKeyService apiKeyService;
  35 +
  36 + @PostMapping("/create")
  37 + @Operation(summary = "创建 API 密钥")
  38 + @PreAuthorize("@ss.hasPermission('ai:api-key:create')")
  39 + public CommonResult<Long> createApiKey(@Valid @RequestBody AiApiKeySaveReqVO createReqVO) {
  40 + return success(apiKeyService.createApiKey(createReqVO));
  41 + }
  42 +
  43 + @PutMapping("/update")
  44 + @Operation(summary = "更新 API 密钥")
  45 + @PreAuthorize("@ss.hasPermission('ai:api-key:update')")
  46 + public CommonResult<Boolean> updateApiKey(@Valid @RequestBody AiApiKeySaveReqVO updateReqVO) {
  47 + apiKeyService.updateApiKey(updateReqVO);
  48 + return success(true);
  49 + }
  50 +
  51 + @DeleteMapping("/delete")
  52 + @Operation(summary = "删除 API 密钥")
  53 + @Parameter(name = "id", description = "编号", required = true)
  54 + @PreAuthorize("@ss.hasPermission('ai:api-key:delete')")
  55 + public CommonResult<Boolean> deleteApiKey(@RequestParam("id") Long id) {
  56 + apiKeyService.deleteApiKey(id);
  57 + return success(true);
  58 + }
  59 +
  60 + @GetMapping("/get")
  61 + @Operation(summary = "获得 API 密钥")
  62 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  63 + @PreAuthorize("@ss.hasPermission('ai:api-key:query')")
  64 + public CommonResult<AiApiKeyRespVO> getApiKey(@RequestParam("id") Long id) {
  65 + AiApiKeyDO apiKey = apiKeyService.getApiKey(id);
  66 + return success(BeanUtils.toBean(apiKey, AiApiKeyRespVO.class));
  67 + }
  68 +
  69 + @GetMapping("/page")
  70 + @Operation(summary = "获得 API 密钥分页")
  71 + @PreAuthorize("@ss.hasPermission('ai:api-key:query')")
  72 + public CommonResult<PageResult<AiApiKeyRespVO>> getApiKeyPage(@Valid AiApiKeyPageReqVO pageReqVO) {
  73 + PageResult<AiApiKeyDO> pageResult = apiKeyService.getApiKeyPage(pageReqVO);
  74 + return success(BeanUtils.toBean(pageResult, AiApiKeyRespVO.class));
  75 + }
  76 +
  77 + @GetMapping("/simple-list")
  78 + @Operation(summary = "获得 API 密钥分页列表")
  79 + public CommonResult<List<AiChatModelRespVO>> getApiKeySimpleList() {
  80 + List<AiApiKeyDO> list = apiKeyService.getApiKeyList();
  81 + return success(convertList(list, key -> new AiChatModelRespVO().setId(key.getId()).setName(key.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/AiChatModelController.java 0 → 100644
  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 +}
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/AiChatRoleController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model;
  2 +
  3 +import cn.hutool.core.util.ObjUtil;
  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.chatRole.AiChatRolePageReqVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleRespVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO;
  10 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;
  11 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  12 +import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
  13 +import io.swagger.v3.oas.annotations.Operation;
  14 +import io.swagger.v3.oas.annotations.Parameter;
  15 +import io.swagger.v3.oas.annotations.tags.Tag;
  16 +import jakarta.annotation.Resource;
  17 +import jakarta.validation.Valid;
  18 +import org.springframework.security.access.prepost.PreAuthorize;
  19 +import org.springframework.validation.annotation.Validated;
  20 +import org.springframework.web.bind.annotation.*;
  21 +
  22 +import java.util.List;
  23 +
  24 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  25 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  26 +
  27 +@Tag(name = "管理后台 - AI 聊天角色")
  28 +@RestController
  29 +@RequestMapping("/ai/chat-role")
  30 +@Validated
  31 +public class AiChatRoleController {
  32 +
  33 + @Resource
  34 + private AiChatRoleService chatRoleService;
  35 +
  36 + @GetMapping("/my-page")
  37 + @Operation(summary = "获得【我的】聊天角色分页")
  38 + public CommonResult<PageResult<AiChatRoleRespVO>> getChatRoleMyPage(@Valid AiChatRolePageReqVO pageReqVO) {
  39 + PageResult<AiChatRoleDO> pageResult = chatRoleService.getChatRoleMyPage(pageReqVO, getLoginUserId());
  40 + return success(BeanUtils.toBean(pageResult, AiChatRoleRespVO.class));
  41 + }
  42 +
  43 + @GetMapping("/get-my")
  44 + @Operation(summary = "获得【我的】聊天角色")
  45 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  46 + public CommonResult<AiChatRoleRespVO> getChatRoleMy(@RequestParam("id") Long id) {
  47 + AiChatRoleDO chatRole = chatRoleService.getChatRole(id);
  48 + if (ObjUtil.notEqual(chatRole.getUserId(), getLoginUserId())) {
  49 + return success(null);
  50 + }
  51 + return success(BeanUtils.toBean(chatRole, AiChatRoleRespVO.class));
  52 + }
  53 +
  54 + @PostMapping("/create-my")
  55 + @Operation(summary = "创建【我的】聊天角色")
  56 + public CommonResult<Long> createChatRoleMy(@Valid @RequestBody AiChatRoleSaveMyReqVO createReqVO) {
  57 + return success(chatRoleService.createChatRoleMy(createReqVO, getLoginUserId()));
  58 + }
  59 +
  60 + @PutMapping("/update-my")
  61 + @Operation(summary = "更新【我的】聊天角色")
  62 + public CommonResult<Boolean> updateChatRoleMy(@Valid @RequestBody AiChatRoleSaveMyReqVO updateReqVO) {
  63 + chatRoleService.updateChatRoleMy(updateReqVO, getLoginUserId());
  64 + return success(true);
  65 + }
  66 +
  67 + @DeleteMapping("/delete-my")
  68 + @Operation(summary = "删除【我的】聊天角色")
  69 + @Parameter(name = "id", description = "编号", required = true)
  70 + public CommonResult<Boolean> deleteChatRoleMy(@RequestParam("id") Long id) {
  71 + chatRoleService.deleteChatRoleMy(id, getLoginUserId());
  72 + return success(true);
  73 + }
  74 +
  75 + @GetMapping("/category-list")
  76 + @Operation(summary = "获得聊天角色的分类列表")
  77 + public CommonResult<List<String>> getChatRoleCategoryList() {
  78 + return success(chatRoleService.getChatRoleCategoryList());
  79 + }
  80 +
  81 + // ========== 角色管理 ==========
  82 +
  83 + @PostMapping("/create")
  84 + @Operation(summary = "创建聊天角色")
  85 + @PreAuthorize("@ss.hasPermission('ai:chat-role:create')")
  86 + public CommonResult<Long> createChatRole(@Valid @RequestBody AiChatRoleSaveReqVO createReqVO) {
  87 + return success(chatRoleService.createChatRole(createReqVO));
  88 + }
  89 +
  90 + @PutMapping("/update")
  91 + @Operation(summary = "更新聊天角色")
  92 + @PreAuthorize("@ss.hasPermission('ai:chat-role:update')")
  93 + public CommonResult<Boolean> updateChatRole(@Valid @RequestBody AiChatRoleSaveReqVO updateReqVO) {
  94 + chatRoleService.updateChatRole(updateReqVO);
  95 + return success(true);
  96 + }
  97 +
  98 + @DeleteMapping("/delete")
  99 + @Operation(summary = "删除聊天角色")
  100 + @Parameter(name = "id", description = "编号", required = true)
  101 + @PreAuthorize("@ss.hasPermission('ai:chat-role:delete')")
  102 + public CommonResult<Boolean> deleteChatRole(@RequestParam("id") Long id) {
  103 + chatRoleService.deleteChatRole(id);
  104 + return success(true);
  105 + }
  106 +
  107 + @GetMapping("/get")
  108 + @Operation(summary = "获得聊天角色")
  109 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  110 + @PreAuthorize("@ss.hasPermission('ai:chat-role:query')")
  111 + public CommonResult<AiChatRoleRespVO> getChatRole(@RequestParam("id") Long id) {
  112 + AiChatRoleDO chatRole = chatRoleService.getChatRole(id);
  113 + return success(BeanUtils.toBean(chatRole, AiChatRoleRespVO.class));
  114 + }
  115 +
  116 + @GetMapping("/page")
  117 + @Operation(summary = "获得聊天角色分页")
  118 + @PreAuthorize("@ss.hasPermission('ai:chat-role:query')")
  119 + public CommonResult<PageResult<AiChatRoleRespVO>> getChatRolePage(@Valid AiChatRolePageReqVO pageReqVO) {
  120 + PageResult<AiChatRoleDO> pageResult = chatRoleService.getChatRolePage(pageReqVO);
  121 + return success(BeanUtils.toBean(pageResult, AiChatRoleRespVO.class));
  122 + }
  123 +
  124 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@Schema(description = "管理后台 - AI API 密钥分页 Request VO")
  13 +@Data
  14 +public class AiApiKeyPageReqVO extends PageParam {
  15 +
  16 + @Schema(description = "名称", example = "文心一言")
  17 + private String name;
  18 +
  19 + @Schema(description = "平台", example = "OpenAI")
  20 + private String platform;
  21 +
  22 + @Schema(description = "状态", example = "1")
  23 + private Integer status;
  24 +
  25 +}
0 26 \ 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/apikey/AiApiKeyRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +
  6 +@Schema(description = "管理后台 - AI API 密钥 Response VO")
  7 +@Data
  8 +public class AiApiKeyRespVO {
  9 +
  10 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23538")
  11 + private Long id;
  12 +
  13 + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "文心一言")
  14 + private String name;
  15 +
  16 + @Schema(description = "密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")
  17 + private String apiKey;
  18 +
  19 + @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
  20 + private String platform;
  21 +
  22 + @Schema(description = "自定义 API 地址", example = "https://aip.baidubce.com")
  23 + private String url;
  24 +
  25 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  26 + private Integer status;
  27 +
  28 +}
0 29 \ 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/apikey/AiApiKeySaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import jakarta.validation.constraints.*;
  7 +
  8 +@Schema(description = "管理后台 - AI API 密钥新增/修改 Request VO")
  9 +@Data
  10 +public class AiApiKeySaveReqVO {
  11 +
  12 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23538")
  13 + private Long id;
  14 +
  15 + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "文心一言")
  16 + @NotEmpty(message = "名称不能为空")
  17 + private String name;
  18 +
  19 + @Schema(description = "密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")
  20 + @NotEmpty(message = "密钥不能为空")
  21 + private String apiKey;
  22 +
  23 + @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
  24 + @NotEmpty(message = "平台不能为空")
  25 + private String platform;
  26 +
  27 + @Schema(description = "自定义 API 地址", example = "https://aip.baidubce.com")
  28 + private String url;
  29 +
  30 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  31 + @NotNull(message = "状态不能为空")
  32 + private Integer status;
  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/chatModel/AiChatModelPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
  2 +
  3 +import lombok.*;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  6 +
  7 +@Schema(description = "管理后台 - API 聊天模型分页 Request VO")
  8 +@Data
  9 +public class AiChatModelPageReqVO extends PageParam {
  10 +
  11 + @Schema(description = "模型名字", example = "张三")
  12 + private String name;
  13 +
  14 + @Schema(description = "模型标识", example = "gpt-3.5-turbo-0125")
  15 + private String model;
  16 +
  17 + @Schema(description = "模型平台", example = "OpenAI")
  18 + private String platform;
  19 +
  20 +}
0 21 \ 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/chatModel/AiChatModelRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - AI 聊天模型 Response VO")
  9 +@Data
  10 +public class AiChatModelRespVO {
  11 +
  12 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2630")
  13 + private Long id;
  14 +
  15 + @Schema(description = "API 秘钥编号", example = "22042")
  16 + private Long keyId;
  17 +
  18 + @Schema(description = "模型名字", example = "张三")
  19 + private String name;
  20 +
  21 + @Schema(description = "模型标识", example = "gpt-3.5-turbo-0125")
  22 + private String model;
  23 +
  24 + @Schema(description = "模型平台", example = "OpenAI")
  25 + private String platform;
  26 +
  27 + @Schema(description = "排序", example = "1")
  28 + private Integer sort;
  29 +
  30 + @Schema(description = "状态", example = "2")
  31 + private Integer status;
  32 +
  33 + @Schema(description = "温度参数", example = "1")
  34 + private Double temperature;
  35 +
  36 + @Schema(description = "单条回复的最大 Token 数量", example = "4096")
  37 + private Integer maxTokens;
  38 +
  39 + @Schema(description = "上下文的最大 Message 数量", example = "8192")
  40 + private Integer maxContexts;
  41 +
  42 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  43 + private LocalDateTime createTime;
  44 +
  45 +}
0 46 \ 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/chatModel/AiChatModelSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
  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 lombok.*;
  7 +import jakarta.validation.constraints.*;
  8 +
  9 +@Schema(description = "管理后台 - API 聊天模型新增/修改 Request VO")
  10 +@Data
  11 +public class AiChatModelSaveReqVO {
  12 +
  13 + @Schema(description = "编号", example = "2630")
  14 + private Long id;
  15 +
  16 + @Schema(description = "API 秘钥编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22042")
  17 + @NotNull(message = "API 秘钥编号不能为空")
  18 + private Long keyId;
  19 +
  20 + @Schema(description = "模型名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
  21 + @NotEmpty(message = "模型名字不能为空")
  22 + private String name;
  23 +
  24 + @Schema(description = "模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "gpt-3.5-turbo-0125")
  25 + @NotEmpty(message = "模型标识不能为空")
  26 + private String model;
  27 +
  28 + @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
  29 + @NotEmpty(message = "模型平台不能为空")
  30 + private String platform;
  31 +
  32 + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  33 + @NotNull(message = "排序不能为空")
  34 + private Integer sort;
  35 +
  36 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  37 + @InEnum(CommonStatusEnum.class)
  38 + @NotNull(message = "状态不能为空")
  39 + private Integer status;
  40 +
  41 + @Schema(description = "温度参数", example = "1")
  42 + private Double temperature;
  43 +
  44 + @Schema(description = "单条回复的最大 Token 数量", example = "4096")
  45 + private Integer maxTokens;
  46 +
  47 + @Schema(description = "上下文的最大 Message 数量", example = "8192")
  48 + private Integer maxContexts;
  49 +
  50 +}
0 51 \ 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/AiChatRolePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;
  2 +
  3 +import lombok.*;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  6 +
  7 +@Schema(description = "管理后台 - AI 聊天角色分页 Request VO")
  8 +@Data
  9 +public class AiChatRolePageReqVO extends PageParam {
  10 +
  11 + @Schema(description = "角色名称", example = "李四")
  12 + private String name;
  13 +
  14 + @Schema(description = "角色类别", example = "创作")
  15 + private String category;
  16 +
  17 + @Schema(description = "是否公开", example = "1")
  18 + private Boolean publicStatus;
  19 +
  20 +}
0 21 \ 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 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;
  2 +
  3 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  4 +import com.fhs.core.trans.anno.Trans;
  5 +import com.fhs.core.trans.constant.TransType;
  6 +import com.fhs.core.trans.vo.VO;
  7 +import io.swagger.v3.oas.annotations.media.Schema;
  8 +import lombok.Data;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +@Schema(description = "管理后台 - AI 聊天角色 Response VO")
  13 +@Data
  14 +public class AiChatRoleRespVO implements VO {
  15 +
  16 + @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32746")
  17 + private Long id;
  18 +
  19 + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "9442")
  20 + private Long userId;
  21 +
  22 + @Schema(description = "模型编号", example = "17640")
  23 + @Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = {"name", "model"}, refs = {"modelName", "model"})
  24 + private Long modelId;
  25 + @Schema(description = "模型名字", example = "张三")
  26 + private String modelName;
  27 + @Schema(description = "模型标识", example = "gpt-3.5-turbo-0125")
  28 + private String model;
  29 +
  30 + @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
  31 + private String name;
  32 +
  33 + @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
  34 + private String avatar;
  35 +
  36 + @Schema(description = "角色类别", requiredMode = Schema.RequiredMode.REQUIRED, example = "创作")
  37 + private String category;
  38 +
  39 + @Schema(description = "角色排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  40 + private Integer sort;
  41 +
  42 + @Schema(description = "角色描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
  43 + private String description;
  44 +
  45 + @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED)
  46 + private String systemMessage;
  47 +
  48 + @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  49 + private Boolean publicStatus;
  50 +
  51 + @Schema(description = "状态", example = "1")
  52 + private Integer status;
  53 +
  54 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  55 + private LocalDateTime createTime;
  56 +
  57 +}
0 58 \ 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/AiChatRoleSaveMyReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import lombok.Data;
  6 +import org.hibernate.validator.constraints.URL;
  7 +
  8 +@Schema(description = "管理后台 - AI 聊天角色新增/修改【我的】 Request VO")
  9 +@Data
  10 +public class AiChatRoleSaveMyReqVO {
  11 +
  12 + @Schema(description = "角色编号", example = "32746")
  13 + private Long id;
  14 +
  15 + @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
  16 + @NotEmpty(message = "角色名称不能为空")
  17 + private String name;
  18 +
  19 + @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
  20 + @NotEmpty(message = "角色头像不能为空")
  21 + @URL(message = "角色头像必须是 URL 格式")
  22 + private String avatar;
  23 +
  24 + @Schema(description = "角色描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
  25 + @NotEmpty(message = "角色描述不能为空")
  26 + private String description;
  27 +
  28 + @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED, example = "现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题")
  29 + @NotEmpty(message = "角色设定不能为空")
  30 + private String systemMessage;
  31 +
  32 +}
0 33 \ 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 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;
  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 lombok.*;
  7 +import jakarta.validation.constraints.*;
  8 +import org.hibernate.validator.constraints.URL;
  9 +
  10 +@Schema(description = "管理后台 - AI 聊天角色新增/修改 Request VO")
  11 +@Data
  12 +public class AiChatRoleSaveReqVO {
  13 +
  14 + @Schema(description = "角色编号", example = "32746")
  15 + private Long id;
  16 +
  17 + @Schema(description = "模型编号", example = "17640")
  18 + private Long modelId;
  19 +
  20 + @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
  21 + @NotEmpty(message = "角色名称不能为空")
  22 + private String name;
  23 +
  24 + @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
  25 + @NotEmpty(message = "角色头像不能为空")
  26 + @URL(message = "角色头像必须是 URL 格式")
  27 + private String avatar;
  28 +
  29 + @Schema(description = "角色类别", requiredMode = Schema.RequiredMode.REQUIRED, example = "创作")
  30 + @NotEmpty(message = "角色类别不能为空")
  31 + private String category;
  32 +
  33 + @Schema(description = "角色排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  34 + @NotNull(message = "角色排序不能为空")
  35 + private Integer sort;
  36 +
  37 + @Schema(description = "角色描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
  38 + @NotEmpty(message = "角色描述不能为空")
  39 + private String description;
  40 +
  41 + @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED, example = "现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题")
  42 + @NotEmpty(message = "角色设定不能为空")
  43 + private String systemMessage;
  44 +
  45 + @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  46 + @NotNull(message = "是否公开不能为空")
  47 + private Boolean publicStatus;
  48 +
  49 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  50 + @NotNull(message = "状态不能为空")
  51 + @InEnum(CommonStatusEnum.class)
  52 + private Integer status;
  53 +
  54 +}
0 55 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.http 0 → 100644
  1 +### 生成音乐:Suno + 歌词模式
  2 +POST {{baseUrl}}/ai/music/generate
  3 +Content-Type: application/json
  4 +Authorization: {{token}}
  5 +
  6 +{
  7 + "platform": "Suno",
  8 + "generateMode": 2,
  9 + "prompt": "创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。",
  10 + "model": "chirp-v3.5",
  11 + "tags": ["Happy"],
  12 + "title": "Happy Song"
  13 +}
  14 +
  15 +### 生成音乐:Suno + 描述模式
  16 +POST {{baseUrl}}/ai/music/generate
  17 +Content-Type: application/json
  18 +Authorization: {{token}}
  19 +
  20 +{
  21 + "platform": "Suno",
  22 + "generateMode": 1,
  23 + "model": "chirp-v3.5",
  24 + "prompt": "happy music",
  25 + "makeInstrumental": false
  26 +}
0 27 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.music;
  2 +
  3 +import cn.hutool.core.util.ObjUtil;
  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.music.vo.*;
  8 +import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
  9 +import cn.iocoder.yudao.module.ai.service.music.AiMusicService;
  10 +import io.swagger.v3.oas.annotations.Operation;
  11 +import io.swagger.v3.oas.annotations.Parameter;
  12 +import io.swagger.v3.oas.annotations.tags.Tag;
  13 +import jakarta.annotation.Resource;
  14 +import jakarta.validation.Valid;
  15 +import org.springframework.security.access.prepost.PreAuthorize;
  16 +import org.springframework.web.bind.annotation.*;
  17 +
  18 +import java.util.List;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  21 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  22 +
  23 +@Tag(name = "管理后台 - AI 音乐")
  24 +@RestController
  25 +@RequestMapping("/ai/music")
  26 +public class AiMusicController {
  27 +
  28 + @Resource
  29 + private AiMusicService musicService;
  30 +
  31 + @GetMapping("/my-page")
  32 + @Operation(summary = "获得【我的】音乐分页")
  33 + public CommonResult<PageResult<AiMusicRespVO>> getMusicMyPage(@Valid AiMusicPageReqVO pageReqVO) {
  34 + PageResult<AiMusicDO> pageResult = musicService.getMusicMyPage(pageReqVO, getLoginUserId());
  35 + return success(BeanUtils.toBean(pageResult, AiMusicRespVO.class));
  36 + }
  37 +
  38 + @PostMapping("/generate")
  39 + @Operation(summary = "音乐生成")
  40 + public CommonResult<List<Long>> generateMusic(@RequestBody @Valid AiSunoGenerateReqVO reqVO) {
  41 + return success(musicService.generateMusic(getLoginUserId(), reqVO));
  42 + }
  43 +
  44 + @Operation(summary = "删除【我的】音乐记录")
  45 + @DeleteMapping("/delete-my")
  46 + @Parameter(name = "id", required = true, description = "音乐编号", example = "1024")
  47 + public CommonResult<Boolean> deleteMusicMy(@RequestParam("id") Long id) {
  48 + musicService.deleteMusicMy(id, getLoginUserId());
  49 + return success(true);
  50 + }
  51 +
  52 + @GetMapping("/get-my")
  53 + @Operation(summary = "获取【我的】音乐")
  54 + @Parameter(name = "id", required = true, description = "音乐编号", example = "1024")
  55 + public CommonResult<AiMusicRespVO> getMusicMy(@RequestParam("id") Long id) {
  56 + AiMusicDO music = musicService.getMusic(id);
  57 + if (music == null || ObjUtil.notEqual(getLoginUserId(), music.getUserId())) {
  58 + return success(null);
  59 + }
  60 + return success(BeanUtils.toBean(music, AiMusicRespVO.class));
  61 + }
  62 +
  63 + @PostMapping("/update-my")
  64 + @Operation(summary = "修改【我的】音乐 目前只支持修改标题")
  65 + @Parameter(name = "title", required = true, description = "音乐名称", example = "夜空中最亮的星")
  66 + public CommonResult<Boolean> updateMy(AiMusicUpdateMyReqVO updateReqVO) {
  67 + musicService.updateMyMusic(updateReqVO, getLoginUserId());
  68 + return success(true);
  69 + }
  70 +
  71 + // ================ 音乐管理 ================
  72 +
  73 + @GetMapping("/page")
  74 + @Operation(summary = "获得音乐分页")
  75 + @PreAuthorize("@ss.hasPermission('ai:music:query')")
  76 + public CommonResult<PageResult<AiMusicRespVO>> getMusicPage(@Valid AiMusicPageReqVO pageReqVO) {
  77 + PageResult<AiMusicDO> pageResult = musicService.getMusicPage(pageReqVO);
  78 + return success(BeanUtils.toBean(pageResult, AiMusicRespVO.class));
  79 + }
  80 +
  81 + @DeleteMapping("/delete")
  82 + @Operation(summary = "删除音乐")
  83 + @Parameter(name = "id", description = "编号", required = true)
  84 + @PreAuthorize("@ss.hasPermission('ai:music:delete')")
  85 + public CommonResult<Boolean> deleteMusic(@RequestParam("id") Long id) {
  86 + musicService.deleteMusic(id);
  87 + return success(true);
  88 + }
  89 +
  90 + @PutMapping("/update")
  91 + @Operation(summary = "更新音乐")
  92 + @PreAuthorize("@ss.hasPermission('ai:music:update')")
  93 + public CommonResult<Boolean> updateMusic(@Valid @RequestBody AiMusicUpdateReqVO updateReqVO) {
  94 + musicService.updateMusic(updateReqVO);
  95 + return success(true);
  96 + }
  97 +
  98 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;
  6 +import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;
  7 +import io.swagger.v3.oas.annotations.media.Schema;
  8 +import lombok.Data;
  9 +import lombok.EqualsAndHashCode;
  10 +import lombok.ToString;
  11 +import org.springframework.format.annotation.DateTimeFormat;
  12 +
  13 +import java.time.LocalDateTime;
  14 +
  15 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  16 +
  17 +@Schema(description = "管理后台 - AI 音乐分页 Request VO")
  18 +@Data
  19 +public class AiMusicPageReqVO extends PageParam {
  20 +
  21 + @Schema(description = "用户编号", example = "12212")
  22 + private Long userId;
  23 +
  24 + @Schema(description = "音乐名称", example = "夜空中最亮的星")
  25 + private String title;
  26 +
  27 + @Schema(description = "音乐状态", example = "20")
  28 + @InEnum(AiMusicStatusEnum.class)
  29 + private Integer status;
  30 +
  31 + @Schema(description = "生成模式", example = "1")
  32 + @InEnum(AiMusicGenerateModeEnum.class)
  33 + private Integer generateMode;
  34 +
  35 + @Schema(description = "是否发布", example = "true")
  36 + private Boolean publicStatus;
  37 +
  38 + @Schema(description = "创建时间")
  39 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  40 + private LocalDateTime[] createTime;
  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/music/vo/AiMusicRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - AI 音乐 Response VO")
  10 +@Data
  11 +public class AiMusicRespVO {
  12 +
  13 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
  14 + private Long id;
  15 +
  16 + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12212")
  17 + private Long userId;
  18 +
  19 + @Schema(description = "音乐名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "夜空中最亮的星")
  20 + private String title;
  21 +
  22 + @Schema(description = "歌词", example = "oh~卖糕的")
  23 + private String lyric;
  24 +
  25 + @Schema(description = "图片地址", example = "https://www.iocoder.cn")
  26 + private String imageUrl;
  27 +
  28 + @Schema(description = "音频地址", example = "https://www.iocoder.cn")
  29 + private String audioUrl;
  30 +
  31 + @Schema(description = "视频地址", example = "https://www.iocoder.cn")
  32 + private String videoUrl;
  33 +
  34 + @Schema(description = "音乐状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
  35 + private Integer status;
  36 +
  37 + @Schema(description = "描述词", example = "一首轻快的歌曲")
  38 + private String gptDescriptionPrompt;
  39 +
  40 + @Schema(description = "提示词", example = "创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")
  41 + private String prompt;
  42 +
  43 + @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "Suno")
  44 + private String platform;
  45 +
  46 + @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "chirp-v3.5")
  47 + private String model;
  48 +
  49 + @Schema(description = "生成模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  50 + private Integer generateMode;
  51 +
  52 + @Schema(description = "音乐风格标签")
  53 + private List<String> tags;
  54 +
  55 + @Schema(description = "音乐时长", example = "[\"pop\",\"jazz\",\"punk\"]")
  56 + private Double duration;
  57 +
  58 + @Schema(description = "是否发布", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  59 + private Boolean publicStatus;
  60 +
  61 + @Schema(description = "任务编号", example = "11369")
  62 + private String taskId;
  63 +
  64 + @Schema(description = "错误信息")
  65 + private String errorMessage;
  66 +
  67 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  68 + private LocalDateTime createTime;
  69 +
  70 +}
0 71 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateMyReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - AI 修改我的音乐 Request VO")
  8 +@Data
  9 +public class AiMusicUpdateMyReqVO {
  10 +
  11 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
  12 + @NotNull(message = "编号不能为空")
  13 + private Long id;
  14 +
  15 + @Schema(description = "音乐名称", example = "夜空中最亮的星")
  16 + private String title;
  17 +
  18 +}
0 19 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - AI 音乐修改 Request VO")
  8 +@Data
  9 +public class AiMusicUpdateReqVO {
  10 +
  11 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
  12 + @NotNull(message = "编号不能为空")
  13 + private Long id;
  14 +
  15 + @Schema(description = "是否发布", example = "true")
  16 + private Boolean publicStatus;
  17 +
  18 +}
0 19 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiSunoGenerateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
  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 +
  9 +import java.util.List;
  10 +
  11 +@Schema(description = "管理后台 - AI 音乐生成 Request VO")
  12 +@Data
  13 +public class AiSunoGenerateReqVO {
  14 +
  15 + @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "Suno")
  16 + @NotBlank(message = "平台不能为空")
  17 + private String platform; // 参见 AiPlatformEnum 枚举
  18 +
  19 + /**
  20 + * 1. 描述模式:描述词 + 是否纯音乐 + 模型
  21 + * 2. 歌词模式:歌词 + 音乐风格 + 标题 + 模型
  22 + */
  23 + @Schema(description = "生成模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  24 + @NotNull(message = "生成模式不能为空")
  25 + private Integer generateMode; // 参见 AiMusicGenerateModeEnum 枚举
  26 +
  27 + @Schema(description = "用于生成音乐音频的歌词提示",
  28 + example = """
  29 + 1.描述模式:创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。
  30 + 2.歌词模式:
  31 + [Verse]
  32 + 阳光下奔跑 多么欢快
  33 + 假期就要来 心都飞起来
  34 + 朋友在一旁 笑声又灿烂
  35 + 无忧无虑的 每一天甜蜜
  36 + [Chorus]
  37 + 马上放假了 快来庆祝
  38 + 一起去旅行 快去冒险
  39 + 日子太短暂 别再等待
  40 + 马上放假了 梦想起飞
  41 + """)
  42 + private String prompt;
  43 +
  44 + @Schema(description = "是否纯音乐", example = "true")
  45 + private Boolean makeInstrumental;
  46 +
  47 + @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "chirp-v3.5")
  48 + @NotEmpty(message = "模型不能为空")
  49 + private String model;
  50 +
  51 + @Schema(description = "音乐风格", example = "[\"pop\",\"jazz\",\"punk\"]")
  52 + private List<String> tags;
  53 +
  54 + @Schema(description = "音乐/歌曲名称", example = "夜空中最亮的星")
  55 + private String title;
  56 +
  57 +}
0 58 \ 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 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.write;
  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.write.vo.AiWriteGenerateReqVO;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteRespVO;
  9 +import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
  10 +import cn.iocoder.yudao.module.ai.service.write.AiWriteService;
  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.annotation.security.PermitAll;
  16 +import jakarta.validation.Valid;
  17 +import org.springframework.http.MediaType;
  18 +import org.springframework.security.access.prepost.PreAuthorize;
  19 +import org.springframework.web.bind.annotation.*;
  20 +import reactor.core.publisher.Flux;
  21 +
  22 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  23 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  24 +
  25 +@Tag(name = "管理后台 - AI 写作")
  26 +@RestController
  27 +@RequestMapping("/ai/write")
  28 +public class AiWriteController {
  29 +
  30 + @Resource
  31 + private AiWriteService writeService;
  32 +
  33 + @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
  34 + @Operation(summary = "写作生成(流式)", description = "流式返回,响应较快")
  35 + @PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题
  36 + public Flux<CommonResult<String>> generateWriteContent(@RequestBody @Valid AiWriteGenerateReqVO generateReqVO) {
  37 + return writeService.generateWriteContent(generateReqVO, getLoginUserId());
  38 + }
  39 +
  40 + // ================ 写作管理 ================
  41 +
  42 + @DeleteMapping("/delete")
  43 + @Operation(summary = "删除写作")
  44 + @Parameter(name = "id", description = "编号", required = true)
  45 + @PreAuthorize("@ss.hasPermission('ai:write:delete')")
  46 + public CommonResult<Boolean> deleteWrite(@RequestParam("id") Long id) {
  47 + writeService.deleteWrite(id);
  48 + return success(true);
  49 + }
  50 +
  51 + @GetMapping("/page")
  52 + @Operation(summary = "获得写作分页")
  53 + @PreAuthorize("@ss.hasPermission('ai:write:query')")
  54 + public CommonResult<PageResult<AiWriteRespVO>> getWritePage(@Valid AiWritePageReqVO pageReqVO) {
  55 + PageResult<AiWriteDO> pageResult = writeService.getWritePage(pageReqVO);
  56 + return success(BeanUtils.toBean(pageResult, AiWriteRespVO.class));
  57 + }
  58 +
  59 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.write.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;
  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 AiWriteGenerateReqVO {
  12 +
  13 + @Schema(description = "写作类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  14 + @InEnum(value = AiWriteTypeEnum.class, message = "写作类型必须是 {value}")
  15 + private Integer type;
  16 +
  17 + @Schema(description = "写作内容提示", example = "1.撰写:田忌赛马;2.回复:不批")
  18 + private String prompt;
  19 +
  20 + @Schema(description = "原文", example = "领导我要辞职")
  21 + private String originalContent;
  22 +
  23 + @Schema(description = "长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  24 + @NotNull(message = "长度不能为空")
  25 + private Integer length;
  26 +
  27 + @Schema(description = "格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  28 + @NotNull(message = "格式不能为空")
  29 + private Integer format;
  30 +
  31 + @Schema(description = "语气", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  32 + @NotNull(message = "语气不能为空")
  33 + private Integer tone;
  34 +
  35 + @Schema(description = "语言", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  36 + @NotNull(message = "语言不能为空")
  37 + private Integer language;
  38 +
  39 +}
0 40 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWritePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.write.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  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;
  13 +
  14 +@Schema(description = "管理后台 - AI 写作分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class AiWritePageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "用户编号", example = "28404")
  21 + private Long userId;
  22 +
  23 + @Schema(description = "写作类型", example = "1")
  24 + private Integer type;
  25 +
  26 + @Schema(description = "平台", example = "TongYi")
  27 + private String platform;
  28 +
  29 + @Schema(description = "创建时间")
  30 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  31 + private LocalDateTime[] createTime;
  32 +
  33 +}
0 34 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.write.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - AI 写作 Response VO")
  9 +@Data
  10 +public class AiWriteRespVO {
  11 +
  12 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5311")
  13 + private Long id;
  14 +
  15 + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28404")
  16 + private Long userId;
  17 +
  18 + @Schema(description = "写作类型", example = "1")
  19 + private Integer type;
  20 +
  21 + @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "TongYi")
  22 + private String platform;
  23 +
  24 + @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen")
  25 + private String model;
  26 +
  27 + @Schema(description = "生成内容提示", requiredMode = Schema.RequiredMode.REQUIRED, example = "撰写:田忌赛马")
  28 + private String prompt;
  29 +
  30 + @Schema(description = "生成的内容", example = "你非常不错")
  31 + private String generatedContent;
  32 +
  33 + @Schema(description = "原文", example = "真的么?")
  34 + private String originalContent;
  35 +
  36 + @Schema(description = "长度提示词", example = "1")
  37 + private Integer length;
  38 +
  39 + @Schema(description = "格式提示词", example = "2")
  40 + private Integer format;
  41 +
  42 + @Schema(description = "语气提示词", example = "3")
  43 + private Integer tone;
  44 +
  45 + @Schema(description = "语言提示词", example = "4")
  46 + private Integer language;
  47 +
  48 + @Schema(description = "错误信息")
  49 + private String errorMessage;
  50 +
  51 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  52 + private LocalDateTime createTime;
  53 +
  54 +}
0 55 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/app/package-info.java 0 → 100644
  1 +/**
  2 + * TODO 芋艿:站位,无特殊作用
  3 + */
  4 +package cn.iocoder.yudao.module.ai.controller.app;
0 5 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/package-info.java 0 → 100644
  1 +/**
  2 + * 提供 RESTful API 给前端:
  3 + * 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目
  4 + * 2. app 包:提供给用户 APP yudao-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分
  5 + */
  6 +package cn.iocoder.yudao.module.ai.controller;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  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 +import java.time.LocalDateTime;
  12 +
  13 +/**
  14 + * AI Chat 对话 DO
  15 + *
  16 + * 用户每次发起 Chat 聊天时,会创建一个 {@link AiChatConversationDO} 对象,将它的消息关联在一起
  17 + *
  18 + * @author fansili
  19 + * @since 2024/4/14 17:35
  20 + */
  21 +@TableName("ai_chat_conversation")
  22 +@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  23 +@Data
  24 +@EqualsAndHashCode(callSuper = true)
  25 +@Builder
  26 +@NoArgsConstructor
  27 +@AllArgsConstructor
  28 +public class AiChatConversationDO extends BaseDO {
  29 +
  30 + public static final String TITLE_DEFAULT = "新对话";
  31 +
  32 + /**
  33 + * ID 编号,自增
  34 + */
  35 + @TableId
  36 + private Long id;
  37 +
  38 + /**
  39 + * 用户编号
  40 + *
  41 + * 关联 AdminUserDO 的 userId 字段
  42 + */
  43 + private Long userId;
  44 +
  45 + /**
  46 + * 对话标题
  47 + *
  48 + * 默认由系统自动生成,可用户手动修改
  49 + */
  50 + private String title;
  51 + /**
  52 + * 是否置顶
  53 + */
  54 + private Boolean pinned;
  55 + /**
  56 + * 置顶时间
  57 + */
  58 + private LocalDateTime pinnedTime;
  59 +
  60 + /**
  61 + * 角色编号
  62 + *
  63 + * 关联 {@link AiChatRoleDO#getId()}
  64 + */
  65 + private Long roleId;
  66 +
  67 + /**
  68 + * 模型编号
  69 + *
  70 + * 关联 {@link AiChatModelDO#getId()} 字段
  71 + */
  72 + private Long modelId;
  73 + /**
  74 + * 模型标志
  75 + */
  76 + private String model;
  77 +
  78 + // ========== 对话配置 ==========
  79 +
  80 + /**
  81 + * 角色设定
  82 + */
  83 + private String systemMessage;
  84 + /**
  85 + * 温度参数
  86 + *
  87 + * 用于调整生成回复的随机性和多样性程度:较低的温度值会使输出更收敛于高频词汇,较高的则增加多样性
  88 + */
  89 + private Double temperature;
  90 + /**
  91 + * 单条回复的最大 Token 数量
  92 + */
  93 + private Integer maxTokens;
  94 + /**
  95 + * 上下文的最大 Message 数量
  96 + */
  97 + private Integer maxContexts;
  98 +
  99 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
  2 +
  3 +import com.baomidou.mybatisplus.annotation.TableId;
  4 +import org.springframework.ai.chat.messages.MessageType;
  5 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  6 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  8 +import com.baomidou.mybatisplus.annotation.KeySequence;
  9 +import com.baomidou.mybatisplus.annotation.TableName;
  10 +import lombok.*;
  11 +
  12 +/**
  13 + * AI Chat 消息 DO
  14 + *
  15 + * @since 2024/4/14 17:35
  16 + * @since 2024/4/14 17:35
  17 + */
  18 +@TableName("ai_chat_message")
  19 +@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  20 +@Data
  21 +@EqualsAndHashCode(callSuper = true)
  22 +@Builder
  23 +@NoArgsConstructor
  24 +@AllArgsConstructor
  25 +public class AiChatMessageDO extends BaseDO {
  26 +
  27 + /**
  28 + * 编号,作为每条聊天记录的唯一标识符
  29 + */
  30 + @TableId
  31 + private Long id;
  32 +
  33 + /**
  34 + * 对话编号
  35 + *
  36 + * 关联 {@link AiChatConversationDO#getId()} 字段
  37 + */
  38 + private Long conversationId;
  39 + /**
  40 + * 回复消息编号
  41 + *
  42 + * 关联 {@link #id} 字段
  43 + *
  44 + * 大模型回复的消息编号,用于“问答”的关联
  45 + */
  46 + private Long replyId;
  47 +
  48 + /**
  49 + * 消息类型
  50 + *
  51 + * 也等价于 OpenAPI 的 role 字段
  52 + *
  53 + * 枚举 {@link MessageType}
  54 + */
  55 + private String type;
  56 + /**
  57 + * 用户编号
  58 + *
  59 + * 关联 AdminUserDO 的 userId 字段
  60 + */
  61 + private Long userId;
  62 + /**
  63 + * 角色编号
  64 + *
  65 + * 关联 {@link AiChatRoleDO#getId()} 字段
  66 + */
  67 + private Long roleId;
  68 +
  69 + /**
  70 + * 模型标志
  71 + */
  72 + private String model;
  73 + /**
  74 + * 模型编号
  75 + *
  76 + * 关联 {@link AiChatModelDO#getId()} 字段
  77 + */
  78 + private Long modelId;
  79 +
  80 + /**
  81 + * 聊天内容
  82 + */
  83 + private String content;
  84 +
  85 + /**
  86 + * 是否携带上下文
  87 + */
  88 + private Boolean useContext;
  89 +
  90 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.dataobject.image;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
  4 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  5 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  6 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  7 +import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
  8 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  9 +import com.baomidou.mybatisplus.annotation.IdType;
  10 +import com.baomidou.mybatisplus.annotation.TableField;
  11 +import com.baomidou.mybatisplus.annotation.TableId;
  12 +import com.baomidou.mybatisplus.annotation.TableName;
  13 +import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
  14 +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
  15 +import lombok.Data;
  16 +import org.springframework.ai.openai.OpenAiImageOptions;
  17 +import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
  18 +
  19 +import java.time.LocalDateTime;
  20 +import java.util.List;
  21 +import java.util.Map;
  22 +
  23 +/**
  24 + * AI 绘画 DO
  25 + *
  26 + * @author fansili
  27 + */
  28 +@TableName(value = "ai_image", autoResultMap = true)
  29 +@Data
  30 +public class AiImageDO extends BaseDO {
  31 +
  32 + /**
  33 + * 编号
  34 + */
  35 + @TableId(type = IdType.AUTO)
  36 + private Long id;
  37 +
  38 + /**
  39 + * 用户编号
  40 + *
  41 + * 关联 {@link AdminUserRespDTO#getId()}
  42 + */
  43 + private Long userId;
  44 +
  45 + /**
  46 + * 提示词
  47 + */
  48 + private String prompt;
  49 +
  50 + /**
  51 + * 平台
  52 + *
  53 + * 枚举 {@link cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum}
  54 + */
  55 + private String platform;
  56 + /**
  57 + * 模型
  58 + *
  59 + * 冗余 {@link AiChatModelDO#getModel()}
  60 + */
  61 + private String model;
  62 +
  63 + /**
  64 + * 图片宽度
  65 + */
  66 + private Integer width;
  67 + /**
  68 + * 图片高度
  69 + */
  70 + private Integer height;
  71 +
  72 + /**
  73 + * 生成状态
  74 + *
  75 + * 枚举 {@link AiImageStatusEnum}
  76 + */
  77 + private Integer status;
  78 +
  79 + /**
  80 + * 完成时间
  81 + */
  82 + private LocalDateTime finishTime;
  83 +
  84 + /**
  85 + * 绘画错误信息
  86 + */
  87 + private String errorMessage;
  88 +
  89 + /**
  90 + * 图片地址
  91 + */
  92 + private String picUrl;
  93 + /**
  94 + * 是否公开
  95 + */
  96 + private Boolean publicStatus;
  97 +
  98 + /**
  99 + * 绘制参数,不同 platform 的不同参数
  100 + *
  101 + * 1. {@link OpenAiImageOptions}
  102 + * 2. {@link StabilityAiImageOptions}
  103 + */
  104 + @TableField(typeHandler = JacksonTypeHandler.class)
  105 + private Map<String, Object> options;
  106 +
  107 + /**
  108 + * mj buttons 按钮
  109 + */
  110 + @TableField(typeHandler = ButtonTypeHandler.class)
  111 + private List<MidjourneyApi.Button> buttons;
  112 +
  113 + /**
  114 + * 任务编号
  115 + *
  116 + * 1. midjourney proxy:关联的 task id
  117 + */
  118 + private String taskId;
  119 +
  120 + public static class ButtonTypeHandler extends AbstractJsonTypeHandler<Object> {
  121 +
  122 + @Override
  123 + protected Object parse(String json) {
  124 + return JsonUtils.parseArray(json, MidjourneyApi.Button.class);
  125 + }
  126 +
  127 + @Override
  128 + protected String toJson(Object obj) {
  129 + return JsonUtils.toJsonString(obj);
  130 + }
  131 +
  132 + }
  133 +
  134 +}
  135 +
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.dataobject.mindmap;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import com.baomidou.mybatisplus.annotation.IdType;
  6 +import com.baomidou.mybatisplus.annotation.TableId;
  7 +import com.baomidou.mybatisplus.annotation.TableName;
  8 +import lombok.Data;
  9 +
  10 +/**
  11 + * AI 思维导图 DO
  12 + *
  13 + * @author xiaoxin
  14 + */
  15 +@TableName(value = "ai_mind_map")
  16 +@Data
  17 +public class AiMindMapDO extends BaseDO {
  18 +
  19 + /**
  20 + * 编号
  21 + */
  22 + @TableId(type = IdType.AUTO)
  23 + private Long id;
  24 +
  25 + /**
  26 + * 用户编号
  27 + * <p>
  28 + * 关联 AdminUserDO 的 userId 字段
  29 + */
  30 + private Long userId;
  31 +
  32 + /**
  33 + * 平台
  34 + * <p>
  35 + * 枚举 {@link AiPlatformEnum}
  36 + */
  37 + private String platform;
  38 + /**
  39 + * 模型
  40 + */
  41 + private String model;
  42 +
  43 + /**
  44 + * 生成内容提示
  45 + */
  46 + private String prompt;
  47 +
  48 + /**
  49 + * 生成的内容
  50 + */
  51 + private String generatedContent;
  52 +
  53 + /**
  54 + * 错误信息
  55 + */
  56 + private String errorMessage;
  57 +
  58 +}
0 59 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.dataobject.model;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  5 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  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 API 秘钥 DO
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@TableName("ai_api_key")
  17 +@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  18 +@Data
  19 +@EqualsAndHashCode(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class AiApiKeyDO extends BaseDO {
  24 +
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 名称
  32 + */
  33 + private String name;
  34 + /**
  35 + * 密钥
  36 + */
  37 + private String apiKey;
  38 + /**
  39 + * 平台
  40 + *
  41 + * 枚举 {@link AiPlatformEnum}
  42 + */
  43 + private String platform;
  44 + /**
  45 + * API 地址
  46 + */
  47 + private String url;
  48 + /**
  49 + * 状态
  50 + *
  51 + * 枚举 {@link CommonStatusEnum}
  52 + */
  53 + private Integer status;
  54 +
  55 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatModelDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.dataobject.model;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  5 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  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 + * 默认聊天模型:{@link #status} 为开启,并且 {@link #sort} 排序第一
  15 + *
  16 + * @author fansili
  17 + * @since 2024/4/24 19:39
  18 + */
  19 +@TableName("ai_chat_model")
  20 +@KeySequence("ai_chat_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  21 +@Data
  22 +@EqualsAndHashCode(callSuper = true)
  23 +@Builder
  24 +@NoArgsConstructor
  25 +@AllArgsConstructor
  26 +public class AiChatModelDO extends BaseDO {
  27 +
  28 + /**
  29 + * 编号
  30 + */
  31 + @TableId
  32 + private Long id;
  33 + /**
  34 + * API 秘钥编号
  35 + *
  36 + * 关联 {@link AiApiKeyDO#getId()}
  37 + */
  38 + private Long keyId;
  39 + /**
  40 + * 模型名称
  41 + */
  42 + private String name;
  43 + /**
  44 + * 模型标志
  45 + */
  46 + private String model;
  47 + /**
  48 + * 平台
  49 + *
  50 + * 枚举 {@link AiPlatformEnum}
  51 + */
  52 + private String platform;
  53 +
  54 + /**
  55 + * 排序值
  56 + */
  57 + private Integer sort;
  58 + /**
  59 + * 状态
  60 + *
  61 + * 枚举 {@link CommonStatusEnum}
  62 + */
  63 + private Integer status;
  64 +
  65 + // ========== 对话配置 ==========
  66 +
  67 + /**
  68 + * 温度参数
  69 + *
  70 + * 用于调整生成回复的随机性和多样性程度:较低的温度值会使输出更收敛于高频词汇,较高的则增加多样性
  71 + */
  72 + private Double temperature;
  73 + /**
  74 + * 单条回复的最大 Token 数量
  75 + */
  76 + private Integer maxTokens;
  77 + /**
  78 + * 上下文的最大 Message 数量
  79 + */
  80 + private Integer maxContexts;
  81 +
  82 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.dataobject.model;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import com.baomidou.mybatisplus.annotation.*;
  6 +import lombok.*;
  7 +
  8 +/**
  9 + * AI 聊天角色 DO
  10 + *
  11 + * @author fansili
  12 + * @since 2024/4/24 19:39
  13 + */
  14 +@TableName(value = "ai_chat_role", autoResultMap = true)
  15 +@KeySequence("ai_chat_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  16 +@Data
  17 +@EqualsAndHashCode(callSuper = true)
  18 +@Builder
  19 +@NoArgsConstructor
  20 +@AllArgsConstructor
  21 +public class AiChatRoleDO extends BaseDO {
  22 +
  23 + /**
  24 + * 编号
  25 + */
  26 + @TableId
  27 + private Long id;
  28 + /**
  29 + * 角色名称
  30 + */
  31 + private String name;
  32 + /**
  33 + * 角色头像
  34 + */
  35 + private String avatar;
  36 + /**
  37 + * 角色分类
  38 + */
  39 + private String category;
  40 + /**
  41 + * 角色描述
  42 + */
  43 + private String description;
  44 + /**
  45 + * 角色设定
  46 + */
  47 + private String systemMessage;
  48 +
  49 + /**
  50 + * 用户编号
  51 + *
  52 + * 关联 AdminUserDO 的 userId 字段
  53 + */
  54 + private Long userId;
  55 +
  56 + /**
  57 + * 模型编号
  58 + *
  59 + * 关联 {@link AiChatModelDO#getId()} 字段
  60 + */
  61 + private Long modelId;
  62 +
  63 + /**
  64 + * 是否公开
  65 + *
  66 + * 1. true - 公开;由管理员在【角色管理】所创建
  67 + * 2. false - 私有;由个人在【我的角色】所创建
  68 + */
  69 + private Boolean publicStatus;
  70 +
  71 + /**
  72 + * 排序值
  73 + */
  74 + private Integer sort;
  75 + /**
  76 + * 状态
  77 + *
  78 + * 枚举 {@link CommonStatusEnum}
  79 + */
  80 + private Integer status;
  81 +
  82 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.dataobject.music;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;
  6 +import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;
  7 +import com.baomidou.mybatisplus.annotation.IdType;
  8 +import com.baomidou.mybatisplus.annotation.TableField;
  9 +import com.baomidou.mybatisplus.annotation.TableId;
  10 +import com.baomidou.mybatisplus.annotation.TableName;
  11 +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
  12 +import lombok.Data;
  13 +
  14 +import java.util.List;
  15 +
  16 +/**
  17 + * AI 音乐 DO
  18 + *
  19 + * @author xiaoxin
  20 + */
  21 +@TableName(value = "ai_music", autoResultMap = true)
  22 +@Data
  23 +public class AiMusicDO extends BaseDO {
  24 +
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId(type = IdType.AUTO)
  29 + private Long id;
  30 +
  31 + /**
  32 + * 用户编号
  33 + * <p>
  34 + * 关联 AdminUserDO 的 userId 字段
  35 + */
  36 + private Long userId;
  37 +
  38 + /**
  39 + * 音乐名称
  40 + */
  41 + private String title;
  42 +
  43 + /**
  44 + * 歌词
  45 + */
  46 + private String lyric;
  47 +
  48 + /**
  49 + * 图片地址
  50 + */
  51 + private String imageUrl;
  52 + /**
  53 + * 音频地址
  54 + */
  55 + private String audioUrl;
  56 + /**
  57 + * 视频地址
  58 + */
  59 + private String videoUrl;
  60 +
  61 + /**
  62 + * 音乐状态
  63 + * <p>
  64 + * 枚举 {@link AiMusicStatusEnum}
  65 + */
  66 + private Integer status;
  67 +
  68 + /**
  69 + * 生成模式
  70 + * <p>
  71 + * 枚举 {@link AiMusicGenerateModeEnum}
  72 + */
  73 + private Integer generateMode;
  74 +
  75 + /**
  76 + * 描述词
  77 + */
  78 + private String description;
  79 +
  80 + /**
  81 + * 平台
  82 + * <p>
  83 + * 枚举 {@link AiPlatformEnum}
  84 + */
  85 + private String platform;
  86 + /**
  87 + * 模型
  88 + */
  89 + private String model;
  90 +
  91 + /**
  92 + * 音乐风格标签
  93 + */
  94 + @TableField(typeHandler = JacksonTypeHandler.class)
  95 + private List<String> tags;
  96 +
  97 + /**
  98 + * 音乐时长
  99 + */
  100 + private Double duration;
  101 +
  102 + /**
  103 + * 是否公开
  104 + */
  105 + private Boolean publicStatus;
  106 +
  107 + /**
  108 + * 任务编号
  109 + */
  110 + private String taskId;
  111 +
  112 + /**
  113 + * 错误信息
  114 + */
  115 + private String errorMessage;
  116 +
  117 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.dataobject.write;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;
  6 +import com.baomidou.mybatisplus.annotation.IdType;
  7 +import com.baomidou.mybatisplus.annotation.TableId;
  8 +import com.baomidou.mybatisplus.annotation.TableName;
  9 +import lombok.Data;
  10 +
  11 +/**
  12 + * AI 写作 DO
  13 + *
  14 + * @author xiaoxin
  15 + */
  16 +@TableName("ai_write")
  17 +@Data
  18 +public class AiWriteDO extends BaseDO {
  19 +
  20 + /**
  21 + * 编号
  22 + */
  23 + @TableId(type = IdType.AUTO)
  24 + private Long id;
  25 +
  26 + /**
  27 + * 用户编号
  28 + *
  29 + * 关联 AdminUserDO 的 userId 字段
  30 + */
  31 + private Long userId;
  32 +
  33 + /**
  34 + * 写作类型
  35 + * <p>
  36 + * 枚举 {@link AiWriteTypeEnum}
  37 + */
  38 + private Integer type;
  39 +
  40 + /**
  41 + * 平台
  42 + *
  43 + * 枚举 {@link AiPlatformEnum}
  44 + */
  45 + private String platform;
  46 + /**
  47 + * 模型
  48 + */
  49 + private String model;
  50 +
  51 + /**
  52 + * 生成内容提示
  53 + */
  54 + private String prompt;
  55 +
  56 + /**
  57 + * 生成的内容
  58 + */
  59 + private String generatedContent;
  60 + /**
  61 + * 原文
  62 + */
  63 + private String originalContent;
  64 +
  65 + /**
  66 + * 长度提示词
  67 + *
  68 + * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_LENGTH}
  69 + */
  70 + private Integer length;
  71 + /**
  72 + * 格式提示词
  73 + *
  74 + * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_FORMAT}
  75 + */
  76 + private Integer format;
  77 + /**
  78 + * 语气提示词
  79 + *
  80 + * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_TONE}
  81 + */
  82 + private Integer tone;
  83 + /**
  84 + * 语言提示词
  85 + *
  86 + * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_LANGUAGE}
  87 + */
  88 + private Integer language;
  89 +
  90 + /**
  91 + * 错误信息
  92 + */
  93 + private String errorMessage;
  94 +
  95 +}
0 96 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatConversationMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.mysql.chat;
  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.chat.vo.conversation.AiChatConversationPageReqVO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
  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 AiChatConversationMapper extends BaseMapperX<AiChatConversationDO> {
  19 +
  20 + default List<AiChatConversationDO> selectListByUserId(Long userId) {
  21 + return selectList(AiChatConversationDO::getUserId, userId);
  22 + }
  23 +
  24 + default List<AiChatConversationDO> selectListByUserIdAndPinned(Long userId, boolean pinned) {
  25 + return selectList(new LambdaQueryWrapperX<AiChatConversationDO>()
  26 + .eq(AiChatConversationDO::getUserId, userId)
  27 + .eq(AiChatConversationDO::getPinned, pinned));
  28 + }
  29 +
  30 + default PageResult<AiChatConversationDO> selectChatConversationPage(AiChatConversationPageReqVO pageReqVO) {
  31 + return selectPage(pageReqVO, new LambdaQueryWrapperX<AiChatConversationDO>()
  32 + .eqIfPresent(AiChatConversationDO::getUserId, pageReqVO.getUserId())
  33 + .likeIfPresent(AiChatConversationDO::getTitle, pageReqVO.getTitle())
  34 + .betweenIfPresent(AiChatConversationDO::getCreateTime, pageReqVO.getCreateTime())
  35 + .orderByDesc(AiChatConversationDO::getId));
  36 + }
  37 +
  38 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatMessageMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.mysql.chat;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.map.MapUtil;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  7 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  8 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
  10 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
  11 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
  12 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
  13 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  14 +import org.apache.ibatis.annotations.Mapper;
  15 +
  16 +import java.util.Collection;
  17 +import java.util.Collections;
  18 +import java.util.List;
  19 +import java.util.Map;
  20 +
  21 +/**
  22 + * AI 聊天对话 Mapper
  23 + *
  24 + * @author fansili
  25 + */
  26 +@Mapper
  27 +public interface AiChatMessageMapper extends BaseMapperX<AiChatMessageDO> {
  28 +
  29 + default List<AiChatMessageDO> selectListByConversationId(Long conversationId) {
  30 + return selectList(new LambdaQueryWrapperX<AiChatMessageDO>()
  31 + .eq(AiChatMessageDO::getConversationId, conversationId)
  32 + .orderByAsc(AiChatMessageDO::getId));
  33 + }
  34 +
  35 + default Map<Long, Integer> selectCountMapByConversationId(Collection<Long> conversationIds) {
  36 + // SQL count 查询
  37 + List<Map<String, Object>> result = selectMaps(new QueryWrapper<AiChatMessageDO>()
  38 + .select("COUNT(id) AS count, conversation_id AS conversationId")
  39 + .in("conversation_id", conversationIds)
  40 + .groupBy("conversation_id"));
  41 + if (CollUtil.isEmpty(result)) {
  42 + return Collections.emptyMap();
  43 + }
  44 + // 转换数据
  45 + return CollectionUtils.convertMap(result,
  46 + record -> MapUtil.getLong(record, "conversationId"),
  47 + record -> MapUtil.getInt(record, "count" ));
  48 + }
  49 +
  50 + default PageResult<AiChatMessageDO> selectPage(AiChatMessagePageReqVO pageReqVO) {
  51 + return selectPage(pageReqVO, new LambdaQueryWrapperX<AiChatMessageDO>()
  52 + .eqIfPresent(AiChatMessageDO::getConversationId, pageReqVO.getConversationId())
  53 + .eqIfPresent(AiChatMessageDO::getUserId, pageReqVO.getUserId())
  54 + .likeIfPresent(AiChatMessageDO::getContent, pageReqVO.getContent())
  55 + .betweenIfPresent(AiChatMessageDO::getCreateTime, pageReqVO.getCreateTime())
  56 + .orderByDesc(AiChatMessageDO::getId));
  57 + }
  58 +
  59 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.mysql.image;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageReqVO;
  8 +import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * AI 绘图 Mapper
  15 + *
  16 + * @author fansili
  17 + */
  18 +@Mapper
  19 +public interface AiImageMapper extends BaseMapperX<AiImageDO> {
  20 +
  21 + default AiImageDO selectByTaskId(String taskId) {
  22 + return this.selectOne(AiImageDO::getTaskId, taskId);
  23 + }
  24 +
  25 + default PageResult<AiImageDO> selectPage(AiImagePageReqVO reqVO) {
  26 + return selectPage(reqVO, new LambdaQueryWrapperX<AiImageDO>()
  27 + .eqIfPresent(AiImageDO::getUserId, reqVO.getUserId())
  28 + .eqIfPresent(AiImageDO::getPlatform, reqVO.getPlatform())
  29 + .eqIfPresent(AiImageDO::getStatus, reqVO.getStatus())
  30 + .eqIfPresent(AiImageDO::getPublicStatus, reqVO.getPublicStatus())
  31 + .betweenIfPresent(AiImageDO::getCreateTime, reqVO.getCreateTime())
  32 + .orderByDesc(AiImageDO::getId));
  33 + }
  34 +
  35 + default PageResult<AiImageDO> selectPage(Long userId, PageParam pageReqVO) {
  36 + return selectPage(pageReqVO, new LambdaQueryWrapperX<AiImageDO>()
  37 + .eq(AiImageDO::getUserId, userId)
  38 + .orderByDesc(AiImageDO::getId));
  39 + }
  40 +
  41 + default List<AiImageDO> selectListByStatusAndPlatform(Integer status, String platform) {
  42 + return selectList(AiImageDO::getStatus, status,
  43 + AiImageDO::getPlatform, platform);
  44 + }
  45 +
  46 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/mindmap/AiMindMapMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.mysql.mindmap;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO;
  5 +import org.apache.ibatis.annotations.Mapper;
  6 +
  7 +/**
  8 + * AI 思维导图 Mapper
  9 + *
  10 + * @author xiaoxin
  11 + */
  12 +@Mapper
  13 +public interface AiMindMapMapper extends BaseMapperX<AiMindMapDO> {
  14 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.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.apikey.AiApiKeyPageReqVO;
  8 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +/**
  12 + * AI API 密钥 Mapper
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Mapper
  17 +public interface AiApiKeyMapper extends BaseMapperX<AiApiKeyDO> {
  18 +
  19 + default PageResult<AiApiKeyDO> selectPage(AiApiKeyPageReqVO reqVO) {
  20 + return selectPage(reqVO, new LambdaQueryWrapperX<AiApiKeyDO>()
  21 + .likeIfPresent(AiApiKeyDO::getName, reqVO.getName())
  22 + .eqIfPresent(AiApiKeyDO::getPlatform, reqVO.getPlatform())
  23 + .eqIfPresent(AiApiKeyDO::getStatus, reqVO.getStatus())
  24 + .orderByDesc(AiApiKeyDO::getId));
  25 + }
  26 +
  27 + default AiApiKeyDO selectFirstByPlatformAndStatus(String platform, Integer status) {
  28 + return selectOne(new QueryWrapperX<AiApiKeyDO>()
  29 + .eq("platform", platform)
  30 + .eq("status", status)
  31 + .limitN(1)
  32 + .orderByAsc("id"));
  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/dal/mysql/model/AiChatModelMapper.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.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/AiChatRoleMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.mysql.model;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;
  8 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * AI 聊天角色 Mapper
  15 + *
  16 + * @author fansili
  17 + */
  18 +@Mapper
  19 +public interface AiChatRoleMapper extends BaseMapperX<AiChatRoleDO> {
  20 +
  21 + default PageResult<AiChatRoleDO> selectPage(AiChatRolePageReqVO reqVO) {
  22 + return selectPage(reqVO, new LambdaQueryWrapperX<AiChatRoleDO>()
  23 + .likeIfPresent(AiChatRoleDO::getName, reqVO.getName())
  24 + .eqIfPresent(AiChatRoleDO::getCategory, reqVO.getCategory())
  25 + .eqIfPresent(AiChatRoleDO::getPublicStatus, reqVO.getPublicStatus())
  26 + .orderByAsc(AiChatRoleDO::getSort));
  27 + }
  28 +
  29 + default PageResult<AiChatRoleDO> selectPageByMy(AiChatRolePageReqVO reqVO, Long userId) {
  30 + return selectPage(reqVO, new LambdaQueryWrapperX<AiChatRoleDO>()
  31 + .likeIfPresent(AiChatRoleDO::getName, reqVO.getName())
  32 + .eqIfPresent(AiChatRoleDO::getCategory, reqVO.getCategory())
  33 + // 情况一:公开
  34 + .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getPublicStatus, reqVO.getPublicStatus())
  35 + // 情况二:私有
  36 + .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getUserId, userId)
  37 + .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
  38 + .orderByAsc(AiChatRoleDO::getSort));
  39 + }
  40 +
  41 + default List<AiChatRoleDO> selectListGroupByCategory(Integer status) {
  42 + return selectList(new LambdaQueryWrapperX<AiChatRoleDO>()
  43 + .select(AiChatRoleDO::getCategory)
  44 + .eq(AiChatRoleDO::getStatus, status)
  45 + .groupBy(AiChatRoleDO::getCategory));
  46 + }
  47 +
  48 + default List<AiChatRoleDO> selectListByName(String name) {
  49 + return selectList(new LambdaQueryWrapperX<AiChatRoleDO>()
  50 + .likeIfPresent(AiChatRoleDO::getName, name)
  51 + .orderByAsc(AiChatRoleDO::getSort));
  52 + }
  53 +
  54 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/music/AiMusicMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.mysql.music;
  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.music.vo.AiMusicPageReqVO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * AI 音乐 Mapper
  14 + *
  15 + * @author xiaoxin
  16 + */
  17 +@Mapper
  18 +public interface AiMusicMapper extends BaseMapperX<AiMusicDO> {
  19 +
  20 + default List<AiMusicDO> selectListByStatus(Integer status) {
  21 + return selectList(AiMusicDO::getStatus, status);
  22 + }
  23 +
  24 + default PageResult<AiMusicDO> selectPage(AiMusicPageReqVO reqVO) {
  25 + return selectPage(reqVO, new LambdaQueryWrapperX<AiMusicDO>()
  26 + .eqIfPresent(AiMusicDO::getUserId, reqVO.getUserId())
  27 + .eqIfPresent(AiMusicDO::getTitle, reqVO.getTitle())
  28 + .eqIfPresent(AiMusicDO::getStatus, reqVO.getStatus())
  29 + .eqIfPresent(AiMusicDO::getGenerateMode, reqVO.getGenerateMode())
  30 + .betweenIfPresent(AiMusicDO::getCreateTime, reqVO.getCreateTime())
  31 + .eqIfPresent(AiMusicDO::getPublicStatus, reqVO.getPublicStatus())
  32 + .orderByDesc(AiMusicDO::getId));
  33 + }
  34 +
  35 + default PageResult<AiMusicDO> selectPageByMy(AiMusicPageReqVO reqVO, Long userId) {
  36 + return selectPage(reqVO, new LambdaQueryWrapperX<AiMusicDO>()
  37 + // 情况一:公开
  38 + .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiMusicDO::getPublicStatus, reqVO.getPublicStatus())
  39 + // 情况二:私有
  40 + .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiMusicDO::getUserId, userId)
  41 + .orderByAsc(AiMusicDO::getId));
  42 + }
  43 +
  44 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/write/AiWriteMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.mysql.write;
  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.write.vo.AiWritePageReqVO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +/**
  11 + * AI 写作 Mapper
  12 + *
  13 + * @author xiaoxin
  14 + */
  15 +@Mapper
  16 +public interface AiWriteMapper extends BaseMapperX<AiWriteDO> {
  17 +
  18 + default PageResult<AiWriteDO> selectPage(AiWritePageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<AiWriteDO>()
  20 + .eqIfPresent(AiWriteDO::getUserId, reqVO.getUserId())
  21 + .eqIfPresent(AiWriteDO::getType, reqVO.getType())
  22 + .eqIfPresent(AiWriteDO::getPlatform, reqVO.getPlatform())
  23 + .betweenIfPresent(AiWriteDO::getCreateTime, reqVO.getCreateTime())
  24 + .orderByDesc(AiWriteDO::getId));
  25 + }
  26 +
  27 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/job/image/AiMidjourneySyncJob.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.job.image;
  2 +
  3 +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
  4 +import cn.iocoder.yudao.module.ai.service.image.AiImageService;
  5 +import jakarta.annotation.Resource;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +/**
  10 + * Midjourney 同步 Job:定时拉去 midjourney 绘制状态
  11 + *
  12 + * @author fansili
  13 + */
  14 +@Component
  15 +@Slf4j
  16 +public class AiMidjourneySyncJob implements JobHandler {
  17 +
  18 + @Resource
  19 + private AiImageService imageService;
  20 +
  21 + @Override
  22 + public String execute(String param) {
  23 + Integer count = imageService.midjourneySync();
  24 + log.info("[execute][同步 Midjourney ({}) 个]", count);
  25 + return String.format("同步 Midjourney %s 个", count);
  26 + }
  27 +
  28 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/job/music/AiSunoSyncJob.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.job.music;
  2 +
  3 +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
  4 +import cn.iocoder.yudao.module.ai.service.music.AiMusicService;
  5 +import jakarta.annotation.Resource;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +
  10 +/**
  11 + * 同步 Suno 任务状态以及回写对应的音乐信息 Job
  12 + *
  13 + * @author xiaoxin
  14 + */
  15 +@Component
  16 +@Slf4j
  17 +public class AiSunoSyncJob implements JobHandler {
  18 +
  19 + @Resource
  20 + private AiMusicService musicService;
  21 +
  22 + @Override
  23 + public String execute(String param) {
  24 + Integer count = musicService.syncMusic();
  25 + log.info("[execute][同步 Suno ({}) 个]", count);
  26 + return String.format("同步 Suno %s 个", count);
  27 + }
  28 +
  29 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.chat;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
  8 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * AI 聊天对话 Service 接口
  14 + *
  15 + * @author fansili
  16 + */
  17 +public interface AiChatConversationService {
  18 +
  19 + /**
  20 + * 创建【我的】聊天对话
  21 + *
  22 + * @param createReqVO 创建信息
  23 + * @param userId 用户编号
  24 + * @return 编号
  25 + */
  26 + Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId);
  27 +
  28 + /**
  29 + * 更新【我的】聊天对话
  30 + *
  31 + * @param updateReqVO 更新信息
  32 + * @param userId 用户编号
  33 + */
  34 + void updateChatConversationMy(AiChatConversationUpdateMyReqVO updateReqVO, Long userId);
  35 +
  36 + /**
  37 + * 获得【我的】聊天对话列表
  38 + *
  39 + * @param userId 用户编号
  40 + * @return 聊天对话列表
  41 + */
  42 + List<AiChatConversationDO> getChatConversationListByUserId(Long userId);
  43 +
  44 + /**
  45 + * 获得聊天对话
  46 + *
  47 + * @param id 编号
  48 + * @return 聊天对话
  49 + */
  50 + AiChatConversationDO getChatConversation(Long id);
  51 +
  52 + /**
  53 + * 删除【我的】聊天对话
  54 + *
  55 + * @param id 编号
  56 + * @param userId 用户编号
  57 + */
  58 + void deleteChatConversationMy(Long id, Long userId);
  59 +
  60 + /**
  61 + * 【管理员】删除聊天对话
  62 + *
  63 + * @param id 编号
  64 + */
  65 + void deleteChatConversationByAdmin(Long id);
  66 +
  67 + /**
  68 + * 校验聊天对话是否存在
  69 + *
  70 + * @param id 编号
  71 + * @return 聊天对话
  72 + */
  73 + AiChatConversationDO validateChatConversationExists(Long id);
  74 +
  75 + /**
  76 + * 删除【我的】 + 非置顶的聊天对话
  77 + *
  78 + * @param userId 用户编号
  79 + */
  80 + void deleteChatConversationMyByUnpinned(Long userId);
  81 +
  82 + /**
  83 + * 获得聊天对话的分页列表
  84 + *
  85 + * @param pageReqVO 分页查询
  86 + * @return 聊天对话的分页列表
  87 + */
  88 + PageResult<AiChatConversationDO> getChatConversationPage(AiChatConversationPageReqVO pageReqVO);
  89 +
  90 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.chat;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
  5 +import cn.hutool.core.util.ObjUtil;
  6 +import cn.hutool.core.util.ObjectUtil;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
  10 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
  11 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
  12 +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.AiChatRoleDO;
  15 +import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper;
  16 +import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
  17 +import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
  18 +import jakarta.annotation.Resource;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.stereotype.Service;
  21 +import org.springframework.validation.annotation.Validated;
  22 +
  23 +import java.time.LocalDateTime;
  24 +import java.util.List;
  25 +
  26 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  27 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  28 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR;
  29 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;
  30 +
  31 +/**
  32 + * AI 聊天对话 Service 实现类
  33 + *
  34 + * @author fansili
  35 + */
  36 +@Service
  37 +@Validated
  38 +@Slf4j
  39 +public class AiChatConversationServiceImpl implements AiChatConversationService {
  40 +
  41 + @Resource
  42 + private AiChatConversationMapper chatConversationMapper;
  43 +
  44 + @Resource
  45 + private AiChatModelService chatModalService;
  46 + @Resource
  47 + private AiChatRoleService chatRoleService;
  48 +
  49 + @Override
  50 + public Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId) {
  51 + // 1.1 获得 AiChatRoleDO 聊天角色
  52 + AiChatRoleDO role = createReqVO.getRoleId() != null ? chatRoleService.validateChatRole(createReqVO.getRoleId()) : null;
  53 + // 1.2 获得 AiChatModelDO 聊天模型
  54 + AiChatModelDO model = role != null && role.getModelId() != null ? chatModalService.validateChatModel(role.getModelId())
  55 + : chatModalService.getRequiredDefaultChatModel();
  56 + Assert.notNull(model, "必须找到默认模型");
  57 + validateChatModel(model);
  58 +
  59 + // 2. 创建 AiChatConversationDO 聊天对话
  60 + AiChatConversationDO conversation = new AiChatConversationDO().setUserId(userId).setPinned(false)
  61 + .setModelId(model.getId()).setModel(model.getModel())
  62 + .setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts());
  63 + if (role != null) {
  64 + conversation.setTitle(role.getName()).setRoleId(role.getId()).setSystemMessage(role.getSystemMessage());
  65 + } else {
  66 + conversation.setTitle(AiChatConversationDO.TITLE_DEFAULT);
  67 + }
  68 + chatConversationMapper.insert(conversation);
  69 + return conversation.getId();
  70 + }
  71 +
  72 + @Override
  73 + public void updateChatConversationMy(AiChatConversationUpdateMyReqVO updateReqVO, Long userId) {
  74 + // 1.1 校验对话是否存在
  75 + AiChatConversationDO conversation = validateChatConversationExists(updateReqVO.getId());
  76 + if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
  77 + throw exception(CHAT_CONVERSATION_NOT_EXISTS);
  78 + }
  79 + // 1.2 校验模型是否存在(修改模型的情况)
  80 + AiChatModelDO model = null;
  81 + if (updateReqVO.getModelId() != null) {
  82 + model = chatModalService.validateChatModel(updateReqVO.getModelId());
  83 + }
  84 +
  85 + // 2. 更新对话信息
  86 + AiChatConversationDO updateObj = BeanUtils.toBean(updateReqVO, AiChatConversationDO.class);
  87 + if (Boolean.TRUE.equals(updateReqVO.getPinned())) {
  88 + updateObj.setPinnedTime(LocalDateTime.now());
  89 + }
  90 + if (model != null) {
  91 + updateObj.setModel(model.getModel());
  92 + }
  93 + chatConversationMapper.updateById(updateObj);
  94 + }
  95 +
  96 + @Override
  97 + public List<AiChatConversationDO> getChatConversationListByUserId(Long userId) {
  98 + return chatConversationMapper.selectListByUserId(userId);
  99 + }
  100 +
  101 + @Override
  102 + public AiChatConversationDO getChatConversation(Long id) {
  103 + return chatConversationMapper.selectById(id);
  104 + }
  105 +
  106 + @Override
  107 + public void deleteChatConversationMy(Long id, Long userId) {
  108 + // 1. 校验对话是否存在
  109 + AiChatConversationDO conversation = validateChatConversationExists(id);
  110 + if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), userId)) {
  111 + throw exception(CHAT_CONVERSATION_NOT_EXISTS);
  112 + }
  113 + // 2. 执行删除
  114 + chatConversationMapper.deleteById(id);
  115 + }
  116 +
  117 + @Override
  118 + public void deleteChatConversationByAdmin(Long id) {
  119 + // 1. 校验对话是否存在
  120 + AiChatConversationDO conversation = validateChatConversationExists(id);
  121 + if (conversation == null) {
  122 + throw exception(CHAT_CONVERSATION_NOT_EXISTS);
  123 + }
  124 + // 2. 执行删除
  125 + chatConversationMapper.deleteById(id);
  126 + }
  127 +
  128 + private void validateChatModel(AiChatModelDO model) {
  129 + if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) {
  130 + return;
  131 + }
  132 + throw exception(CHAT_CONVERSATION_MODEL_ERROR);
  133 + }
  134 +
  135 + public AiChatConversationDO validateChatConversationExists(Long id) {
  136 + AiChatConversationDO conversation = chatConversationMapper.selectById(id);
  137 + if (conversation == null) {
  138 + throw exception(CHAT_CONVERSATION_NOT_EXISTS);
  139 + }
  140 + return conversation;
  141 + }
  142 +
  143 + @Override
  144 + public void deleteChatConversationMyByUnpinned(Long userId) {
  145 + List<AiChatConversationDO> list = chatConversationMapper.selectListByUserIdAndPinned(userId, false);
  146 + if (CollUtil.isEmpty(list)) {
  147 + return;
  148 + }
  149 + chatConversationMapper.deleteBatchIds(convertList(list, AiChatConversationDO::getId));
  150 + }
  151 +
  152 + @Override
  153 + public PageResult<AiChatConversationDO> getChatConversationPage(AiChatConversationPageReqVO pageReqVO) {
  154 + return chatConversationMapper.selectChatConversationPage(pageReqVO);
  155 + }
  156 +
  157 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.chat;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
  8 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
  9 +import reactor.core.publisher.Flux;
  10 +
  11 +import java.util.Collection;
  12 +import java.util.List;
  13 +import java.util.Map;
  14 +
  15 +/**
  16 + * AI 聊天消息 Service 接口
  17 + *
  18 + * @author fansili
  19 + */
  20 +public interface AiChatMessageService {
  21 +
  22 + /**
  23 + * 发送消息
  24 + *
  25 + * @param sendReqVO 发送信息
  26 + * @param userId 用户编号
  27 + * @return 发送结果
  28 + */
  29 + AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId);
  30 +
  31 + /**
  32 + * 发送消息
  33 + *
  34 + * @param sendReqVO 发送信息
  35 + * @param userId 用户编号
  36 + * @return 发送结果
  37 + */
  38 + Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId);
  39 +
  40 + /**
  41 + * 获得指定对话的消息列表
  42 + *
  43 + * @param conversationId 对话编号
  44 + * @return 消息列表
  45 + */
  46 + List<AiChatMessageDO> getChatMessageListByConversationId(Long conversationId);
  47 +
  48 + /**
  49 + * 删除消息
  50 + *
  51 + * @param id 消息编号
  52 + * @param userId 用户编号
  53 + */
  54 + void deleteChatMessage(Long id, Long userId);
  55 +
  56 + /**
  57 + * 删除指定对话的消息
  58 + *
  59 + * @param conversationId 对话编号
  60 + * @param userId 用户编号
  61 + */
  62 + void deleteChatMessageByConversationId(Long conversationId, Long userId);
  63 +
  64 + /**
  65 + * 【管理员】删除消息
  66 + *
  67 + * @param id 消息编号
  68 + */
  69 + void deleteChatMessageByAdmin(Long id);
  70 +
  71 + /**
  72 + * 获得聊天对话的消息数量 Map
  73 + *
  74 + * @param conversationIds 对话编号数组
  75 + * @return 消息数量 Map
  76 + */
  77 + Map<Long, Integer> getChatMessageCountMap(Collection<Long> conversationIds);
  78 +
  79 + /**
  80 + * 获得聊天消息的分页
  81 + *
  82 + * @param pageReqVO 分页查询
  83 + * @return 聊天消息的分页
  84 + */
  85 + PageResult<AiChatMessageDO> getChatMessagePage(AiChatMessagePageReqVO pageReqVO);
  86 +
  87 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.chat;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
  5 +import cn.hutool.core.util.StrUtil;
  6 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  7 +import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
  8 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  9 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  10 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  11 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
  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.AiChatMessageSendReqVO;
  14 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
  15 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
  16 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
  17 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  18 +import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper;
  19 +import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
  20 +import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
  21 +import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
  22 +import jakarta.annotation.Resource;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.springframework.ai.chat.messages.*;
  25 +import org.springframework.ai.chat.model.ChatModel;
  26 +import org.springframework.ai.chat.model.ChatResponse;
  27 +import org.springframework.ai.chat.model.StreamingChatModel;
  28 +import org.springframework.ai.chat.prompt.ChatOptions;
  29 +import org.springframework.ai.chat.prompt.Prompt;
  30 +import org.springframework.stereotype.Service;
  31 +import org.springframework.transaction.annotation.Transactional;
  32 +import reactor.core.publisher.Flux;
  33 +
  34 +import java.time.LocalDateTime;
  35 +import java.util.*;
  36 +
  37 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  38 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
  39 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  40 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  41 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;
  42 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_NOT_EXIST;
  43 +
  44 +/**
  45 + * AI 聊天消息 Service 实现类
  46 + *
  47 + * @author fansili
  48 + */
  49 +@Service
  50 +@Slf4j
  51 +public class AiChatMessageServiceImpl implements AiChatMessageService {
  52 +
  53 + @Resource
  54 + private AiChatMessageMapper chatMessageMapper;
  55 +
  56 + @Resource
  57 + private AiChatConversationService chatConversationService;
  58 + @Resource
  59 + private AiChatModelService chatModalService;
  60 + @Resource
  61 + private AiApiKeyService apiKeyService;
  62 +
  63 + @Transactional(rollbackFor = Exception.class)
  64 + public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) {
  65 + // 1.1 校验对话存在
  66 + AiChatConversationDO conversation = chatConversationService.validateChatConversationExists(sendReqVO.getConversationId());
  67 + if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
  68 + throw exception(CHAT_CONVERSATION_NOT_EXISTS);
  69 + }
  70 + List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());
  71 + // 1.2 校验模型
  72 + AiChatModelDO model = chatModalService.validateChatModel(conversation.getModelId());
  73 + ChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
  74 +
  75 + // 2. 插入 user 发送消息
  76 + AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
  77 + userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext());
  78 +
  79 + // 3.1 插入 assistant 接收消息
  80 + AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
  81 + userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext());
  82 +
  83 + // 3.2 创建 chat 需要的 Prompt
  84 + Prompt prompt = buildPrompt(conversation, historyMessages, model, sendReqVO);
  85 + ChatResponse chatResponse = chatModel.call(prompt);
  86 +
  87 + // 3.3 段式返回
  88 + String newContent = chatResponse.getResult().getOutput().getContent();
  89 + chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(newContent));
  90 + return new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
  91 + .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent));
  92 + }
  93 +
  94 + @Override
  95 + public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId) {
  96 + // 1.1 校验对话存在
  97 + AiChatConversationDO conversation = chatConversationService.validateChatConversationExists(sendReqVO.getConversationId());
  98 + if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
  99 + throw exception(CHAT_CONVERSATION_NOT_EXISTS);
  100 + }
  101 + List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());
  102 + // 1.2 校验模型
  103 + AiChatModelDO model = chatModalService.validateChatModel(conversation.getModelId());
  104 + StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
  105 +
  106 + // 2. 插入 user 发送消息
  107 + AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
  108 + userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext());
  109 +
  110 + // 3.1 插入 assistant 接收消息
  111 + AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
  112 + userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext());
  113 +
  114 + // 3.2 构建 Prompt,并进行调用
  115 + Prompt prompt = buildPrompt(conversation, historyMessages, model, sendReqVO);
  116 + Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
  117 +
  118 + // 3.3 流式返回
  119 + // TODO 注意:Schedulers.immediate() 目的是,避免默认 Schedulers.parallel() 并发消费 chunk 导致 SSE 响应前端会乱序问题
  120 + StringBuffer contentBuffer = new StringBuffer();
  121 + return streamResponse.map(chunk -> {
  122 + String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null;
  123 + newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
  124 + contentBuffer.append(newContent);
  125 + // 响应结果
  126 + return success(new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
  127 + .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent)));
  128 + }).doOnComplete(() -> {
  129 + // 忽略租户,因为 Flux 异步无法透传租户
  130 + TenantUtils.executeIgnore(() ->
  131 + chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString())));
  132 + }).doOnError(throwable -> {
  133 + log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable);
  134 + // 忽略租户,因为 Flux 异步无法透传租户
  135 + TenantUtils.executeIgnore(() ->
  136 + chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage())));
  137 + }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR)));
  138 + }
  139 +
  140 + private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,
  141 + AiChatModelDO model, AiChatMessageSendReqVO sendReqVO) {
  142 + // 1. 构建 Prompt Message 列表
  143 + List<Message> chatMessages = new ArrayList<>();
  144 + // 1.1 system context 角色设定
  145 + if (StrUtil.isNotBlank(conversation.getSystemMessage())) {
  146 + chatMessages.add(new SystemMessage(conversation.getSystemMessage()));
  147 + }
  148 + // 1.2 history message 历史消息
  149 + List<AiChatMessageDO> contextMessages = filterContextMessages(messages, conversation, sendReqVO);
  150 + contextMessages.forEach(message -> chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent())));
  151 + // 1.3 user message 新发送消息
  152 + chatMessages.add(new UserMessage(sendReqVO.getContent()));
  153 +
  154 + // 2. 构建 ChatOptions 对象
  155 + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
  156 + ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(),
  157 + conversation.getTemperature(), conversation.getMaxTokens());
  158 + return new Prompt(chatMessages, chatOptions);
  159 + }
  160 +
  161 + /**
  162 + * 从历史消息中,获得倒序的 n 组消息作为消息上下文
  163 + *
  164 + * n 组:指的是 user + assistant 形成一组
  165 + *
  166 + * @param messages 消息列表
  167 + * @param conversation 对话
  168 + * @param sendReqVO 发送请求
  169 + * @return 消息上下文
  170 + */
  171 + private List<AiChatMessageDO> filterContextMessages(List<AiChatMessageDO> messages,
  172 + AiChatConversationDO conversation,
  173 + AiChatMessageSendReqVO sendReqVO) {
  174 + if (conversation.getMaxContexts() == null || ObjUtil.notEqual(sendReqVO.getUseContext(), Boolean.TRUE)) {
  175 + return Collections.emptyList();
  176 + }
  177 + List<AiChatMessageDO> contextMessages = new ArrayList<>(conversation.getMaxContexts() * 2);
  178 + for (int i = messages.size() - 1; i >= 0; i--) {
  179 + AiChatMessageDO assistantMessage = CollUtil.get(messages, i);
  180 + if (assistantMessage == null || assistantMessage.getReplyId() == null) {
  181 + continue;
  182 + }
  183 + AiChatMessageDO userMessage = CollUtil.get(messages, i - 1);
  184 + if (userMessage == null || ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId())
  185 + || StrUtil.isEmpty(assistantMessage.getContent())) {
  186 + continue;
  187 + }
  188 + // 由于后续要 reverse 反转,所以先添加 assistantMessage
  189 + contextMessages.add(assistantMessage);
  190 + contextMessages.add(userMessage);
  191 + // 超过最大上下文,结束
  192 + if (contextMessages.size() >= conversation.getMaxContexts() * 2) {
  193 + break;
  194 + }
  195 + }
  196 + Collections.reverse(contextMessages);
  197 + return contextMessages;
  198 + }
  199 +
  200 + private AiChatMessageDO createChatMessage(Long conversationId, Long replyId,
  201 + AiChatModelDO model, Long userId, Long roleId,
  202 + MessageType messageType, String content, Boolean useContext) {
  203 + AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId)
  204 + .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId)
  205 + .setType(messageType.getValue()).setContent(content).setUseContext(useContext);
  206 + message.setCreateTime(LocalDateTime.now());
  207 + chatMessageMapper.insert(message);
  208 + return message;
  209 + }
  210 +
  211 + @Override
  212 + public List<AiChatMessageDO> getChatMessageListByConversationId(Long conversationId) {
  213 + return chatMessageMapper.selectListByConversationId(conversationId);
  214 + }
  215 +
  216 + @Override
  217 + public void deleteChatMessage(Long id, Long userId) {
  218 + // 1. 校验消息存在
  219 + AiChatMessageDO message = chatMessageMapper.selectById(id);
  220 + if (message == null || ObjUtil.notEqual(message.getUserId(), userId)) {
  221 + throw exception(CHAT_MESSAGE_NOT_EXIST);
  222 + }
  223 + // 2. 执行删除
  224 + chatMessageMapper.deleteById(id);
  225 + }
  226 +
  227 + @Override
  228 + public void deleteChatMessageByConversationId(Long conversationId, Long userId) {
  229 + // 1. 校验消息存在
  230 + List<AiChatMessageDO> messages = chatMessageMapper.selectListByConversationId(conversationId);
  231 + if (CollUtil.isEmpty(messages) || ObjUtil.notEqual(messages.get(0).getUserId(), userId)) {
  232 + throw exception(CHAT_MESSAGE_NOT_EXIST);
  233 + }
  234 + // 2. 执行删除
  235 + chatMessageMapper.deleteBatchIds(convertList(messages, AiChatMessageDO::getId));
  236 + }
  237 +
  238 + @Override
  239 + public void deleteChatMessageByAdmin(Long id) {
  240 + // 1. 校验消息存在
  241 + AiChatMessageDO message = chatMessageMapper.selectById(id);
  242 + if (message == null) {
  243 + throw exception(CHAT_MESSAGE_NOT_EXIST);
  244 + }
  245 + // 2. 执行删除
  246 + chatMessageMapper.deleteById(id);
  247 + }
  248 +
  249 + @Override
  250 + public Map<Long, Integer> getChatMessageCountMap(Collection<Long> conversationIds) {
  251 + return chatMessageMapper.selectCountMapByConversationId(conversationIds);
  252 + }
  253 +
  254 + @Override
  255 + public PageResult<AiChatMessageDO> getChatMessagePage(AiChatMessagePageReqVO pageReqVO) {
  256 + return chatMessageMapper.selectPage(pageReqVO);
  257 + }
  258 +
  259 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.image;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageReqVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageUpdateReqVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;
  10 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;
  11 +import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
  12 +import jakarta.validation.Valid;
  13 +
  14 +import java.util.List;
  15 +
  16 +/**
  17 + * AI 绘图 Service 接口
  18 + *
  19 + * @author fansili
  20 + */
  21 +public interface AiImageService {
  22 +
  23 + /**
  24 + * 获取【我的】绘图分页
  25 + *
  26 + * @param userId 用户编号
  27 + * @param pageReqVO 分页条件
  28 + * @return 绘图分页
  29 + */
  30 + PageResult<AiImageDO> getImagePageMy(Long userId, PageParam pageReqVO);
  31 +
  32 + /**
  33 + * 获得绘图记录
  34 + *
  35 + * @param id 绘图编号
  36 + * @return 绘图记录
  37 + */
  38 + AiImageDO getImage(Long id);
  39 +
  40 + /**
  41 + * 获得绘图列表
  42 + *
  43 + * @param ids 绘图编号数组
  44 + * @return 绘图记录列表
  45 + */
  46 + List<AiImageDO> getImageList(List<Long> ids);
  47 +
  48 + /**
  49 + * 绘制图片
  50 + *
  51 + * @param userId 用户编号
  52 + * @param drawReqVO 绘制请求
  53 + * @return 绘画编号
  54 + */
  55 + Long drawImage(Long userId, AiImageDrawReqVO drawReqVO);
  56 +
  57 + /**
  58 + * 删除【我的】绘画记录
  59 + *
  60 + * @param id 绘画编号
  61 + * @param userId 用户编号
  62 + */
  63 + void deleteImageMy(Long id, Long userId);
  64 +
  65 + /**
  66 + * 获得绘画分页
  67 + *
  68 + * @param pageReqVO 分页查询
  69 + * @return 绘画分页
  70 + */
  71 + PageResult<AiImageDO> getImagePage(AiImagePageReqVO pageReqVO);
  72 +
  73 + /**
  74 + * 更新绘画
  75 + *
  76 + * @param updateReqVO 更新信息
  77 + */
  78 + void updateImage(@Valid AiImageUpdateReqVO updateReqVO);
  79 +
  80 + /**
  81 + * 删除绘画
  82 + *
  83 + * @param id 编号
  84 + */
  85 + void deleteImage(Long id);
  86 +
  87 + // ================ midjourney 专属 ================
  88 +
  89 + /**
  90 + * 【Midjourney】生成图片
  91 + *
  92 + * @param userId 用户编号
  93 + * @param reqVO 绘制请求
  94 + * @return 绘画编号
  95 + */
  96 + Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO reqVO);
  97 +
  98 + /**
  99 + * 【Midjourney】同步图片进展
  100 + *
  101 + * @return 同步成功数量
  102 + */
  103 + Integer midjourneySync();
  104 +
  105 + /**
  106 + * 【Midjourney】通知图片进展
  107 + *
  108 + * @param notify 通知
  109 + */
  110 + void midjourneyNotify(MidjourneyApi.Notify notify);
  111 +
  112 + /**
  113 + * 【Midjourney】Action 操作(放大、缩小、U1、U2...)
  114 + *
  115 + * @param userId 用户编号
  116 + * @param reqVO 绘制请求
  117 + * @return 绘画编号
  118 + */
  119 + Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO);
  120 +
  121 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.image;
  2 +
  3 +import cn.hutool.core.bean.BeanUtil;
  4 +import cn.hutool.core.codec.Base64;
  5 +import cn.hutool.core.collection.CollUtil;
  6 +import cn.hutool.core.map.MapUtil;
  7 +import cn.hutool.core.util.ObjUtil;
  8 +import cn.hutool.core.util.StrUtil;
  9 +import cn.hutool.extra.spring.SpringUtil;
  10 +import cn.hutool.http.HttpUtil;
  11 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  12 +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
  13 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  14 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  15 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  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.AiImageUpdateReqVO;
  19 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;
  20 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;
  21 +import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
  22 +import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper;
  23 +import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
  24 +import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
  25 +import cn.iocoder.yudao.module.infra.api.file.FileApi;
  26 +import com.alibaba.cloud.ai.tongyi.image.TongYiImagesOptions;
  27 +import jakarta.annotation.Resource;
  28 +import lombok.extern.slf4j.Slf4j;
  29 +import org.springframework.ai.image.ImageModel;
  30 +import org.springframework.ai.image.ImageOptions;
  31 +import org.springframework.ai.image.ImagePrompt;
  32 +import org.springframework.ai.image.ImageResponse;
  33 +import org.springframework.ai.openai.OpenAiImageOptions;
  34 +import org.springframework.ai.qianfan.QianFanImageOptions;
  35 +import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
  36 +import org.springframework.scheduling.annotation.Async;
  37 +import org.springframework.stereotype.Service;
  38 +import org.springframework.transaction.annotation.Transactional;
  39 +
  40 +import java.time.LocalDateTime;
  41 +import java.util.Collections;
  42 +import java.util.List;
  43 +import java.util.Map;
  44 +
  45 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  46 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  47 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  48 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
  49 +
  50 +/**
  51 + * AI 绘画 Service 实现类
  52 + *
  53 + * @author fansili
  54 + */
  55 +@Service
  56 +@Slf4j
  57 +public class AiImageServiceImpl implements AiImageService {
  58 +
  59 + @Resource
  60 + private AiImageMapper imageMapper;
  61 +
  62 + @Resource
  63 + private FileApi fileApi;
  64 +
  65 + @Resource
  66 + private AiApiKeyService apiKeyService;
  67 +
  68 + @Override
  69 + public PageResult<AiImageDO> getImagePageMy(Long userId, PageParam pageReqVO) {
  70 + return imageMapper.selectPage(userId, pageReqVO);
  71 + }
  72 +
  73 + @Override
  74 + public AiImageDO getImage(Long id) {
  75 + return imageMapper.selectById(id);
  76 + }
  77 +
  78 + @Override
  79 + public List<AiImageDO> getImageList(List<Long> ids) {
  80 + if (CollUtil.isEmpty(ids)) {
  81 + return Collections.emptyList();
  82 + }
  83 + return imageMapper.selectBatchIds(ids);
  84 + }
  85 +
  86 + @Override
  87 + public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) {
  88 + // 1. 保存数据库
  89 + AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)
  90 + .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
  91 + imageMapper.insert(image);
  92 + // 2. 异步绘制,后续前端通过返回的 id 进行轮询结果
  93 + getSelf().executeDrawImage(image, drawReqVO);
  94 + return image.getId();
  95 + }
  96 +
  97 + @Async
  98 + public void executeDrawImage(AiImageDO image, AiImageDrawReqVO req) {
  99 + try {
  100 + // 1.1 构建请求
  101 + ImageOptions request = buildImageOptions(req);
  102 + // 1.2 执行请求
  103 + ImageModel imageModel = apiKeyService.getImageModel(AiPlatformEnum.validatePlatform(req.getPlatform()));
  104 + ImageResponse response = imageModel.call(new ImagePrompt(req.getPrompt(), request));
  105 +
  106 + // 2. 上传到文件服务
  107 + byte[] fileContent = Base64.decode(response.getResult().getOutput().getB64Json());
  108 + String filePath = fileApi.createFile(fileContent);
  109 +
  110 + // 3. 更新数据库
  111 + imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.SUCCESS.getStatus())
  112 + .setPicUrl(filePath).setFinishTime(LocalDateTime.now()));
  113 + } catch (Exception ex) {
  114 + log.error("[doDall][image({}) 生成异常]", image, ex);
  115 + imageMapper.updateById(new AiImageDO().setId(image.getId())
  116 + .setStatus(AiImageStatusEnum.FAIL.getStatus())
  117 + .setErrorMessage(ex.getMessage()).setFinishTime(LocalDateTime.now()));
  118 + }
  119 + }
  120 +
  121 + private static ImageOptions buildImageOptions(AiImageDrawReqVO draw) {
  122 + if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) {
  123 + // https://platform.openai.com/docs/api-reference/images/create
  124 + return OpenAiImageOptions.builder().withModel(draw.getModel())
  125 + .withHeight(draw.getHeight()).withWidth(draw.getWidth())
  126 + .withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格
  127 + .withResponseFormat("b64_json")
  128 + .build();
  129 + } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) {
  130 + // https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage
  131 + // https://platform.stability.ai/docs/api-reference#tag/Text-to-Image/operation/textToImage
  132 + return StabilityAiImageOptions.builder().withModel(draw.getModel())
  133 + .withHeight(draw.getHeight()).withWidth(draw.getWidth())
  134 + .withSeed(Long.valueOf(draw.getOptions().get("seed")))
  135 + .withCfgScale(Float.valueOf(draw.getOptions().get("scale")))
  136 + .withSteps(Integer.valueOf(draw.getOptions().get("steps")))
  137 + .withSampler(String.valueOf(draw.getOptions().get("sampler")))
  138 + .withStylePreset(String.valueOf(draw.getOptions().get("stylePreset")))
  139 + .withClipGuidancePreset(String.valueOf(draw.getOptions().get("clipGuidancePreset")))
  140 + .build();
  141 + } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) {
  142 + return TongYiImagesOptions.builder()
  143 + .withModel(draw.getModel()).withN(1)
  144 + .withHeight(draw.getHeight()).withWidth(draw.getWidth())
  145 + .build();
  146 + } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) {
  147 + return QianFanImageOptions.builder()
  148 + .withModel(draw.getModel()).withN(1)
  149 + .withHeight(draw.getHeight()).withWidth(draw.getWidth())
  150 + .build();
  151 + }
  152 + throw new IllegalArgumentException("不支持的 AI 平台:" + draw.getPlatform());
  153 + }
  154 +
  155 + @Override
  156 + public void deleteImageMy(Long id, Long userId) {
  157 + // 1. 校验是否存在
  158 + AiImageDO image = validateImageExists(id);
  159 + if (ObjUtil.notEqual(image.getUserId(), userId)) {
  160 + throw exception(IMAGE_NOT_EXISTS);
  161 + }
  162 + // 2. 删除记录
  163 + imageMapper.deleteById(id);
  164 + }
  165 +
  166 + @Override
  167 + public PageResult<AiImageDO> getImagePage(AiImagePageReqVO pageReqVO) {
  168 + return imageMapper.selectPage(pageReqVO);
  169 + }
  170 +
  171 + @Override
  172 + public void updateImage(AiImageUpdateReqVO updateReqVO) {
  173 + // 1. 校验存在
  174 + validateImageExists(updateReqVO.getId());
  175 + // 2. 更新发布状态
  176 + imageMapper.updateById(BeanUtils.toBean(updateReqVO, AiImageDO.class));
  177 + }
  178 +
  179 + @Override
  180 + public void deleteImage(Long id) {
  181 + // 1. 校验存在
  182 + validateImageExists(id);
  183 + // 2. 删除
  184 + imageMapper.deleteById(id);
  185 + }
  186 +
  187 + private AiImageDO validateImageExists(Long id) {
  188 + AiImageDO image = imageMapper.selectById(id);
  189 + if (image == null) {
  190 + throw exception(IMAGE_NOT_EXISTS);
  191 + }
  192 + return image;
  193 + }
  194 +
  195 + // ================ midjourney 专属 ================
  196 +
  197 + @Override
  198 + @Transactional(rollbackFor = Exception.class)
  199 + public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO reqVO) {
  200 + MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi();
  201 + // 1. 保存数据库
  202 + AiImageDO image = BeanUtils.toBean(reqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)
  203 + .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus())
  204 + .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform());
  205 + imageMapper.insert(image);
  206 +
  207 + // 2. 调用 Midjourney Proxy 提交任务
  208 + List<String> base64Array = StrUtil.isBlank(reqVO.getReferImageUrl()) ? null :
  209 + Collections.singletonList("data:image/jpeg;base64,".concat(Base64.encode(HttpUtil.downloadBytes(reqVO.getReferImageUrl()))));
  210 + MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest(
  211 + base64Array, reqVO.getPrompt(),null,
  212 + MidjourneyApi.ImagineRequest.buildState(reqVO.getWidth(),
  213 + reqVO.getHeight(), reqVO.getVersion(), reqVO.getModel()));
  214 + MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest);
  215 +
  216 + // 3. 情况一【失败】:抛出业务异常
  217 + if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) {
  218 + String description = imagineResponse.description().contains("quota_not_enough") ?
  219 + "账户余额不足" : imagineResponse.description();
  220 + throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description);
  221 + }
  222 +
  223 + // 4. 情况二【成功】:更新 taskId 和参数
  224 + imageMapper.updateById(new AiImageDO().setId(image.getId())
  225 + .setTaskId(imagineResponse.result()).setOptions(BeanUtil.beanToMap(reqVO)));
  226 + return image.getId();
  227 + }
  228 +
  229 + @Override
  230 + public Integer midjourneySync() {
  231 + MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi();
  232 + // 1.1 获取 Midjourney 平台,状态在 “进行中” 的 image
  233 + List<AiImageDO> imageList = imageMapper.selectListByStatusAndPlatform(
  234 + AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform());
  235 + if (CollUtil.isEmpty(imageList)) {
  236 + return 0;
  237 + }
  238 + // 1.2 调用 Midjourney Proxy 获取任务进展
  239 + List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(convertSet(imageList, AiImageDO::getTaskId));
  240 + Map<String, MidjourneyApi.Notify> taskMap = convertMap(taskList, MidjourneyApi.Notify::id);
  241 +
  242 + // 2. 逐个处理,更新进展
  243 + int count = 0;
  244 + for (AiImageDO image : imageList) {
  245 + MidjourneyApi.Notify notify = taskMap.get(image.getTaskId());
  246 + if (notify == null) {
  247 + log.error("[midjourneySync][image({}) 查询不到进展]", image);
  248 + continue;
  249 + }
  250 + count++;
  251 + updateMidjourneyStatus(image, notify);
  252 + }
  253 + return count;
  254 + }
  255 +
  256 + @Override
  257 + public void midjourneyNotify(MidjourneyApi.Notify notify) {
  258 + // 1. 校验 image 存在
  259 + AiImageDO image = imageMapper.selectByTaskId(notify.id());
  260 + if (image == null) {
  261 + log.warn("[midjourneyNotify][回调任务({}) 不存在]", notify.id());
  262 + return;
  263 + }
  264 + // 2. 更新状态
  265 + updateMidjourneyStatus(image, notify);
  266 + }
  267 +
  268 + private void updateMidjourneyStatus(AiImageDO image, MidjourneyApi.Notify notify) {
  269 + // 1. 转换状态
  270 + Integer status = null;
  271 + LocalDateTime finishTime = null;
  272 + if (StrUtil.isNotBlank(notify.status())) {
  273 + MidjourneyApi.TaskStatusEnum taskStatusEnum = MidjourneyApi.TaskStatusEnum.valueOf(notify.status());
  274 + if (MidjourneyApi.TaskStatusEnum.SUCCESS == taskStatusEnum) {
  275 + status = AiImageStatusEnum.SUCCESS.getStatus();
  276 + finishTime = LocalDateTime.now();
  277 + } else if (MidjourneyApi.TaskStatusEnum.FAILURE == taskStatusEnum) {
  278 + status = AiImageStatusEnum.FAIL.getStatus();
  279 + finishTime = LocalDateTime.now();
  280 + }
  281 + }
  282 +
  283 + // 2. 上传图片
  284 + String picUrl = null;
  285 + if (StrUtil.isNotBlank(notify.imageUrl())) {
  286 + try {
  287 + picUrl = fileApi.createFile(HttpUtil.downloadBytes(notify.imageUrl()));
  288 + } catch (Exception e) {
  289 + picUrl = notify.imageUrl();
  290 + log.warn("[updateMidjourneyStatus][图片({}) 地址({}) 上传失败]", image.getId(), notify.imageUrl(), e);
  291 + }
  292 + }
  293 +
  294 + // 3. 更新 image 状态
  295 + imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(status)
  296 + .setPicUrl(picUrl).setButtons(notify.buttons()).setErrorMessage(notify.failReason())
  297 + .setFinishTime(finishTime));
  298 + }
  299 +
  300 + @Override
  301 + public Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO) {
  302 + MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi();
  303 + // 1.1 检查 image
  304 + AiImageDO image = validateImageExists(reqVO.getId());
  305 + if (ObjUtil.notEqual(userId, image.getUserId())) {
  306 + throw exception(IMAGE_NOT_EXISTS);
  307 + }
  308 + // 1.2 检查 customId
  309 + MidjourneyApi.Button button = CollUtil.findOne(image.getButtons(),
  310 + buttonX -> buttonX.customId().equals(reqVO.getCustomId()));
  311 + if (button == null) {
  312 + throw exception(IMAGE_CUSTOM_ID_NOT_EXISTS);
  313 + }
  314 +
  315 + // 2. 调用 Midjourney Proxy 提交任务
  316 + MidjourneyApi.SubmitResponse actionResponse = midjourneyApi.action(
  317 + new MidjourneyApi.ActionRequest(button.customId(), image.getTaskId(), null));
  318 + if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(actionResponse.code())) {
  319 + String description = actionResponse.description().contains("quota_not_enough") ?
  320 + "账户余额不足" : actionResponse.description();
  321 + throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description);
  322 + }
  323 +
  324 + // 3. 新增 image 记录
  325 + AiImageDO newImage = new AiImageDO().setUserId(image.getUserId()).setPublicStatus(false).setPrompt(image.getPrompt())
  326 + .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus())
  327 + .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform())
  328 + .setModel(image.getModel()).setWidth(image.getWidth()).setHeight(image.getHeight())
  329 + .setOptions(image.getOptions()).setTaskId(actionResponse.result());
  330 + imageMapper.insert(newImage);
  331 + return newImage.getId();
  332 + }
  333 +
  334 + /**
  335 + * 获得自身的代理对象,解决 AOP 生效问题
  336 + *
  337 + * @return 自己
  338 + */
  339 + private AiImageServiceImpl getSelf() {
  340 + return SpringUtil.getBean(getClass());
  341 + }
  342 +
  343 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.mindmap;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;
  5 +import reactor.core.publisher.Flux;
  6 +
  7 +/**
  8 + * AI 思维导图 Service 接口
  9 + *
  10 + * @author xiaoxin
  11 + */
  12 +public interface AiMindMapService {
  13 +
  14 + /**
  15 + * 生成思维导图内容
  16 + *
  17 + * @param generateReqVO 请求参数
  18 + * @param userId 用户编号
  19 + * @return 生成结果
  20 + */
  21 + Flux<CommonResult<String>> generateMindMap(AiMindMapGenerateReqVO generateReqVO, Long userId);
  22 +
  23 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.mindmap;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
  5 +import cn.hutool.core.util.StrUtil;
  6 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  7 +import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
  8 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  9 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  10 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
  11 +import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;
  12 +import cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO;
  13 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  14 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  15 +import cn.iocoder.yudao.module.ai.dal.mysql.mindmap.AiMindMapMapper;
  16 +import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;
  17 +import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
  18 +import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
  19 +import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
  20 +import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
  21 +import jakarta.annotation.Resource;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +import org.springframework.ai.chat.messages.Message;
  24 +import org.springframework.ai.chat.messages.SystemMessage;
  25 +import org.springframework.ai.chat.messages.UserMessage;
  26 +import org.springframework.ai.chat.model.ChatModel;
  27 +import org.springframework.ai.chat.model.ChatResponse;
  28 +import org.springframework.ai.chat.prompt.ChatOptions;
  29 +import org.springframework.ai.chat.prompt.Prompt;
  30 +import org.springframework.stereotype.Service;
  31 +import reactor.core.publisher.Flux;
  32 +
  33 +import java.util.ArrayList;
  34 +import java.util.List;
  35 +
  36 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
  37 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  38 +
  39 +/**
  40 + * AI 思维导图 Service 实现类
  41 + *
  42 + * @author xiaoxin
  43 + */
  44 +@Service
  45 +@Slf4j
  46 +public class AiMindMapServiceImpl implements AiMindMapService {
  47 +
  48 + @Resource
  49 + private AiApiKeyService apiKeyService;
  50 + @Resource
  51 + private AiChatModelService chatModalService;
  52 + @Resource
  53 + private AiChatRoleService chatRoleService;
  54 +
  55 + @Resource
  56 + private AiMindMapMapper mindMapMapper;
  57 +
  58 + @Override
  59 + public Flux<CommonResult<String>> generateMindMap(AiMindMapGenerateReqVO generateReqVO, Long userId) {
  60 + // 1. 获取脑图模型。尝试获取思维导图助手角色,如果没有则使用默认模型
  61 + AiChatRoleDO role = CollUtil.getFirst(
  62 + chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_MIND_MAP_ROLE.getName()));
  63 + // 1.1 获取脑图执行模型
  64 + AiChatModelDO model = getModel(role);
  65 + // 1.2 获取角色设定消息
  66 + String systemMessage = role != null && StrUtil.isNotBlank(role.getSystemMessage())
  67 + ? role.getSystemMessage() : AiChatRoleEnum.AI_MIND_MAP_ROLE.getSystemMessage();
  68 + // 1.3 校验平台
  69 + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
  70 + ChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
  71 +
  72 + // 2. 插入思维导图信息
  73 + AiMindMapDO mindMapDO = BeanUtils.toBean(generateReqVO, AiMindMapDO.class,
  74 + mindMap -> mindMap.setUserId(userId).setModel(model.getModel()).setPlatform(platform.getPlatform()));
  75 + mindMapMapper.insert(mindMapDO);
  76 +
  77 + // 3.1 构建 Prompt,并进行调用
  78 + Prompt prompt = buildPrompt(generateReqVO, model, systemMessage);
  79 + Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
  80 +
  81 + // 3.2 流式返回
  82 + StringBuffer contentBuffer = new StringBuffer();
  83 + return streamResponse.map(chunk -> {
  84 + String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null;
  85 + newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
  86 + contentBuffer.append(newContent);
  87 + // 响应结果
  88 + return success(newContent);
  89 + }).doOnComplete(() -> {
  90 + // 忽略租户,因为 Flux 异步无法透传租户
  91 + TenantUtils.executeIgnore(() ->
  92 + mindMapMapper.updateById(new AiMindMapDO().setId(mindMapDO.getId()).setGeneratedContent(contentBuffer.toString())));
  93 + }).doOnError(throwable -> {
  94 + log.error("[generateWriteContent][generateReqVO({}) 发生异常]", generateReqVO, throwable);
  95 + // 忽略租户,因为 Flux 异步无法透传租户
  96 + TenantUtils.executeIgnore(() ->
  97 + mindMapMapper.updateById(new AiMindMapDO().setId(mindMapDO.getId()).setErrorMessage(throwable.getMessage())));
  98 + }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));
  99 +
  100 + }
  101 +
  102 + private Prompt buildPrompt(AiMindMapGenerateReqVO generateReqVO, AiChatModelDO model, String systemMessage) {
  103 + // 1. 构建 message 列表
  104 + List<Message> chatMessages = buildMessages(generateReqVO, systemMessage);
  105 + // 2. 构建 options 对象
  106 + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
  107 + ChatOptions options = AiUtils.buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens());
  108 + return new Prompt(chatMessages, options);
  109 + }
  110 +
  111 + private static List<Message> buildMessages(AiMindMapGenerateReqVO generateReqVO, String systemMessage) {
  112 + List<Message> chatMessages = new ArrayList<>();
  113 + // 1. 角色设定
  114 + if (StrUtil.isNotBlank(systemMessage)) {
  115 + chatMessages.add(new SystemMessage(systemMessage));
  116 + }
  117 + // 2. 用户输入
  118 + chatMessages.add(new UserMessage(generateReqVO.getPrompt()));
  119 + return chatMessages;
  120 + }
  121 +
  122 + private AiChatModelDO getModel(AiChatRoleDO role) {
  123 + AiChatModelDO model = null;
  124 + if (role != null && role.getModelId() != null) {
  125 + model = chatModalService.getChatModel(role.getModelId());
  126 + }
  127 + if (model != null) {
  128 + model = chatModalService.getRequiredDefaultChatModel();
  129 + }
  130 + Assert.notNull(model, "[AI] 获取不到模型");
  131 + return model;
  132 + }
  133 +
  134 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.model;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  4 +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
  5 +import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
  9 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
  10 +import jakarta.validation.Valid;
  11 +import org.springframework.ai.chat.model.ChatModel;
  12 +import org.springframework.ai.image.ImageModel;
  13 +
  14 +import java.util.List;
  15 +
  16 +/**
  17 + * AI API 密钥 Service 接口
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +public interface AiApiKeyService {
  22 +
  23 + /**
  24 + * 创建 API 密钥
  25 + *
  26 + * @param createReqVO 创建信息
  27 + * @return 编号
  28 + */
  29 + Long createApiKey(@Valid AiApiKeySaveReqVO createReqVO);
  30 +
  31 + /**
  32 + * 更新 API 密钥
  33 + *
  34 + * @param updateReqVO 更新信息
  35 + */
  36 + void updateApiKey(@Valid AiApiKeySaveReqVO updateReqVO);
  37 +
  38 + /**
  39 + * 删除 API 密钥
  40 + *
  41 + * @param id 编号
  42 + */
  43 + void deleteApiKey(Long id);
  44 +
  45 + /**
  46 + * 获得 API 密钥
  47 + *
  48 + * @param id 编号
  49 + * @return API 密钥
  50 + */
  51 + AiApiKeyDO getApiKey(Long id);
  52 +
  53 + /**
  54 + * 校验 API 密钥
  55 + *
  56 + * @param id 比那好
  57 + * @return API 密钥
  58 + */
  59 + AiApiKeyDO validateApiKey(Long id);
  60 +
  61 + /**
  62 + * 获得 API 密钥分页
  63 + *
  64 + * @param pageReqVO 分页查询
  65 + * @return API 密钥分页
  66 + */
  67 + PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO);
  68 +
  69 + /**
  70 + * 获得 API 密钥列表
  71 + *
  72 + * @return API 密钥列表
  73 + */
  74 + List<AiApiKeyDO> getApiKeyList();
  75 +
  76 + // ========== 与 spring-ai 集成 ==========
  77 +
  78 + /**
  79 + * 获得 ChatModel 对象
  80 + *
  81 + * @param id 编号
  82 + * @return ChatModel 对象
  83 + */
  84 + ChatModel getChatModel(Long id);
  85 +
  86 + /**
  87 + * 获得 ImageModel 对象
  88 + *
  89 + * TODO 可优化点:目前默认获取 platform 对应的第一个开启的配置用于绘画;后续可以支持配置选择
  90 + *
  91 + * @param platform 平台
  92 + * @return ImageModel 对象
  93 + */
  94 + ImageModel getImageModel(AiPlatformEnum platform);
  95 +
  96 + /**
  97 + * 获得 MidjourneyApi 对象
  98 + *
  99 + * TODO 可优化点:目前默认获取 Midjourney 对应的第一个开启的配置用于绘画;后续可以支持配置选择
  100 + *
  101 + * @return MidjourneyApi 对象
  102 + */
  103 + MidjourneyApi getMidjourneyApi();
  104 +
  105 + /**
  106 + * 获得 SunoApi 对象
  107 + *
  108 + * TODO 可优化点:目前默认获取 Suno 对应的第一个开启的配置用于音乐;后续可以支持配置选择
  109 + *
  110 + * @return SunoApi 对象
  111 + */
  112 + SunoApi getSunoApi();
  113 +
  114 +}
0 115 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.model;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  4 +import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
  5 +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
  6 +import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
  7 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  8 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  9 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  10 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
  11 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
  12 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
  13 +import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper;
  14 +import jakarta.annotation.Resource;
  15 +import org.springframework.ai.chat.model.ChatModel;
  16 +import org.springframework.ai.image.ImageModel;
  17 +import org.springframework.stereotype.Service;
  18 +import org.springframework.validation.annotation.Validated;
  19 +
  20 +import java.util.List;
  21 +
  22 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  23 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
  24 +
  25 +/**
  26 + * AI API 密钥 Service 实现类
  27 + *
  28 + * @author 芋道源码
  29 + */
  30 +@Service
  31 +@Validated
  32 +public class AiApiKeyServiceImpl implements AiApiKeyService {
  33 +
  34 + @Resource
  35 + private AiApiKeyMapper apiKeyMapper;
  36 +
  37 + @Resource
  38 + private AiModelFactory modelFactory;
  39 +
  40 + @Override
  41 + public Long createApiKey(AiApiKeySaveReqVO createReqVO) {
  42 + // 插入
  43 + AiApiKeyDO apiKey = BeanUtils.toBean(createReqVO, AiApiKeyDO.class);
  44 + apiKeyMapper.insert(apiKey);
  45 + // 返回
  46 + return apiKey.getId();
  47 + }
  48 +
  49 + @Override
  50 + public void updateApiKey(AiApiKeySaveReqVO updateReqVO) {
  51 + // 校验存在
  52 + validateApiKeyExists(updateReqVO.getId());
  53 + // 更新
  54 + AiApiKeyDO updateObj = BeanUtils.toBean(updateReqVO, AiApiKeyDO.class);
  55 + apiKeyMapper.updateById(updateObj);
  56 + }
  57 +
  58 + @Override
  59 + public void deleteApiKey(Long id) {
  60 + // 校验存在
  61 + validateApiKeyExists(id);
  62 + // 删除
  63 + apiKeyMapper.deleteById(id);
  64 + }
  65 +
  66 + private AiApiKeyDO validateApiKeyExists(Long id) {
  67 + AiApiKeyDO apiKey = apiKeyMapper.selectById(id);
  68 + if (apiKey == null) {
  69 + throw exception(API_KEY_NOT_EXISTS);
  70 + }
  71 + return apiKey;
  72 + }
  73 +
  74 + @Override
  75 + public AiApiKeyDO getApiKey(Long id) {
  76 + return apiKeyMapper.selectById(id);
  77 + }
  78 +
  79 + @Override
  80 + public AiApiKeyDO validateApiKey(Long id) {
  81 + AiApiKeyDO apiKey = validateApiKeyExists(id);
  82 + if (CommonStatusEnum.isDisable(apiKey.getStatus())) {
  83 + throw exception(API_KEY_DISABLE);
  84 + }
  85 + return apiKey;
  86 + }
  87 +
  88 + @Override
  89 + public PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO) {
  90 + return apiKeyMapper.selectPage(pageReqVO);
  91 + }
  92 +
  93 + @Override
  94 + public List<AiApiKeyDO> getApiKeyList() {
  95 + return apiKeyMapper.selectList();
  96 + }
  97 +
  98 + // ========== 与 spring-ai 集成 ==========
  99 +
  100 + @Override
  101 + public ChatModel getChatModel(Long id) {
  102 + AiApiKeyDO apiKey = validateApiKey(id);
  103 + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
  104 + return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl());
  105 + }
  106 +
  107 + @Override
  108 + public ImageModel getImageModel(AiPlatformEnum platform) {
  109 + AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
  110 + if (apiKey == null) {
  111 + throw exception(API_KEY_IMAGE_NODE_FOUND, platform.getName());
  112 + }
  113 + return modelFactory.getOrCreateImageModel(platform, apiKey.getApiKey(), apiKey.getUrl());
  114 + }
  115 +
  116 + @Override
  117 + public MidjourneyApi getMidjourneyApi() {
  118 + AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(
  119 + AiPlatformEnum.MIDJOURNEY.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
  120 + if (apiKey == null) {
  121 + throw exception(API_KEY_MIDJOURNEY_NOT_FOUND);
  122 + }
  123 + return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl());
  124 + }
  125 +
  126 + @Override
  127 + public SunoApi getSunoApi() {
  128 + AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(
  129 + AiPlatformEnum.SUNO.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
  130 + if (apiKey == null) {
  131 + throw exception(API_KEY_SUNO_NOT_FOUND);
  132 + }
  133 + return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl());
  134 + }
  135 +}
0 136 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.model;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO;
  6 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +import java.util.Collection;
  10 +import java.util.List;
  11 +
  12 +import java.util.Set;
  13 +
  14 +/**
  15 + * AI 聊天模型 Service 接口
  16 + *
  17 + * @author fansili
  18 + * @since 2024/4/24 19:42
  19 + */
  20 +public interface AiChatModelService {
  21 +
  22 + /**
  23 + * 创建聊天模型
  24 + *
  25 + * @param createReqVO 创建信息
  26 + * @return 编号
  27 + */
  28 + Long createChatModel(@Valid AiChatModelSaveReqVO createReqVO);
  29 +
  30 + /**
  31 + * 更新聊天模型
  32 + *
  33 + * @param updateReqVO 更新信息
  34 + */
  35 + void updateChatModel(@Valid AiChatModelSaveReqVO updateReqVO);
  36 +
  37 + /**
  38 + * 删除聊天模型
  39 + *
  40 + * @param id 编号
  41 + */
  42 + void deleteChatModel(Long id);
  43 +
  44 + /**
  45 + * 获得聊天模型
  46 + *
  47 + * @param id 编号
  48 + * @return 聊天模型
  49 + */
  50 + AiChatModelDO getChatModel(Long id);
  51 +
  52 + /**
  53 + * 获得默认的聊天模型
  54 + *
  55 + * 如果获取不到,则抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 业务异常
  56 + *
  57 + * @return 聊天模型
  58 + */
  59 + AiChatModelDO getRequiredDefaultChatModel();
  60 +
  61 + /**
  62 + * 获得聊天模型分页
  63 + *
  64 + * @param pageReqVO 分页查询
  65 + * @return 聊天模型分页
  66 + */
  67 + PageResult<AiChatModelDO> getChatModelPage(AiChatModelPageReqVO pageReqVO);
  68 +
  69 + /**
  70 + * 校验聊天模型
  71 + *
  72 + * @param id 编号
  73 + * @return 聊天模型
  74 + */
  75 + AiChatModelDO validateChatModel(Long id);
  76 +
  77 + /**
  78 + * 获得聊天模型列表
  79 + *
  80 + * @param status 状态
  81 + * @return 聊天模型列表
  82 + */
  83 + List<AiChatModelDO> getChatModelListByStatus(Integer status);
  84 +
  85 + /**
  86 + * 获得聊天模型列表
  87 + *
  88 + * @param ids 编号数组
  89 + * @return 模型列表
  90 + */
  91 + List<AiChatModelDO> getChatModelList(Collection<Long> ids);
  92 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.model;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  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.chatModel.AiChatModelPageReqVO;
  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.dal.mysql.model.AiChatModelMapper;
  11 +import jakarta.annotation.Resource;
  12 +import org.springframework.stereotype.Service;
  13 +import org.springframework.validation.annotation.Validated;
  14 +
  15 +import java.util.Collection;
  16 +import java.util.List;
  17 +
  18 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  19 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
  20 +
  21 +/**
  22 + * AI 聊天模型 Service 实现类
  23 + *
  24 + * @author fansili
  25 + */
  26 +@Service
  27 +@Validated
  28 +public class AiChatModelServiceImpl implements AiChatModelService {
  29 +
  30 + @Resource
  31 + private AiApiKeyService apiKeyService;
  32 +
  33 + @Resource
  34 + private AiChatModelMapper chatModelMapper;
  35 +
  36 + @Override
  37 + public Long createChatModel(AiChatModelSaveReqVO createReqVO) {
  38 + // 1. 校验
  39 + AiPlatformEnum.validatePlatform(createReqVO.getPlatform());
  40 + apiKeyService.validateApiKey(createReqVO.getKeyId());
  41 +
  42 + // 2. 插入
  43 + AiChatModelDO chatModel = BeanUtils.toBean(createReqVO, AiChatModelDO.class);
  44 + chatModelMapper.insert(chatModel);
  45 + return chatModel.getId();
  46 + }
  47 +
  48 + @Override
  49 + public void updateChatModel(AiChatModelSaveReqVO updateReqVO) {
  50 + // 1. 校验
  51 + validateChatModelExists(updateReqVO.getId());
  52 + AiPlatformEnum.validatePlatform(updateReqVO.getPlatform());
  53 + apiKeyService.validateApiKey(updateReqVO.getKeyId());
  54 +
  55 + // 2. 更新
  56 + AiChatModelDO updateObj = BeanUtils.toBean(updateReqVO, AiChatModelDO.class);
  57 + chatModelMapper.updateById(updateObj);
  58 + }
  59 +
  60 + @Override
  61 + public void deleteChatModel(Long id) {
  62 + // 校验存在
  63 + validateChatModelExists(id);
  64 + // 删除
  65 + chatModelMapper.deleteById(id);
  66 + }
  67 +
  68 + private AiChatModelDO validateChatModelExists(Long id) {
  69 + AiChatModelDO model = chatModelMapper.selectById(id);
  70 + if (chatModelMapper.selectById(id) == null) {
  71 + throw exception(CHAT_MODEL_NOT_EXISTS);
  72 + }
  73 + return model;
  74 + }
  75 +
  76 + @Override
  77 + public AiChatModelDO getChatModel(Long id) {
  78 + return chatModelMapper.selectById(id);
  79 + }
  80 +
  81 + @Override
  82 + public AiChatModelDO getRequiredDefaultChatModel() {
  83 + AiChatModelDO model = chatModelMapper.selectFirstByStatus(CommonStatusEnum.ENABLE.getStatus());
  84 + if (model == null) {
  85 + throw exception(CHAT_MODEL_DEFAULT_NOT_EXISTS);
  86 + }
  87 + return model;
  88 + }
  89 +
  90 + @Override
  91 + public PageResult<AiChatModelDO> getChatModelPage(AiChatModelPageReqVO pageReqVO) {
  92 + return chatModelMapper.selectPage(pageReqVO);
  93 + }
  94 +
  95 + @Override
  96 + public AiChatModelDO validateChatModel(Long id) {
  97 + AiChatModelDO model = validateChatModelExists(id);
  98 + if (CommonStatusEnum.isDisable(model.getStatus())) {
  99 + throw exception(CHAT_MODEL_DISABLE);
  100 + }
  101 + return model;
  102 + }
  103 +
  104 + @Override
  105 + public List<AiChatModelDO> getChatModelListByStatus(Integer status) {
  106 + return chatModelMapper.selectList(status);
  107 + }
  108 +
  109 + @Override
  110 + public List<AiChatModelDO> getChatModelList(Collection<Long> ids) {
  111 + return chatModelMapper.selectBatchIds(ids);
  112 + }
  113 +
  114 +}
0 115 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.model;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  8 +import jakarta.validation.Valid;
  9 +
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +import java.util.Map;
  13 +
  14 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  15 +
  16 +/**
  17 + * AI 聊天角色 Service 接口
  18 + *
  19 + * @author fansili
  20 + */
  21 +public interface AiChatRoleService {
  22 +
  23 + /**
  24 + * 创建聊天角色
  25 + *
  26 + * @param createReqVO 创建信息
  27 + * @return 编号
  28 + */
  29 + Long createChatRole(@Valid AiChatRoleSaveReqVO createReqVO);
  30 +
  31 + /**
  32 + * 创建【我的】聊天角色
  33 + *
  34 + * @param createReqVO 创建信息
  35 + * @param userId 用户编号
  36 + * @return 编号
  37 + */
  38 + Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId);
  39 +
  40 + /**
  41 + * 更新聊天角色
  42 + *
  43 + * @param updateReqVO 更新信息
  44 + */
  45 + void updateChatRole(@Valid AiChatRoleSaveReqVO updateReqVO);
  46 +
  47 + /**
  48 + * 创建【我的】聊天角色
  49 + *
  50 + * @param updateReqVO 更新信息
  51 + * @param userId 用户编号
  52 + */
  53 + void updateChatRoleMy(AiChatRoleSaveMyReqVO updateReqVO, Long userId);
  54 +
  55 + /**
  56 + * 删除聊天角色
  57 + *
  58 + * @param id 编号
  59 + */
  60 + void deleteChatRole(Long id);
  61 +
  62 + /**
  63 + * 删除【我的】聊天角色
  64 + *
  65 + * @param id 编号
  66 + * @param userId 用户编号
  67 + */
  68 + void deleteChatRoleMy(Long id, Long userId);
  69 +
  70 + /**
  71 + * 获得聊天角色
  72 + *
  73 + * @param id 编号
  74 + * @return AI 聊天角色
  75 + */
  76 + AiChatRoleDO getChatRole(Long id);
  77 +
  78 + /**
  79 + * 获得聊天角色列表
  80 + *
  81 + * @param ids 编号数组
  82 + * @return 聊天角色列表
  83 + */
  84 + List<AiChatRoleDO> getChatRoleList(Collection<Long> ids);
  85 +
  86 + default Map<Long, AiChatRoleDO> getChatRoleMap(Collection<Long> ids) {
  87 + return convertMap(getChatRoleList(ids), AiChatRoleDO::getId);
  88 + }
  89 +
  90 + /**
  91 + * 校验聊天角色是否合法
  92 + *
  93 + * @param id 角色编号
  94 + */
  95 + AiChatRoleDO validateChatRole(Long id);
  96 +
  97 + /**
  98 + * 获得聊天角色分页
  99 + *
  100 + * @param pageReqVO 分页查询
  101 + * @return 聊天角色分页
  102 + */
  103 + PageResult<AiChatRoleDO> getChatRolePage(AiChatRolePageReqVO pageReqVO);
  104 +
  105 + /**
  106 + * 获得【我的】聊天角色分页
  107 + *
  108 + * @param pageReqVO 分页查询
  109 + * @param userId 用户编号
  110 + * @return 聊天角色分页
  111 + */
  112 + PageResult<AiChatRoleDO> getChatRoleMyPage(AiChatRolePageReqVO pageReqVO, Long userId);
  113 +
  114 + /**
  115 + * 获得聊天角色的分类列表
  116 + *
  117 + * @return 分类列表
  118 + */
  119 + List<String> getChatRoleCategoryList();
  120 +
  121 + /**
  122 + * 根据名字获得聊天角色
  123 + *
  124 + * @param name 名字
  125 + * @return 聊天角色列表
  126 + */
  127 + List<AiChatRoleDO> getChatRoleListByName(String name);
  128 +
  129 +}
0 130 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.model;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjectUtil;
  5 +import cn.hutool.core.util.StrUtil;
  6 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;
  10 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO;
  11 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;
  12 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  13 +import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatRoleMapper;
  14 +import jakarta.annotation.Resource;
  15 +import lombok.extern.slf4j.Slf4j;
  16 +import org.springframework.stereotype.Service;
  17 +
  18 +import java.util.Collection;
  19 +import java.util.Collections;
  20 +import java.util.List;
  21 +
  22 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  23 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  24 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
  25 +
  26 +/**
  27 + * AI 聊天角色 Service 实现类
  28 + *
  29 + * @author fansili
  30 + */
  31 +@Service
  32 +@Slf4j
  33 +public class AiChatRoleServiceImpl implements AiChatRoleService {
  34 +
  35 + @Resource
  36 + private AiChatRoleMapper chatRoleMapper;
  37 +
  38 + @Override
  39 + public Long createChatRole(AiChatRoleSaveReqVO createReqVO) {
  40 + AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class);
  41 + chatRoleMapper.insert(chatRole);
  42 + return chatRole.getId();
  43 + }
  44 +
  45 + @Override
  46 + public Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId) {
  47 + AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class).setUserId(userId)
  48 + .setStatus(CommonStatusEnum.ENABLE.getStatus()).setPublicStatus(false);
  49 + chatRoleMapper.insert(chatRole);
  50 + return chatRole.getId();
  51 + }
  52 +
  53 + @Override
  54 + public void updateChatRole(AiChatRoleSaveReqVO updateReqVO) {
  55 + // 校验存在
  56 + validateChatRoleExists(updateReqVO.getId());
  57 + // 更新
  58 + AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class);
  59 + chatRoleMapper.updateById(updateObj);
  60 + }
  61 +
  62 + @Override
  63 + public void updateChatRoleMy(AiChatRoleSaveMyReqVO updateReqVO, Long userId) {
  64 + // 校验存在
  65 + AiChatRoleDO chatRole = validateChatRoleExists(updateReqVO.getId());
  66 + if (ObjectUtil.notEqual(chatRole.getUserId(), userId)) {
  67 + throw exception(CHAT_ROLE_NOT_EXISTS);
  68 + }
  69 +
  70 + // 更新
  71 + AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class);
  72 + chatRoleMapper.updateById(updateObj);
  73 + }
  74 +
  75 + @Override
  76 + public void deleteChatRole(Long id) {
  77 + // 校验存在
  78 + validateChatRoleExists(id);
  79 + // 删除
  80 + chatRoleMapper.deleteById(id);
  81 + }
  82 +
  83 + @Override
  84 + public void deleteChatRoleMy(Long id, Long userId) {
  85 + // 校验存在
  86 + AiChatRoleDO chatRole = validateChatRoleExists(id);
  87 + if (ObjectUtil.notEqual(chatRole.getUserId(), userId)) {
  88 + throw exception(CHAT_ROLE_NOT_EXISTS);
  89 + }
  90 + // 删除
  91 + chatRoleMapper.deleteById(id);
  92 + }
  93 +
  94 + private AiChatRoleDO validateChatRoleExists(Long id) {
  95 + AiChatRoleDO chatRole = chatRoleMapper.selectById(id);
  96 + if (chatRole == null) {
  97 + throw exception(CHAT_ROLE_NOT_EXISTS);
  98 + }
  99 + return chatRole;
  100 + }
  101 +
  102 + @Override
  103 + public AiChatRoleDO getChatRole(Long id) {
  104 + return chatRoleMapper.selectById(id);
  105 + }
  106 +
  107 + @Override
  108 + public List<AiChatRoleDO> getChatRoleList(Collection<Long> ids) {
  109 + if (CollUtil.isEmpty(ids)) {
  110 + return Collections.emptyList();
  111 + }
  112 + return chatRoleMapper.selectBatchIds(ids);
  113 + }
  114 +
  115 + @Override
  116 + public AiChatRoleDO validateChatRole(Long id) {
  117 + AiChatRoleDO chatRole = validateChatRoleExists(id);
  118 + if (CommonStatusEnum.isDisable(chatRole.getStatus())) {
  119 + throw exception(CHAT_ROLE_DISABLE, chatRole.getName());
  120 + }
  121 + return chatRole;
  122 + }
  123 +
  124 + @Override
  125 + public PageResult<AiChatRoleDO> getChatRolePage(AiChatRolePageReqVO pageReqVO) {
  126 + return chatRoleMapper.selectPage(pageReqVO);
  127 + }
  128 +
  129 + @Override
  130 + public PageResult<AiChatRoleDO> getChatRoleMyPage(AiChatRolePageReqVO pageReqVO, Long userId) {
  131 + return chatRoleMapper.selectPageByMy(pageReqVO, userId);
  132 + }
  133 +
  134 + @Override
  135 + public List<String> getChatRoleCategoryList() {
  136 + List<AiChatRoleDO> list = chatRoleMapper.selectListGroupByCategory(CommonStatusEnum.ENABLE.getStatus());
  137 + return convertList(list, AiChatRoleDO::getCategory, role -> role != null && StrUtil.isNotBlank(role.getCategory()));
  138 + }
  139 +
  140 + @Override
  141 + public List<AiChatRoleDO> getChatRoleListByName(String name) {
  142 + return chatRoleMapper.selectListByName(name);
  143 + }
  144 +
  145 +}
  146 +
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.music;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.*;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
  6 +import jakarta.validation.Valid;
  7 +
  8 +import java.util.List;
  9 +
  10 +/**
  11 + * AI 音乐 Service 接口
  12 + *
  13 + * @author xiaoxin
  14 + */
  15 +public interface AiMusicService {
  16 +
  17 + /**
  18 + * 音乐生成
  19 + *
  20 + * @param userId 用户编号
  21 + * @param reqVO 请求参数
  22 + * @return 生成的音乐ID
  23 + */
  24 + List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO);
  25 +
  26 + /**
  27 + * 同步音乐任务
  28 + *
  29 + * @return 同步数量
  30 + */
  31 + Integer syncMusic();
  32 +
  33 + /**
  34 + * 更新音乐发布状态
  35 + *
  36 + * @param updateReqVO 更新信息
  37 + */
  38 + void updateMusic(@Valid AiMusicUpdateReqVO updateReqVO);
  39 +
  40 + /**
  41 + * 更新我的音乐
  42 + *
  43 + * @param updateReqVO 更新信息
  44 + */
  45 + void updateMyMusic(@Valid AiMusicUpdateMyReqVO updateReqVO, Long userId);
  46 +
  47 + /**
  48 + * 删除AI 音乐
  49 + *
  50 + * @param id 编号
  51 + */
  52 + void deleteMusic(Long id);
  53 +
  54 + /**
  55 + * 删除【我的】音乐记录
  56 + *
  57 + * @param id 音乐编号
  58 + * @param userId 用户编号
  59 + */
  60 + void deleteMusicMy(Long id, Long userId);
  61 +
  62 + /**
  63 + * 获得AI 音乐
  64 + *
  65 + * @param id 音乐编号
  66 + * @return 音乐内容
  67 + */
  68 + AiMusicDO getMusic(Long id);
  69 +
  70 + /**
  71 + * 获得音乐分页
  72 + *
  73 + * @param pageReqVO 分页查询
  74 + * @return 音乐分页
  75 + */
  76 + PageResult<AiMusicDO> getMusicPage(AiMusicPageReqVO pageReqVO);
  77 +
  78 + /**
  79 + * 获得【我的】音乐分页
  80 + *
  81 + * @param pageReqVO 分页查询
  82 + * @param userId 用户编号
  83 + * @return 音乐分页
  84 + */
  85 + PageResult<AiMusicDO> getMusicMyPage(AiMusicPageReqVO pageReqVO, Long userId);
  86 +
  87 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.music;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.text.StrPool;
  5 +import cn.hutool.core.util.ObjUtil;
  6 +import cn.hutool.core.util.ObjectUtil;
  7 +import cn.hutool.core.util.StrUtil;
  8 +import cn.hutool.http.HttpUtil;
  9 +import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
  10 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  11 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
  12 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdateMyReqVO;
  13 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdateReqVO;
  14 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
  15 +import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
  16 +import cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper;
  17 +import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;
  18 +import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;
  19 +import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
  20 +import cn.iocoder.yudao.module.infra.api.file.FileApi;
  21 +import jakarta.annotation.Resource;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +import org.springframework.stereotype.Service;
  24 +import org.springframework.transaction.annotation.Transactional;
  25 +
  26 +import java.util.*;
  27 +
  28 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  29 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  30 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  31 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.IMAGE_NOT_EXISTS;
  32 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MUSIC_NOT_EXISTS;
  33 +
  34 +/**
  35 + * AI 音乐 Service 实现类
  36 + *
  37 + * @author xiaoxin
  38 + */
  39 +@Service
  40 +@Slf4j
  41 +public class AiMusicServiceImpl implements AiMusicService {
  42 +
  43 + @Resource
  44 + private AiApiKeyService apiKeyService;
  45 +
  46 + @Resource
  47 + private AiMusicMapper musicMapper;
  48 +
  49 + @Resource
  50 + private FileApi fileApi;
  51 +
  52 + @Override
  53 + @Transactional(rollbackFor = Exception.class)
  54 + public List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO) {
  55 + // 1. 调用 Suno 生成音乐
  56 + SunoApi sunoApi = apiKeyService.getSunoApi();
  57 + List<SunoApi.MusicData> musicDataList;
  58 + if (Objects.equals(AiMusicGenerateModeEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) {
  59 + // 1.1 描述模式
  60 + SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
  61 + reqVO.getPrompt(), reqVO.getModel(), reqVO.getMakeInstrumental());
  62 + musicDataList = sunoApi.generate(generateRequest);
  63 + } else if (Objects.equals(AiMusicGenerateModeEnum.LYRIC.getMode(), reqVO.getGenerateMode())) {
  64 + // 1.2 歌词模式
  65 + SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
  66 + reqVO.getPrompt(), reqVO.getModel(), CollUtil.join(reqVO.getTags(), StrPool.COMMA), reqVO.getTitle());
  67 + musicDataList = sunoApi.customGenerate(generateRequest);
  68 + } else {
  69 + throw new IllegalArgumentException(StrUtil.format("未知生成模式({})", reqVO));
  70 + }
  71 +
  72 + // 2. 插入数据库
  73 + if (CollUtil.isEmpty(musicDataList)) {
  74 + return Collections.emptyList();
  75 + }
  76 + List<AiMusicDO> musicList = buildMusicDOList(musicDataList);
  77 + musicList.forEach(music -> music.setUserId(userId).setPlatform(reqVO.getPlatform()).setGenerateMode(reqVO.getGenerateMode()));
  78 + musicMapper.insertBatch(musicList);
  79 + return convertList(musicList, AiMusicDO::getId);
  80 + }
  81 +
  82 + @Override
  83 + public Integer syncMusic() {
  84 + List<AiMusicDO> streamingTask = musicMapper.selectListByStatus(AiMusicStatusEnum.IN_PROGRESS.getStatus());
  85 + if (CollUtil.isEmpty(streamingTask)) {
  86 + return 0;
  87 + }
  88 + log.info("[syncMusic][Suno 开始同步, 共 ({}) 个任务]", streamingTask.size());
  89 +
  90 + // GET 请求,为避免参数过长,分批次处理
  91 + SunoApi sunoApi = apiKeyService.getSunoApi();
  92 + CollUtil.split(streamingTask, 36).forEach(chunkList -> {
  93 + Map<String, Long> taskIdMap = convertMap(chunkList, AiMusicDO::getTaskId, AiMusicDO::getId);
  94 + List<SunoApi.MusicData> musicTaskList = sunoApi.getMusicList(new ArrayList<>(taskIdMap.keySet()));
  95 + if (CollUtil.isEmpty(musicTaskList)) {
  96 + log.warn("Suno 任务同步失败, 任务ID: [{}]", taskIdMap.keySet());
  97 + return;
  98 + }
  99 + // 更新进度
  100 + List<AiMusicDO> updateMusicList = buildMusicDOList(musicTaskList);
  101 + updateMusicList.forEach(music -> music.setId(taskIdMap.get(music.getTaskId())));
  102 + musicMapper.updateBatch(updateMusicList);
  103 + });
  104 + return streamingTask.size();
  105 + }
  106 +
  107 + @Override
  108 + public void updateMusic(AiMusicUpdateReqVO updateReqVO) {
  109 + // 校验存在
  110 + validateMusicExists(updateReqVO.getId());
  111 + // 更新
  112 + musicMapper.updateById(new AiMusicDO().setId(updateReqVO.getId()).setPublicStatus(updateReqVO.getPublicStatus()));
  113 + }
  114 +
  115 + @Override
  116 + public void updateMyMusic(AiMusicUpdateMyReqVO updateReqVO, Long userId) {
  117 + // 校验音乐是否存在
  118 + AiMusicDO musicDO = validateMusicExists(updateReqVO.getId());
  119 + if (ObjUtil.notEqual(musicDO.getUserId(), userId)) {
  120 + throw exception(MUSIC_NOT_EXISTS);
  121 + }
  122 + // 更新
  123 + musicMapper.updateById(new AiMusicDO().setId(updateReqVO.getId()).setTitle(updateReqVO.getTitle()));
  124 + }
  125 +
  126 + @Override
  127 + public void deleteMusic(Long id) {
  128 + // 校验存在
  129 + validateMusicExists(id);
  130 + // 删除
  131 + musicMapper.deleteById(id);
  132 + }
  133 +
  134 + @Override
  135 + public void deleteMusicMy(Long id, Long userId) {
  136 + // 1. 校验是否存在
  137 + AiMusicDO music = validateMusicExists(id);
  138 + if (ObjUtil.notEqual(music.getUserId(), userId)) {
  139 + throw exception(IMAGE_NOT_EXISTS);
  140 + }
  141 + // 2. 删除记录
  142 + musicMapper.deleteById(id);
  143 + }
  144 +
  145 + @Override
  146 + public AiMusicDO getMusic(Long id) {
  147 + return musicMapper.selectById(id);
  148 + }
  149 +
  150 + @Override
  151 + public PageResult<AiMusicDO> getMusicPage(AiMusicPageReqVO pageReqVO) {
  152 + return musicMapper.selectPage(pageReqVO);
  153 + }
  154 +
  155 + @Override
  156 + public PageResult<AiMusicDO> getMusicMyPage(AiMusicPageReqVO pageReqVO, Long userId) {
  157 + return musicMapper.selectPageByMy(pageReqVO, userId);
  158 + }
  159 +
  160 + /**
  161 + * 构建 AiMusicDO 集合
  162 + *
  163 + * @param musicList suno 音乐任务列表
  164 + * @return AiMusicDO 集合
  165 + */
  166 + private List<AiMusicDO> buildMusicDOList(List<SunoApi.MusicData> musicList) {
  167 + return convertList(musicList, musicData -> {
  168 + Integer status = Objects.equals("complete", musicData.status()) ? AiMusicStatusEnum.SUCCESS.getStatus()
  169 + : Objects.equals("error", musicData.status()) ? AiMusicStatusEnum.FAIL.getStatus()
  170 + : AiMusicStatusEnum.IN_PROGRESS.getStatus();
  171 + return new AiMusicDO()
  172 + .setTaskId(musicData.id()).setModel(musicData.modelName())
  173 + .setDescription(musicData.gptDescriptionPrompt())
  174 + .setAudioUrl(downloadFile(status, musicData.audioUrl()))
  175 + .setVideoUrl(downloadFile(status, musicData.videoUrl()))
  176 + .setImageUrl(downloadFile(status, musicData.imageUrl()))
  177 + .setTitle(musicData.title()).setDuration(musicData.duration())
  178 + .setLyric(musicData.lyric()).setTags(StrUtil.split(musicData.tags(), StrPool.COMMA))
  179 + .setErrorMessage(musicData.errorMessage())
  180 + .setStatus(status);
  181 + });
  182 + }
  183 +
  184 + /**
  185 + * 音乐生成好后,将音频文件上传到文件服务器
  186 + *
  187 + * @param status 音乐状态
  188 + * @param url 音频文件地址
  189 + * @return 内部文件地址
  190 + */
  191 + private String downloadFile(Integer status, String url) {
  192 + if (StrUtil.isBlank(url) || ObjectUtil.notEqual(status, AiMusicStatusEnum.SUCCESS.getStatus())) {
  193 + return url;
  194 + }
  195 + try {
  196 + byte[] bytes = HttpUtil.downloadBytes(url);
  197 + return fileApi.createFile(bytes);
  198 + } catch (Exception e) {
  199 + log.error("[downloadFile][url({}) 下载失败]", url, e);
  200 + return url;
  201 + }
  202 + }
  203 +
  204 + /**
  205 + * 校验音乐是否存在
  206 + *
  207 + * @param id 音乐编号
  208 + * @return 音乐信息
  209 + */
  210 + private AiMusicDO validateMusicExists(Long id) {
  211 + AiMusicDO music = musicMapper.selectById(id);
  212 + if (music == null) {
  213 + throw exception(MUSIC_NOT_EXISTS);
  214 + }
  215 + return music;
  216 + }
  217 +
  218 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.write;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
  8 +import reactor.core.publisher.Flux;
  9 +
  10 +/**
  11 + * AI 写作 Service 接口
  12 + *
  13 + * @author xiaoxin
  14 + */
  15 +public interface AiWriteService {
  16 +
  17 + /**
  18 + * 生成写作内容
  19 + *
  20 + * @param generateReqVO 作文生成请求参数
  21 + * @param userId 用户编号
  22 + * @return 生成结果
  23 + */
  24 + Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId);
  25 +
  26 + /**
  27 + * 删除写作
  28 + *
  29 + * @param id 编号
  30 + */
  31 + void deleteWrite(Long id);
  32 +
  33 + /**
  34 + * 获得写作分页
  35 + *
  36 + * @param pageReqVO 分页查询
  37 + * @return AI 写作分页
  38 + */
  39 + PageResult<AiWriteDO> getWritePage(AiWritePageReqVO pageReqVO);
  40 +
  41 +}
... ...